# Android 基础知识

# 架构

# Android的大体架构图

分为四个层次:linux内核;libraies和Android runntime;framework;Application

img

更多参考

# Android的四大组件是哪些,它们的作用?

  1. Activity:Activity是Android程序与用户交互的窗口,对用户来说是可见的
  2. service:后台服务于Activity,是一个服务,不可见
  3. Content Provider:对外提提供数据
  4. BroadCast Receiver:接受一种或者多种Intent作触发事件,接受相关消息,做一些简单处理

更多参考

# Android 中进程的优先级

  1. 前台进程
  2. 可见进程
  3. 服务进程
  4. 后台进程
  5. 空进程

更多参考

# Android中asset和res目录的区别

  1. res目录下的资源文件会在R文件中生成对应的id,asset不会
  2. res目录下的文件在生成apk时,除raw(即res/raw)目录下文件不进行编译外,都会被编译成二进制文件;asset目录下的文件不会进行编译
  3. asset目录允许有子目录

# Android中App 是如何沙箱化的,为何要这么做

  1. 沙箱化可以提升安全性和效率
  2. Android的底层内核为Linux,因此继承了Linux良好的安全性,并对其进行了优化。在Linux中,一个用户对应一个uid,而在Android中,(通常)一个APP对应一个uid,拥有独立的资源和空间,与其他APP互不干扰。如有两个APP A和B,A并不能访问B的资源,A的崩溃也不会对B造成影响,从而保证了安全性和效率

更多参考

# Activity

# Activity 生命周期

img

# Activity在屏幕旋转时的生命周期

  1. 没有任何设置时,会调用整个生命周期方法,并且会调用onSaveInstance和onRestoreInstanceState方法
  2. 在Manifest中为Activity设置android:configChanges="orientation"时,只调用onConfigChanges方法
  3. android:configChanges="orientation"属性有可能不起作用,依然会调用整个生命周期方法,这是因为不同版本处理方式可能不同,有时候还需要加上android:configChanges="orientation|keyboardHidden|screenSize"等。

# onSaveInstanceState 什么时候调用

  1. 非用户主动明确结束(按back键,自定义click方法调用finish)时都会调用onSaveInstanceState:
    1. 屏幕旋转
    2. 按HOME键
    3. 内存不足
    4. 从一个activity启动另一个activity
  2. 这个方法的调用时机是在onStop前,但是它和onPause没有既定的时序关系

# 自定义View控件的状态被保存需要满足两个条件

  1. View有唯一的ID
  2. View的初始化时要调用setSaveEnabled(true)

# configChanges属性

对Activity配置了android:configChanges="xxx"属性之后,Activity就不会在对应变化发生时重新创建,而是调用Activity的onConfigurationChanged方法。常用的有local:设备的本地位置发生了变化,一般指切换了系统语言;keyboardHidden:键盘的可访问性发生了变化,比如用户调出了键盘;orientation:屏幕方向发生了变化,比如旋转了手机屏幕。

# A activity启动B activity和B activity返回A activity的生命周期执行过程

  1. A启动B:A.onPause()→B.onCreate()→B.onStart()→B.onResume()→A.onStop
  2. B返回A:B.onPause()→A.onRestart()/A.onCreate()→A.onStart()→A.onResume()→B.onStop()

# Activity执行finish后的生命周期

  1. 在onCreate中执行:onCreate -> onDestroy
  2. 在onStart中执行:onCreate -> onStart -> onStop -> onDestroy
  3. 在onResume中执行:onCreate -> onStart -> onResume -> onpause -> onStop -> onDestroy

更多参考

# 如果用了一些解耦的策略,怎么管理生命周期的?

  1. 可以用Google的 LifeCycle框架 0. 引入LifeCycle框架
    1. 将控件实现LifecycleObserver接口
    2. 在Activity中中注册控件:getLifeCycle().addObderver(View);
    3. 在控件中使用: @OnLifecycleEvent(Lifecycle.Event.ON_START)

# Activity的启动流程

img

更多参考

# Android中Activity的启动模式

  1. standard:每一次启动,都会生成一个新的实例,放入栈顶中
  2. singleTop:通过singelTop启动Activity时,如果发现有需要启动的实例正在栈顶,责直接重用,否则生成新的实例
  3. singleTask:通过singleTask启动Activity时,如果发现有需要启动的实例正在栈中,责直接移除它上边的实例,并重用该实例,否则生成新的实例
  4. singleInstance:通过singleTask启动Activity时,会启用一个新的栈结构,并将新生成的实例放入栈中。

更多参考

# TaskAffinity 属性

  1. 任务相关性,标识一个Activity所需的任务栈的名字。默认情况下,所有的Activity所需的任务栈的名字是应用的包名,当然也可以单独指定TaskAffinity属性。
  2. TaskAffinity属性主要和singleTask启动模式和allowTaskRepeating属性配对使用,在其他情况下使用没有意义
  3. 当TaskAffinity和singleTask启动模式配对使用的时候,它是具有该模式的Activity的目前任务栈的名字,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中
  4. 当TaskAffinity和allowTaskReparenting结合的时候,当一个应用A启动了应用B的某个Activity C后,如果Activity C的allowTaskReparenting属性设置为true的话,那么当应用B被启动后,系统会发现Activity C所需的任务栈存在了,就将Activity C从A的任务栈中转移到B的任务栈中。

# Activity启动模式的TaskAffinity和allowTaskReparenting

  1. TaskAffinity配合singleTask使用,指定任务栈:如果没有TaskAffinity指定的任务栈,则开启新栈
  2. allowTaskReparenting配合standard和singleTop使用,标明该Activity的任务栈可以重新设置

更多参考

# 当前应用有两个Activity A和B,B的 android:launchMode 设置了singleTask模式,A是默认的standard,那么A startActivity启动B,B会新启一个Task吗?如果不会,那么startActivity的Intent加上FLAG_ACTIVITY_NEW_TASK这个参数会不会呢?

设置了singleTask启动模式的Activity,它在启动的时会先在系统中查看属性值affinity等于它的属性值taskAffinity ( taskAffinity默认为包名 ) 的任务栈是否存在。如果存在这样的任务栈,它就会在这个任务栈中启动,否则就会在新任务栈中启动。

当Intent对象包含FLAG_ACTIVITY_NEW_TASK标记时,系统在查找时仍然按Activity的taskAffinity属性进行匹配,如果找到一个任务栈的taskAffinity与之相同,就将目标Activity压入此任务栈中,如果找不到则创建一个新的任务栈。

设置了singleTask启动模式的Activity在已有的任务栈中已经存在相应的Activity实例,再启动它时会把这个Activity实例上面的Activity全部结束掉。也就是说singleTask自带clear top的效果。

# IntentFilter的匹配规则

IntentFilter中的过滤信息有action、category、data,为了匹配过滤列表,需要同时匹配过滤列表中的action、category、data信息,否则匹配失败。

# 验证是否有当前Activity

  1. PackageManager的resolveActivity方法或者Intent的resolveActivity方法:如果找不到就会返回null
  2. PackageManager的queryIntentActivities方法:它返回所有成功匹配的Activity信息

# 如何获取当前屏幕Activity的对象?

通过在Application中注册Activity生命周期的监听函数Application.registerActivityLifecycleCallbacks()

更多参考

# onNewIntent调用时机

一个Activity已经启动,当再次启动它时,如果他的启动模式(如SingleTask,SingleTop)标明不需要重新启动,会调用onNewIntent

更多参考

# 除了用Intent 去启动一个Activity,还有其他方法吗

使用adb shell am 命令 :如adb shell am start com.example.fuchenxuan/.MainActivity 或者 adb shell am broadcast -a magcomm.action.TOUCH_LETTER

# Android中子线程更新UI的方式

  1. activity.runOnUiThread(runnable)
  2. 通过主线程中的Handler进行更新
  3. 通过View的post()或者postDelayed方法进行更新

# Activity之间的通信方式

  1. Intent
  2. BroadCast或者LocalBroadCast
  3. 数据存储的方式
  4. 静态变量

更多参考

# Service

# Service的启动方式

  1. start
  2. bind

更多参考

# Service生命周期

img

# Service 和Activity 的通信方式

  1. 如上Activity和Activity的通信方式
  2. bind方式启动时可以通过ServiceConnection通信:在SerVice的onBind方法中返回一个binder,该binder可以是AIDL方法产生的,也可以是Messenger方法产生的

# Service和Thread的区别

  1. 这是没用任何关系的两个概念,servie是系统的组件,Thread是CPU运行的最小单元
  2. Service不可见,我们可以把Service当成是不可见的Activity,用于在后台执行一些服务;
  3. Service可以运行在任意线程上,如果我们生成它时没有做特殊说明,那么它运行在主线程上
  4. 很多时候我们需要在Activity中开启一个Service,再在Service中开启一个线程,这么做的原因是Service只会初始化一次,我们可以随时找到Service中生成的thread,使用场景举例:
    1. 如我们需要在多个Activity中对同一个thread进行控制时;
    2. 如果你的 Thread 需要不停地隔一段时间就要连接服务器做某种同步的话,该 Thread 需要在 Activity 没有start的时候也在运行。这个时候当你 start 一个 Activity 就没有办法在该 Activity 里面控制之前创建的 Thread。因此你便需要创建并启动一个 Service ,在 Service 里面创建、运行并控制该 Thread,这样便解决了该问题

# 为什么有时需要在Service中创建子线程而不是Activity中

这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。

更多参考

# IntentService

  1. IntentService 是继承自 Service,内部通过HandlerThread启动一个新线程处理耗时操作么,可以看做是Service和HandlerThread的结合体,在完成了使命之后会自动停止,适合需要在工作线程处理UI无关任务的场景
  2. 如果启动 IntentService 多次,那么每一个耗时操作会以工作队列的方式在 IntentService 的 onHandleIntent 回调方法中执行,依次去执行,使用串行的方式,执行完自动结束。

# IntentService生命周期是怎样的

  1. 在所有任务执行完毕后,自动结束生命

# BroadCast

# BroadCast的注册方式与区别

  1. 在manifest中静态注册:广播是常驻的,App关闭后仍能接收广播,唤醒App
  2. 动态的注册和注销:动态注册的广播生命周期和他的宿主相同,或者调用注销方法注销广播

# Android中发送BroadCast的方式

  1. 无序广播:通过mContext.sendBroadcast(Intent)或mContext.sendBroadcast(Intent, String)发送的是无序广播(后者加了权限);
  2. 通过mContext.sendOrderedBroadcast(Intent, String, BroadCastReceiver, Handler, int, String, Bundle)发送的是有序广播(不再推荐使用)。
  3. 在无序广播中,所有的Receiver会接收到相同广播;而在有序广播中,我们可以为Receiver设置优先级,优先级高的先接收广播,并有权对广播进行处理和决定要不要继续向下传送

更多参考

# BroadCastReceiver处理耗时操作

  1. BroadcastReceiver的生命周期只有一个回调方法onReceive(Context context, Intent intent);无法进行耗时操作,即使启动线程处理,也是出于非活动状态,有可能被系统杀掉。
  2. 如果需要进行耗时操作,可以启动一个service处理。

# 广播发送和接收的原理了解吗

  1. 继承BroadcastReceiver,重写onReceive()方法。
  2. 通过Binder机制向ActivityManagerService注册广播。
  3. 通过Binder机制向ActivityMangerService发送广播。
  4. ActivityManagerService查找符合相应条件的广播(IntentFilter/Permission)的BroadcastReceiver,将广播发送到BroadcastReceiver所在的消息队列中。
  5. BroadcastReceiver所在消息队列拿到此广播后,回调它的onReceive()方法。

# 广播传输的数据是否有限制,是多少,为什么要限制?

  1. 广播是通过Intent携带需要传递的数据的
  2. Intent是通过Binder机制实现的
  3. Binder对数据大小有限制,不同room不一样,一般为1M

# Localbroadcast

本地广播,只有本进程中的receivers能接收到此广播

实现原理(监听者模式):

  1. LocalBroadcastManager是一个单例
  2. 在LocalBroadcastManager实例中维护一个Action和ReceiverRecord的Map.(ReceiverRecord是reveiver和intentfilter的组合)
  3. 当调用LocalBroadcastManager的sendBroadcast方法时,会从2中的map找到合适的receiver,让后加到待执行的队列mPendingBroadcasts,并通过Handler发送一个空消息(此Handler运行在主线程中,是创建manager时创建的)
  4. handler 的handle方法收到消息,从mPendingBroadcasts取出receiver并调用onreceive方法 其他:删除方法是通过一个辅助的hashmap实现的,hashmap存储了receiver和receiverRecord

更多参考

# ContentProvider

# 请介绍下ContentProvider是如何实现数据共享的

  1. 准确的说,ContentProvider是一个APP间共享数据的接口。一个程序可以通过实现一个Content provider的抽象接口将自己的数据完全暴露出去,数据可以是SqLite中的,也可以是文件或者其他类型。
  2. 使用方式:
    1. 在A APP中实现建ContentProvider,并在Manifest中生命它的Uri和权限
    2. 在B APP中注册权限,并通过ContentResolver和Uri进行增删改查
  3. 扩展:ContentProvider底层是通过Binder机制来实现跨进程间通信,通过匿名共享内存方式进行数据的传输 一个应用进程有16个Binder线程去和远程服务进行交互,而每个线程可占用的缓存空间是128KB,超过会报异常。

更多参考

# 每个ContentProvider的操作是在哪个线程中运行的呢(其实我们关心的是UI线程和工作线程)?比如我们在UI线程调用getContentResolver().query查询数据,而当数据量很大时(或者需要进行较长时间的计算)会不会阻塞UI线程呢?

  1. ContentProvider和调用者在同一个进程,ContentProvider的方法(query/insert/update/delete等)和调用者在同一线程中
  2. ContentProvider和调用者在不同的进程,ContentProvider的方法会运行在它自身所在进程的一个Binder线程中

# ContentProvider、ContentResolver与ContentObserver之间的关系是什么?

  1. ContentProvider:管理数据,提供数据的增删改查操作,数据源可以是数据库、文件、XML、网络等,ContentProvider为这些数据的访问提供了统一的接口,可以用来做进程间数据共享。
  2. ContentResolver:ContentResolver可以不同URI操作不同的ContentProvider中的数据,外部进程可以通过ContentResolver与ContentProvider进行交互。
  3. ContentObserver:观察ContentProvider中的数据变化,并将变化通知给外界。

# Fragment

# Fragment生命周期

onAttach -> onCreate -> onCreateView -> onActivityCreate -> onStart -> onResume -> onPause -> onStop -> onDestoryView -> onDestory -> onDetach

img

# 遇到过哪些关于Fragment的问题,如何处理的

举例:getActivity()空指针:这种情况一般发生在在异步任务里调用getActivity(),而Fragment已经onDetach()。

# Fragment 有什么优点, Fragment和View可以相互替换嘛

  1. Fragment为了解决Andriod碎片化而产生的
  2. Fragment和View都有助于界面复用
  3. Fragment的复用粒度更大,包含生命周期和业务逻辑,通常包含好几个View
  4. View通常更关注视图的实现

# Fragment add replace 区别

  1. replace 先删除容器中的内容,再添加
  2. add直接添加,可以配合hide适用

# 参考资料

LearningNotes

40 个 Android 面试题

https://www.nowcoder.com/discuss/3043

http://weixin.niurenqushi.com/article/2017-03-17/4790406.html

https://blog.csdn.net/vfush/article/details/51481127

https://blog.csdn.net/vfush/article/details/51790079

Last Updated: 3/5/2020, 6:29:15 PM