Android四大组件流程原理图、Handler、AsyncTask、IntentService(二)

本节将了解一下Android四大组件的工作过程、Handler相关、AsyncTask、IntentService相关

结合Android 源码查看更佳

Android冷启动在之前一篇文章分析过了 AOSP Android 8.0 冷启动流程分析(二)

一、Android APP启动过程简介

  1. 要了解Android四大组件得先搞清楚APP是怎么启动的。

    • 假设头条新闻APP,我们在手机屏幕上点击了该Icon,这个APP的首页(引导页就出现在我们面前了)。这个简单操作,背后经历了反反复复的Activity和ActivityManagerService的通信过程

    • Launcher也是一个APP,和普通的APP没有什么不同,手机屏幕上可以动态查看天气,Icon分组,分页显示

    •  

      Android四大组件流程原理图、Handler、AsyncTask、IntentService(二)_第1张图片

       

    • 带着这个流程去看 AOSP Android 8.0 冷启动流程分析(二)或者查看源码可能效果会好一点,内部是各种Binder进程间通信,如果对Binder进程间通信忘了,可以看看 Binder进程通信示例 这篇文章,及时查漏补缺。

    • Actiivty间的跳转和APP启动过程很相似,知识省略了一些启动过程步骤,不需要启动进程。

  2. 涉及到的类有Launcher、Activity、ActivityThread和其内部类ApplicationThread,ActivityManagerService是通过ApplicationThread来通信的,在Android 8中ActivityManagerNative已经标记了过时,直接使用ActivityManagerService继承IActivityManager.Stub来通信。这里不再过多介绍,详细看上篇文章。

二、Activity的工作过程

  1. 这里想提前说一下Context,毕竟每个Activity对应一个Context

    • Android四大组件流程原理图、Handler、AsyncTask、IntentService(二)_第2张图片

    • 从分析冷启动过程中看,上面的ContextImpl非常重要,由于Activity有一层Theme,所以中间有个ContextThemeWrapper,一个应用包含的Context个数是Service的个数 + Activity个数 +1(Application类本身对应一个Context对象)

    • 在我们用Intent启动Activity的时候,通常在Activity内使用startActivity(),如果使用getApplicationContext().startActivity()呢?也是可以的,只不过Application的生命周期是跟着APP走的,它是在ActivityThread中提取出来的,使用getApplicationContext()得防止内存泄漏

  2. 先贴Activity工作流程图,再介绍

    Android四大组件流程原理图、Handler、AsyncTask、IntentService(二)_第3张图片

  3. 上面是一些方法调用,每个类间的交互图,下面是对它的简单介绍

    • 一个ActivityRecord对应一个Activity,保存了一个Activity的所有信息,Activity多次启动的话,ActivityRecord就有多个。TaskRecord是用来保存ActivityRecord的。ActivityStack是用来管理TaskRecord的。ActivityStackSupervisor,用来管理ActivityStack的。这些源码中有体现,一般都是通过ArrayList来添加管理的。

    • 流程图是基于Android 8.0源码,所以没有ActivityManagerNative,源码内已经对其标注了过时。Android 5.0中ActivityManagerService是继承ActivityManagerNative,AMS是其具体实现,大同小异,都是通过AMS进行进程通信

    • Context的启动Activity其实是实现类ContextImpl的启动Activity,最终都会调用方法2,方法2执行完后,会执行checkStartActivityResult()方法,该方法会检查清单文件有没有注册Activity。方法2会与AMS进行通信,一直到方法14,都可以理解为Activity启动的必要检查和准备工作。方法15是将启动Activity的消息发送并交由内部Handler类H处理,H再调用ActivityThread的方法处理。方法18中主要是从ActivityClientRecord中获取待启动的Activity的组件信息,通过Instrumentation的newActivity方法使用类加载器创建Activity对象,通过LoadApk的makeApplication方法尝试创建Application对象,创建好后,会调用Application的onCreate方法,通过ActivityThread的createBaseContextForActivity方法创建ContextImpl,然后activity.attach方法绑定ContextImpl等来完成一些从要数据的初始化,再最后就是调用Instrumentation的callActivityOnCreate方法。至于方法19,也类似。

三、Service的工作过程

  1. Service的启动过程

    • 首先说一下简化流程,假如要启动的Service是在一个新进程中,会经过以下流程

      • APP向AMS发送一个启动Service的消息

      • AMS检查Service进程,不存在则把Service信息(ServiceRecord对象保存)存下来,然后创建新进程

      • 创建的进程启动后,通知AMS,AMS把保存的Service进程发给新进程

      • 新进程启动Service

    • 如果当前要启动的Service不在一个新进程中,那么上述步骤就少了一个启动进程,直接AMS检查,比如Service是否在清单文件中注册了,把Service在AMS注册,要启动的是该APP所在的Service,就启动

    • 方法8和方法12依次在方法7中调用的

      Android四大组件流程原理图、Handler、AsyncTask、IntentService(二)_第4张图片

    • 停止服务,stopService也是类似流程

  2. Service的绑定过程

    • 同一进程绑定Service的简化流程

      • APP向AMS发送一个绑定Service的消息

      • AMS开始检查,Service是否声明了,把Service注册在AMS,AMS发现启动的Service是需要启动的Service,然后通知APP启动Service,再然后通知APP对Service进行绑定

      • APP收到AMS第一个消息,启动Service

      • APP收到AMS第二个消息,绑定Service,并传给AMS一个Binder对象

      • AMS接受到Binder对象,发送给APP,APP收到Binder对象,正常工作

    • 在上述流程中,可能会有疑问,为什么APP传Binder对象给AMS,AMS又传给Binder。因为需要考虑不在同一个进程的场景,代码写两份太冗余了,所以即使同一进程,也这样实现。

    • Android四大组件流程原理图、Handler、AsyncTask、IntentService(二)_第5张图片

    • 方法2内通过LoadApk.getServiceDispatcher得到实现了IServiceConnection接口的类,其实就是ServiceConnected的内部类InnerConnection,该类继承了IServiceConnection.Stub,所以LoadApk的该方法就是得到一个Binder对象。本质上,就是将ServiceConnection转化成Binder对象,用ServiceDispatcher来连接ServiceConnection和InnerConnection,内部有一个ArrayMap来保存这种映射关系

    • 方法2将ServiceConnection封装为InnerConnection,传递给AMS。方法13内会调用Service的onBind方法,然后通过AMS的publishService将此Bind对象传给AMS。

    • 方法16是c.conn.connected(r.name,service),c的类型是ConnectionRecord,c.conn类型就是ServiceDispatcher.InnerConnection,service就是onBind返回的对象,此connect内部会通过之前getServiceDispatcher传的ActivityThread的Handler类H来运行RunConnection,内部通过ServiceDispatcher调用客户端的onServiceConnected方法

四、BroadcastReceiver的工作流程

  1. 广播注册流程

    • Android四大组件流程原理图、Handler、AsyncTask、IntentService(二)_第6张图片

    • 方法2中,先通过LoadedApk.getReceiverDispatcher得到IItentReceiver对象,这个和Service类似。因为BroadcastReceiver作为一个Android组件是不能跨进程传递的,所以需要通过IIntentReceiver来中转一下,它的具体实现是LoadedApk.ReceiverDispatcher.InnerReceiver,ReceiverDispatcher内部保存了BroadcastReceiver和InnerReceiver,方便接受广播时调用Receiver的onReceiv方法

    • 方法3中,主要是保存远程的InnerReceiver对象以及IntentFilter对象

  2. 广播发送和接受流程

    • Android四大组件流程原理图、Handler、AsyncTask、IntentService(二)_第7张图片

    • 方法4内,Android 8.0源码的第18979行,intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES),代码上有注释,说明在8.0下默认广播不会发送给已经停止的应用,其实从Android 3.1开始就不会发送给已经停止的应用了,如果实在想要调起未启动的应用,那么就需要为广播的intent添加FLAG_INCLUDE_STOPPED_PACKAGES即可。

    • 方法4内会根据intent filter查找匹配的广播接收者并经过一系列条件的过滤,满足添加的将添加到BroadcastQueue中,然后由其发送给相应的广播接收者。

    • 方法11是InnerReceiver类的方法,这个Binder对象在之前已经说过了(方法10会把它传过来),这个方法继而调用ReceiverDispatcher的performReceive方法,继而通过传递过来的ActivityThread的H 处理一个Runnable对象,在Runnable内会调用BroadcastReceiver的onReceive方法。

    • 由于动态receiver只能在Activity的onCreate()方法调用时才能注册再接收广播,所以当程序没有运行就不能接受到广播;但是静态注册的则不依赖于程序是否处于运行状态,因为手机每次启动时会重新安装所有的APK。

五、ContentProvider的工作流程

  1. ContentProvider一般而言是单例的,除非使用了android:multiprocess="true"。ContentProvider的启动不是在Context.getContentResolver时(getContentResolver返回的是ContentResolver的实现类ApplicationContentResolver),而是在调用ApplicationContentResolver类的四大方法之一时,例如调用query方法。

  2. Android四大组件流程原理图、Handler、AsyncTask、IntentService(二)_第8张图片

  3. 上面只是query方法中启动ContentProvider流程,启动完后,得到Binder类型对象IContentProvider,具体实现者是ContentProvider类的Transport类,所以最后调用的是该类的query方法

  4. ContentProvider的本质是把数据存储在SQLite数据库中。Client有一个CursorWindow对象,发送请求时,把这个CursorWindow类型对象发送过去,这个对象暂时为空,Server收到请求后,搜集数据,填充到这个CursorWindow对象,Client读取内部的这个CursorWindow对象,获取到数据,这个CursorWindow对象就是匿名共享内存,ASM.

六、APP安装过程简介

  1. PackageManagerService是用来获取APK包信息的。从前面四大组件分析过程中能了解到,AMS总会使用PMS加载包的信息,将其封装在LoadedApk中。

  2. 下载安装APP过程中,会把APK放在data/app目录下,apk文件是个压缩包,在文件头会记录压缩包的大小,所以在文件后面追加其它东西也不会对解压有影响,木马就是利用这个思路。在多渠道打包过程中,在比较老的版本中,例如Android 4.4,文件后面会有几个字节来标记APK的渠道,谷歌发现了该漏洞,然后现在是APK安装时会检查实际大小,校验是否与APK的头部记录大小相等

  3. 在APP安装过程中,会先分配用户ID和用户组ID,用户ID是唯一的,用户组ID是各种权限,每个权限都在一个用户组中,比如网络访问。然后在Launcher内会生成一个Icon,Icon保存着默认启动的Activity的信息。安装最后会把这些信息记录到xml文件中,方便下次使用

  4. 在Android手机每次启动时,会使用PMS把所有APK都安装一遍

    • 读取上次安装时保存的APP安装信息

    • 扫面安装保存在特定目录下的Apk

    • 为App分配用户组ID

    • 把前面的各种安装信息写入到本地文件,方便下次安装使用

  5. APP安装,为什么不把它解压 ?直接从解压文件中读取资源文件不是更快 ?

    • 每次从apk中读取资源,并不是先解压再找图片资源,而是解析Apk中的Resource.arsc文件,这个文件中存储着资源的所有信息,包括资源所在Apk中的地址、大小等,按图索骥,从这个文件中快速找到相应的资源文件是一种很高效的算法。

七、消息机制

  1. Android的消息机制主要是指Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper的支撑。MessageQueue就是咱们平时所称的消息队列,但是它的内部存储结构不是真正的队列,而是采用单链表的数据结构来存储消息列表。Looper是真正的消息处理者,线程默认没有Looper,使用Handler必须为程序创建Looper。UI线程在ActivityThread创建时会初始化Looper,所以主线程中默认可以直接使用Handler。

  2. ViewRootImpl的checkThread会检查是否UI线程操作UI。不允许子线程访问UI原因是UI控件不是线程安全的,加锁操作又会导致UI的操作过于复杂。

  3. ThreadLocal是一个线程内部的数据存储类。它的set方法会在第一次是会创建一个ThreadLocalMap对象赋给thread的threadLocals属性,ThreadLoalMap内部维护了一个Entry类型的table数组,第一次设置设置值时会找到位置,直接放到table数组对应的位置上。如果不是在该线程上第一次调用set方法,那么它会调用ThreadLocalMap的set方法,这个方法具体就是找到之前存放的值,替换。ThreadLocal的get方法就是从ThreadLocalMap中取出值来,总而言之,它所操作的对象都是当前线程的ThreadLocalMap对象的table数组。

  4. 消息队列工作原理

    • 消息队列指的是MessageQueue,MessageQueue主要包含两个操作:插入和读取,分别是enqueueMessage和next方法。enqueueMessage主要是单链表的插入操作,next方法是一个无限循环,如果消息队列中没有消息,那么next方法会一直阻塞在这里,当有新消息到来时,next方法会返回这条消息并将其从单链表中移除

  5. Looper的工作原理

    • Handler的工作需要Looper,Looper的prepareMainLooper方法主要是给主线程ActivityThread创建Looper使用的,一般在没有Looper的子线程里,通常使用Looper.prepare来创建Looper。Looper的退出有两种方式,分别是quit和quitSafely,内部都是调用MessagaeQueue的quit方法,quitSafely只是设定一个退出标记,然后把消息队列中的已有消息处理完毕后才安全退出。当然创建了Looper对象时,不能忘了调用Looper.loop方法,否则消息循环系统不会真正起作用,这个方法是会阻塞获取msg,然后调用msg.target.dispatchMessage方法处理,msg.target就是handler。

  6. Handler的工作原理

    • 它的工作主要就是发送消息和接收消息

    • 无论是调用sendMessage还是post发送消息,最后都是调用sendMessageAtTime,这个过程就是把消息放到消息队列中,调用了MessageQueue的enqueueMessage方法,然后Looper收到消息后就会调用handler的dispatchMessage方法

    • 一个Thread只能有一个Looper,详细查看Looper.prepare方法,内部利用了ThreadLocal类。可以拥有多个Handler

    • MessageQueue中待处理的Message可能来自不同的Handler

    • Message中记录了负责发送消息和处理消息的Handler

八、线程池

  1. AsyncTask

    • 一种轻量级的异步任务类

    • 三个泛型参数

      • Params:表示执行AsyncTask需要传入的参数类型

      • Progress:表示后台任务的执行进度的类型

      • Result:表示后台任务的返回结果的类型

    • 四个核心方法

      • onPreExecute() :在主线程运行,异步任务执行前调用,一般用于做一些准备工作

      • doInBackground(Params...params):在子线程执行,params表示异步任务的输入参数,需要更新UI需调用publishProgress(Progress...)方法,该方法会调用onProgressUpdate方法

      • onProgressUpdate(Progress...values):在主线程中执行,当后台任务的执行进度发生改变会调用此方法

      • onPostExecute(Result result):在主线程中执行,异步任务执行完毕调用,其中result值为后台任务的返回值

    • 需要注意的是AsyncTask对象必须在主线程创建,因为该类中有一个static final 类型的Handler,为了能够将执行环境切换到主线程,这就要求该handler必须在主线程创建,而且静态成员会在加载类时进行初始化,所以AsyncTask对象需要在主线程创建。当然,它的execute()和cancel()方法也必须在主线程中执行。还有就是一个AsyncTask对象只能执行一次,它内部有状态判断,执行完后再执行会报运行时异常。

    • 当调用AsyncTask的execute(Params...params)方法时,它会继续调用executeOnExecutor(sDefaultExecutor,params)方法,这里sDefaultExecutor是一个串行的线程池,一个进程中所有的AsyncTask任务都在这里排队等候执行。这个方法内判断状态是否为初始值PENDING,不是就会抛出异常,是的话就会把内部mStatus状态置为RUNNING,然后调用onPreExecute()方法,再然后就会调用sDefaultExecutor.execute(mFuture)。

    • sDefaultExecutor类型为SerialExecutor,是AsyncTask内部一个继承Executor的类,它的execute方法就是一个排队执行过程。

    • mFuture是是FutureTask,熟悉Java并发相关知识的肯定对该类不陌生,它实现了接口RunnableFuture,RunnableFuture实现了Runnable接口。mFuture会接受一个mWorker对象,执行过程中会调用doInBackground,然后调用postResult方法

    • postResult方法内会发送一个MESSAGE_POST_RESULT类型的Message,然后由内部InternalHandler类处理,然后把mStatus状态置为FINISHED

  2. HandlerThread继承了Thread,它在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环,所以不需要使用HandlerThread时,还是有必要执行一下quit或者quitSafely方法终止线程执行。

  3. IntentService继承自Service,它可以用于执行后台耗时任务,当任务执行完后会自动停止。它内部封装了Handler和HandlerThread。流程大概如下,当IntentService被第一次启动时,它的onCreate被调用,onCreate会创建一个HandlerThread,然后使用它的Looper来构造一个Handler对象mServiceHandler,这样通过mServiceHandler发送的消息都会在HandlerThread中执行,而在ServiceHandler内的handMessage中可以发现,调用完onHandleIntent方法后,立即调用了stopSelf方法,这也就是为什么任务执行完后,自动停止的原因。IntentService在onStartCommand处理每个后台任务Intent时,会调用onStart方法,在onStart方法内,利用了mServiceHandler发送了利用该Intent组成的Message消息。

你可能感兴趣的:(Android进阶)