面试理论类型技术总结

Android应用开发岗 面试汇总-Android基础篇

2022-03-24  本文已影响0人  hahaoop

背景

最近在准备面试,结合之前的工作经验和近期在网上收集的一些面试资料,准备将Android开发岗位的知识点做一个系统的梳理,整理成一个系列:Android应用开发岗 面试汇总。本系列将分为以下几个大模块:
Java基础篇Java进阶篇常见设计模式
Android基础篇Android进阶篇性能优化
网络相关、数据结构与算法
常用开源库、Kotlin、Jetpack

注1:以上文章将陆续更新,直到我找到满意的工作为止,有跳转链接的表示已发表的文章。
注2:该系列属于个人的总结和网上东拼西凑的结果,每个知识点的内容并不一定完整,有不正确的地方欢迎批评指正。
注3:部分摘抄较多的段落或有注明出处。如有侵权,请联系本人进行删除。

Fragment相关

Activity与Fragment之间生命周期比较

Activity于Fragment的生命周期的不同主要表现在onCreateonDestroy两个生命周期方法中:

Fragment 如何实现类似 Activity 栈的压栈和出栈效果?

Fragment 的事物管理器(FragmentManager )内部维持了一个双向链表结构,该结构可以记录我们每次 add 的Fragment 和 replace 的 Fragment,然后当我们点击 back 按钮的时候会自动帮我们实现退栈操作。

Fragment与Activity之间是如何传值的?

Fragment懒加载

为什么要实现懒加载?

在没有添加懒加载之前,只要使用 add+show+hide 的方式控制并显示 Fragment, 那么不管 Fragment 是否嵌套,在初始化后,如果只调用了add+show,同级下的 Fragment 的相关生命周期函数都会被调用。且调用的生命周期函数如下所示:(不管该Fragment是否用户可见,都会调用以下函数,常见在viewpager中)
onAttach -> onCreate -> onCreatedView -> onActivityCreated -> onStart -> onResume

因此,为了避免性能和流量的浪费,需要对当前不可见的Fragment进行懒加载处理,即Fragment真正被用户看到时才开始去加载数据。

add+show+hide 模式下的老方案

场景:通过show+hide方法控制两个fragment的显示,A和B,当从A切换到B时,两个Fragment的可见状态发生了改变,Fragment会回调onHiddenChanged()方法,A中isHidden()方法的值为 true,B中为false。
方案:

因此

abstract class LazyFragment:Fragment(){
    private var isLoaded = false //控制是否执行懒加载
    override fun onResume() {
        super.onResume()
        judgeLazyInit()
    }
    override fun onHiddenChanged(hidden: Boolean) {
        super.onHiddenChanged(hidden)
        judgeLazyInit()
    }
    private fun judgeLazyInit() {
        if (!isLoaded && !isHidden()) {
            lazyInit()
            isLoaded = true
        }
    }
     override fun onDestroyView() {
        super.onDestroyView()
        isLoaded = false
    }
    //懒加载方法
    abstract fun lazyInit()
}

ViewPager+Fragment 模式下的老方案

ViewPager+Fragment 模式下的老方案
使用传统方式处理 ViewPager 中 Fragment 的懒加载,我们需要控制 setUserVisibleHint(boolean isVisibleToUser) 函数,该函数与之前我们介绍的 onHiddenChanged() 作用非常相似,都是通过传入的参数值来判断当前 Fragment 是否对用户可见,只是 onHiddenChanged() 是在 add+show+hide 模式下使用,而 setUserVisibleHint 是在 ViewPager+Fragment 模式下使用。public void setUserVisibleHint(boolean isVisibleToUser) {}
因此:

abstract class LazyFragment : Fragment() {
    //是否执行懒加载
    private var isLoaded = false
    //当前Fragment是否对用户可见
    private var isVisibleToUser = false

    /**
     * 当使用ViewPager+Fragment形式会调用该方法时,setUserVisibleHint会优先Fragment生命周期函数调用,
     * 所以这个时候就,会导致在setUserVisibleHint方法执行时就执行了懒加载,
     * 而不是在onResume方法实际调用的时候执行懒加载。所以需要这个变量
     */
    private var isCallResume = false
    override fun onResume() {
        super.onResume()
        isCallResume = true
        judgeLazyInit()
    }
    override fun onHiddenChanged(hidden: Boolean) {
        super.onHiddenChanged(hidden)
        isVisibleToUser = !hidden
        judgeLazyInit()
    }
    private fun judgeLazyInit() {
        if (!isLoaded && isVisibleToUser && isCallResume) {
            lazyInit()
            Log.d(TAG, "lazyInit:!!!!!!!")
            isLoaded = true
        }
    }
    //在Fragment销毁View的时候,重置状态
    override fun onDestroyView() {
        super.onDestroyView()
        isLoaded = false
        isVisibleToUser = false
        isCallResume = false
    }
    override fun setUserVisibleHint(isVisibleToUser: Boolean) {
        super.setUserVisibleHint(isVisibleToUser)
        this.isVisibleToUser = isVisibleToUser
        judgeLazyInit()
    }
    abstract fun lazyInit()
}

Androidx 下的懒加载

虽然之前的方案就能解决轻松的解决 Fragment 的懒加载,但这套方案有一个最大的弊端,就是不可见的 Fragment 执行了 onResume() 方法。onResume 方法设计的初衷,难道不是当前 Fragment 可以和用户进行交互吗?你他妈既不可见,又不能和用户进行交互,你执行 onResume 方法干嘛?
基于此问题,Google 在 Androidx 在 FragmentTransaction 中增加了 setMaxLifecycle 方法来控制 Fragment 所能调用的最大的生命周期函数。即Fragment被用户可见和可操作时才调用onResume方法:

add+show+hide 模式下的新方案

通过FragmentTransaction 设置setMaxLifecycle的值:setMaxLifecycle(Lifecycle.State.STARTED),可以使Fragment生命周期降级到onStart。

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
homefragment = new HomeFragment();
ft.add(R.id.fl_container, homefragment);
ft.setMaxLifecycle(homefragment, Lifecycle.State.STARTED);
ft.commit();

ViewPager+Fragment 模式下的老方案

该场景下,通过调用FragmentStatePagerAdapter中的setPrimaryItem(FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)
注:该方法最终也是调setMaxLifecycle()。

以上两种方式只需要在onResume中增加是否已经加载过数据的判断,来决定是否需要执行初次加载。
参考链接

Service相关

简介:

Service(服务)是一个可以在后台执行长时间运行操作而没有用户界面的应用组件。Service是运行在主线程中的(跟Activity同一个线程),不能进行耗时操作。我们一般都是在Service中创建一个新的线程来处理一些耗时工作,这样就不会阻塞主线程。

启动Service的两种方式

Service的生命周期

image.png

回调方法说明

常见面试题

1、什么是 IntentService?有何优点?

  • Service 不会专门启动一条单独的进程,Service 与它所在应用位于同一个进程中;
  • Service 也不是专门一条新线程,因此不应该在 Service 中直接处理耗时的任务;

2、怎么保证应用尽可能不被杀死

保证Service不被销毁

//设置服务的优先级为MAX_VALUE
 <service android:name=".MyService"
          android:priority="2147483647" >
 </service>
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Notification notification = new Notification(R.mipmap.ic_launcher, "服务正在运行",System.currentTimeMillis());
    Intent notificationIntent = new Intent(this, MainActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,notificationIntent,0);
    RemoteViews remoteView = new RemoteViews(this.getPackageName(),R.layout.notification);
    remoteView.setImageViewResource(R.id.image, R.mipmap.ic_launcher);
    remoteView.setTextViewText(R.id.text , "Hello,this message is in a custom expanded view");
    notification.contentView = remoteView;
    notification.contentIntent = pendingIntent;
    startForeground(1, notification);
    return Service.START_STICKY;
}
@Override
public void onDestroy() {
    super.onDestroy();
    startService(new Intent(this, MyService.class));
}
    Intent intent = new Intent(MainActivity.this,MyAlarmReciver.class);
    PendingIntent sender = PendingIntent.getBroadcast( MainActivity.this, 0, intent, 0);

    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.add(Calendar.SECOND, 1);
    AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
    //重复闹钟
    /**
     * @param triggerAtMillis t 闹钟的第一次执行时间,以毫秒为单位
     * @param intervalMillis 表示两次闹钟执行的间隔时间,也是以毫秒为单位
     * @param operation 绑定了闹钟的执行动作,比如发送一个广播、给出提示等等
     */
    am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 2 * 1000, sender);

相关面试题

Broadcast Receiver相关

如何注册 BroadcastReceiver

在清单文件中注册广播接收者称为静态注册,在代码中注册称为动态注册。

<receiver android:name=".BroadcastReceiver1" > 
     <intent-filter>
        <action android:name="android.intent.action.CALL" > </action>
      </intent-filter> 
</receiver>

动态注册:在代码中进行如下注册

receiver = new BroadcastReceiver(); 
IntentFilter intentFilter = new IntentFilter(); 
intentFilter.addAction(CALL_ACTION);
context.registerReceiver(receiver, intentFilter);

Android 引入广播机制的用意?

两种注册各有什么特点

静态注册

动态注册

BrocastReceiver 的生命周期和注意事项

动画相关

动画有哪几类,各有什么特点?
三类:补间动画、帧动画、属性动画。补间动画和Drawable动画可统称为视图动画。也可以将这三类动画都归为属性动画。

补间动画(View Animation)

在xml中定义,有缩放、平移、渐变、旋转等,可组合使用。动画集合 AnimationSet用来存放上面四种动画的集合

帧动画

也叫Frame动画,可以划分到视图动画的类别。专门用来一个一个的显示Drawable的resources,就像放幻灯片一样

属性动画

通过改变视图的属性,来达到动画的效果的动画。

过渡动画和MotionLayout

Window和WindowManager

类结构图:


image.png

SurfaceView相关

我们知道View是通过刷新来重绘视图,系统通过发出VSSYNC信号来进行屏幕的重绘,刷新的时间间隔是16ms,如果我们可以在16ms以内将绘制工作完成,则没有任何问题,如果我们绘制过程逻辑很复杂,并且我们的界面更新还非常频繁,这时候就会造成界面的卡顿,影响用户体验,为此Android提供了SurfaceView来解决这一问题。如:绘制手写签名

View和SurfaceView的区别:

作者:冰鉴IT
链接:https://www.jianshu.com/p/b037249e6d31
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

作者:冰鉴IT
链接:https://www.jianshu.com/p/b037249e6d31
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Android中的重要术语解释

其他面试题

invalidate、postInvalidate和requestLayout区别

Intent 可以传递哪些数据类型

Bitmap和Drawable的区别是什么?

它们之间可以互转吗?理论上是不可以的,但是可以通过自身生成出对方的类型来达到互转的效果。

Webview与Js交互?

Webview调用Js
需要在主线程中调用

String call = "javascript:readyToGo()";
webView.loadUrl(call);
String call = "javascript:alertMessage(\"" + "content" + "\")";
  webView.loadUrl(call);
String call = "javascript:alertMessage(\"" + "content" + "\")";
webView.evaluateJavascript(call, new ValueCallback<String>() {
          @Override
          public void onReceiveValue(String s) {
              Log.d("findCar",s);
          }
      });

Js通过WebView调用Java代码
从API19开始,Android提供了@JavascriptInterface对象注解的方式来建立起Javascript对象和Android原生对象的绑定,提供给JavScript调用的函数必须带有@JavascriptInterface。
实际上,就是3个步骤:
1.Java被Js调用的方法上加@JavascriptInterface注解;
2.WebView注册JavaScriptInterface;
3.js调用,如window.android.show("JavaScript called~!");

Js调用Android Toast方法

 @JavascriptInterface
  public boolean show(String s){
      Toast.makeText(getApplication(), s, Toast.LENGTH_SHORT).show();
      return true;
  }
webView.addJavascriptInterface(this, "android");
addJavascriptInterface的作用是把this所代表的类映射为JavaScript中的android对象。
 function toastClick(){
      var str=window.android.show("JavaScript called~!");
      console.log(str);
  }

通过H5打开App的某个页面?

在manifest文件中最开始启动的activity中添加:
<activity android:name=".activitys.MainActivity">
 <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:host="host"
            android:pathPrefix="/pathPrefix"
            android:scheme="scheme" />
    </intent-filter>
</activity>
//注意host,pathPrefix,scheme都是自己自定义的,只要与h5页面调用的一致即可,如下所示

如果要跳转到指定的页面,在MainActivity的onCreate()中添加:

Intent intent = getIntent();
Uri uri = intent.getData();
if (uri != null) {
        String routeId = uri.getQueryParameter("pid");
        Intent intent0 = new Intent(MainActivity.this, ZhidingActivity.class);
        startActivity(intent0);
}
uri.getQueryParameter("pid");获取h5页面传递的参数,如果没有的话可以忽略
注意一点,微信上对于app的唤醒有拦截,在浏览器中才可以起作用

原文链接:https://blog.csdn.net/m0_37678565/article/details/68491444

其他知识点汇总参考

上一篇下一篇

猜你喜欢

热点阅读