插件化中Activity的加载

前面一系列的文章中我们介绍了Android系统资源加载流程,最后引出插件化中资源加载的方法,完成了『资源动态加载』这一大块的介绍。本系列文章将重点介绍『代码动态加载』,拿最熟悉的Activity来开刀。

基础知识

Activity虽然是一个Java对象,可以使用ClassLoader加载出它的实体,但是由于Activity生命周期管理是由系统框架完成的,为了更好的分析如何加载插件中的Activity以及让它“活”起来,我们有必要了解两方面的知识:

  1. Activity启动流程
  2. Activity生命周期回调流程

Activity启动流程

Activity的启动流程大致分三个阶段:

  1. 应用程序进程内启动Activity操作
  2. AMS进程内部处理
  3. 应用程序进程内创建Activity(可能是一个新的进程)

我们将按照这个顺序来分析Activity的启动。

在应用程序进程内启动Activity

以我们最常见的startActivity作为分析的起点,它最终将调用到startActivityForResult。

1.Activity.startActivityForResult

public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {...Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options);...
}

startActivityForResult方法只是简单调用了Instrumentation类的execStartActivity方法。但是方法参数要说明一下:

  • mMainThread.getApplicationThread。得到的是一个类型为ApplicationThread的Binder本地对象,在后面的操作中会将它传给AMS,以便AMS进程和应用程序进程通信。
  • mToken。它是一个IBinder类型的Binder代理对象,指向AMS中类型为ActivityRecord的Binder本地对象。由于AMS中使用ActivityRecord来描述一个Activity,将mToken传给AMS后,AMS就知道由谁启动了新的Activity。

2.Instrumentation.execStartActivity

public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {...int result = ActivityManagerNative.getDefault().startActivity(whoThread, who.getBasePackageName(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()),token, target != null ? target.mEmbeddedID : null,requestCode, 0, null, options);...
}

ActivityManagerNative的getDefault方法获得的是AMS的一个代理对象,其startActivity方法最终将调用到AMS的startActivityAsUser方法。

AMS进程内部处理

启动Activity的核心逻辑都在AMS进程内进行,整个逻辑流程十分复杂。但在研究插件化框架原理时,我们不用细致的研究每一块逻辑,因为即使可以看明白所有流程,也无法Hook AMS进程完成操作。所以在这里只是对AMS内处理流程进行简单介绍,如果后面有涉及具体某块逻辑时再详细介绍。

先看一下整个过程的时序图:

AMS内启动Activity流程

简要说明每一步作用。

1.ActivityManagerService.startActivityAsUser

这里我们不去分析用户相关逻辑,这里简单调用ActivityStarter的方法。

2.ActivityStarter.startActivityMayWait

得到代表Intent信息的ResolveInfo对象,代表Activity信息的ActivityInfo对象。调用startActivityLock进行下一步操作。

3.ActivityStarter.startActivityLocked

得到代表调用者进程的ProcessRecord对象,代表调用者Activity的ActivityRecord对象。做权限检查的校验,并创建待启动Activity的ActivityRecord对象。调用startActivityUnchecked进一步处理。

4.ActivityStarter.startActivityUnchecked

获取到任务栈相关的信息,然后调用代表任务栈的ActivityStack的startActivityLocked方法。

5.ActivityStack.startActivityLocked

确定了待启动Activity所属的任务栈,并且将其放入任务栈。然后返回到ActivityStarter的startActivityUnchecked方法中,调用ActivityStackSupervisor的resumeFocusedStackTopActivityLocked方法进一步操作。

6.ActivityStackSupervisor.resumeFocusedStackTopActivityLocked

直接将操作委托给ActivityStack执行。

7.ActivityStack.resumeTopActivityUncheckedLocked

调用自身resumeTopActivityInnerLocked方法。

8.ActivityStack.resumeTopActivityInnerLocked

这个方法中执行了两个主要的操作:

  • 调用startPausingLocked方法完成前一个Activity的onPause生命周期调用。
  • 调用ActivityStackSupervisor的startSpecificActivityLocked方法完成启动Activity。

我们在分析Activity生命周期回调时再去分析startPausingLocked方法,现在继续分析启动流程。

9.ActivityStackSupervisor.startSpecificActivityLocked

这个方法中判断待启动Activity所要求的进程是否存在,如果不存在就创建进程。这里我们不分析创建进程流程,继续向下看存在进程情况下的启动流程。

10.ActivityStackSupervisor.realStartActivityLocked

调用应用程序进程内ApplicationThread的scheduleLaunchActivity方法来完成Activity创建和启动。

到这里AMS进程内的操作流程就简单介绍完了,下面来看在在应用程序进程内是如何创建Activity的。

应用程序进程内创建Activity

1.ApplicationThread.scheduleLaunchActivity

public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,ActivityInfo info, ...) {updateProcessState(procState, false);ActivityClientRecord r = new ActivityClientRecord();r.token = token;r.activityInfo = info;...sendMessage(H.LAUNCH_ACTIVITY, r);}

首先创建出一个在应用程序进程内表示Activity的对象。与之对应的是在AMS进程内表示Activity的ActivityRecord对象。然后将一些参数赋值给ActivityClientRecord。这里有两个属性需要注意:

  • token。是一个类型为IBinder的代理对象,指向了AMS中一个ActivityRecord。
  • activityInfo。表示待启动Activity信息。

然后调用sendMessage方法,这个方法执行逻辑在ActivityThread类的属性mH上。mH是一个类型为H的Handler对象,其将操作转到应用程序进程主线程内执行。来看一下类H处理逻辑。

2.H.handleMessage

 public void handleMessage(Message msg) {switch (msg.what) {case LAUNCH_ACTIVITY: {final ActivityClientRecord r = (ActivityClientRecord) msg.obj;r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");} break;}}

调用getPackageInfoNoCheck方法得到PackageInfo对象,然后调用handleLaunchActivity方法启动Activity。getPackageInfoNoCheck方法很重要,暂时先不去管,等到分析插件化处理时再来仔细研究它。继续Activity启动流程,下面是handleLaunchActivity方法。

3.ActivityThread.handleLaunchActivity

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {...Activity a = performLaunchActivity(r, customIntent);...}

主要是调用performLaunchActivity来启动Activity。

4.ActivityThread.performLaunchActivity

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {ActivityInfo aInfo = r.activityInfo;...ComponentName component = r.intent.getComponent();...Activity activity = null;try {java.lang.ClassLoader cl = r.packageInfo.getClassLoader();activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);...} catch (Exception e) {...}try {Application app = r.packageInfo.makeApplication(false, mInstrumentation);...if (activity != null) {Context appContext = createBaseContextForActivity(r, activity);...activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config,r.referrer, r.voiceInteractor, window);...if (r.isPersistable()) {mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);} else {mInstrumentation.callActivityOnCreate(activity, r.state);}r.activity = activity;...}r.paused = true;mActivities.put(r.token, r);} catch (SuperNotCalledException e) {throw e;} return activity;}

这个方法很重要,它做了以下几个操作:

  • 拿到加载Activity的ClassLoader。这个ClassLoader是从ActivityClientRecord的packageInfo属性中拿到的,如果有印象可以记起来这个packageInfo是调用getPackageInfoNoCheck方法创建出来的。
  • 拿到当前进程中的Application对象。
  • 使用ActivityClientRecord中的token等内容创建ContextImpl对象,并且在内部持有待启动Activity的引用。
  • 调用Activity的attach方法在其内部保存一些ActivityClientRecord内的属性。
  • 调用activity的onCreate方法。
  • 将ActivityClientRecord中的activity属性设置为当前activity。
  • 以ActivityClientRecord中的token为key,mActivities中添加待启动的activity。

这样Activity的启动流程大致就介绍完了,下面来介绍Activity的生命周期回调流程。

Activity生命周期回调流程

Activity生命周期回调大概分为两种,一种是在创建了Activity后直接回调的,例如onCreate、onResume。另一种是在进行一系列操作以后回调的,如onPause、onStop等。在这里我们重点分析后者,因为后者通常是在AMS进程中发起回调,我们要关系的是AMS是如何找到对应的Activity并执行回调

Activity中onPause生命周期的回调

这里我们拿onPause方法来举例,因为在刚刚分析的Activity启动流程内就涉及了onPause操作。在AMS进程内部处理第八步中我们谈到了,ActivityStack的resumeTopActivityInnerLock方法中调用了startPausingLocked方法完成对前一个Activity的onPause生命周期回调,现在来看一下这个方法。

1.ActivityStack.startPausingLocked

这个方法在AMS进程中运行,不贴代码了,最终调用了以下代码:

prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,userLeaving, prev.configChangeFlags, dontWait);

很熟悉的流程,prev.app.thread是前一个Activity所在进程的ApplicationThread。

2.Application.schedulePauseActivity

public final void schedulePauseActivity(IBinder token, boolean finished,boolean userLeaving, int configChanges, boolean dontReport) {sendMessage(finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,token,(userLeaving ? USER_LEAVING : 0) | (dontReport ? DONT_REPORT : 0),configChanges,seq);
}

用mH发了消息,看一下消息处理逻辑。

3.mH.handleMessage

public void handleMessage(Message msg) {switch (msg.what) {...case PAUSE_ACTIVITY: {SomeArgs args = (SomeArgs) msg.obj;handlePauseActivity((IBinder) args.arg1, false,(args.argi1 & USER_LEAVING) != 0, args.argi2,(args.argi1 & DONT_REPORT) != 0, args.argi3);} break;}
}

调用handlePauseActivity完成onPause回调。

4.ActivityThread.handlePauseActivity

private void handlePauseActivity(IBinder token, boolean finished,boolean userLeaving, int configChanges, boolean dontReport, int seq) {ActivityClientRecord r = mActivities.get(token);...if (r != null) {...performPauseActivity(token, finished, r.isPreHoneycomb(), "handlePauseActivity");...}}

方法很简单,调用了performPauseActivity。

5.ActivityThread.performPauseActivity

 final Bundle performPauseActivity(ActivityClientRecord r, boolean finished,boolean saveState, String reason) {...if (!r.activity.mFinished && saveState) {callCallActivityOnSaveInstanceState(r);}performPauseActivityIfNeeded(r, reason);...
}

方法先调用Activity的onSaveInstanceState保存一下状态,然后调用onPause方法。

这样Activity启动流程和生命周期回调流程都简单分析完了,接下来将结合两种不同类型的插件化框架分析插件化中Activity的加载和启动。

插件化中Activity的加载相关推荐

  1. Android插件化开发之动态加载三个关键问题详解

    本文摘选自任玉刚著<Android开发艺术探索>,介绍了Android插件化技术的原理和三个关键问题,并给出了作者自己发起的开源插件化框架. 动态加载技术(也叫插件化技术)在技术驱动型的公 ...

  2. 安卓插件化学习 - 类的加载

    安卓插件化学习 - 类的加载 引言 一.类的加载 1. 原理 2. 代码 2.1 宿主apk代码 2.1.1 插件管理器 2.1.2 配置文件 2.1.3 插件初始化 2.1.4 调用插件方法 2.2 ...

  3. Android插件化开发之动态加载本地皮肤包进行换肤

    Android插件化开发之动态加载本地皮肤包进行换肤 前言: 本文主要讲解如何用开源换肤框架 android-skin-loader-lib来实现加载本地皮肤包文件进行换肤,具体可自行参考框架原理进行 ...

  4. 【Android 插件化】基于插件化的恶意软件的加载策略分析 ( 自定义路径加载插件 | 系统路径加载插件 | 用户同意后加载插件 | 隐藏恶意插件 )

    文章目录 一.自定义路径加载插件 二.系统路径加载插件 三.用户同意后加载插件 四.隐藏恶意插件 一.自定义路径加载插件 插件化应用中 , 宿主应用 加载 插件 APK , 需要获取该插件 APK 文 ...

  5. Android插件化开发之动态加载技术系列索引

    动态加载介绍 在Android开发中采用动态加载技术,可以达到不安装新的APK就升级APP功能的目的,可以用来到达快速发版的目的,也可以用来修复一些紧急BUG. 现在使用得比较广泛的动态加载技术的核心 ...

  6. Android插件化开发之动态加载基础之ClassLoader工作机制

    类加载器ClassLoader 早期使用过Eclipse等Java编写的软件的同学可能比较熟悉,Eclipse可以加载许多第三方的插件(或者叫扩展),这就是动态加载.这些插件大多是一些Jar包,而使用 ...

  7. Android插件化开发之动态加载技术简单易懂的介绍方式

    转载地方:https://segmentfault.com/a/1190000004062866 基本信息 Author:kaedea GitHub:android-dynamical-loading ...

  8. Android插件化开发之动态加载的类型

    https://segmentfault.com/a/1190000005113493 基本信息 Author:kaedea GitHub:android-dynamical-loading 现在网络 ...

  9. Android 插件化开发——宿主APP加载APK插件

    本篇博客说一下我们的宿主APP怎样加载别的APK文件. 首先需要说一些知识点,我们的Java文件要想在Android环境运行,需要将.java文件通过转为class文件,然后为了能在DVM上面运行,再 ...

最新文章

  1. VSS自动发布站点功能扩展
  2. 微信小程序 提示Toast
  3. 一定要搜藏的20个非常有用的PHP类库
  4. Xcode8更新约束
  5. 深夜,在这个已不再喧嚣的城市中寻找到一片属于自己的宁静,仰望那片属于自己的星空……...
  6. 解决libc.so.6: version `GLIBC_2.14' not found问题
  7. vue-cil解决开发环境的跨域问题
  8. 面试题--------6、String常用的方法
  9. eova、easyui及普通DOM表单元素操作
  10. 2021年法国经济发展研究报告
  11. 计算机学院手绘,PS电脑手绘效果绘画教程
  12. 十分详细的React入门实例
  13. TrackFormer: Multi-Object Tracking with Transformers
  14. 【线性代数】A为方阵,当存在B使得 AB=E ,证明BA=E
  15. 2019年下半年系统架构设计师上午真题及答案解析
  16. SpringSecurity(一)
  17. 继电器开关性能简要对比
  18. 20180402-F · US Tuition Costs · pheatmap 绘制热图 · R 语言数据可视化 案例 源码
  19. pytorch 模型并行 model parallel
  20. 星云服务器装系统,装win10,装win10系统-总算知道

热门文章

  1. [SDK]Unity接入Sign in with Apple
  2. java实现加密———Base64加解密
  3. linux mdadm删除raid0,mdadm彻底删除software RAID 0
  4. 51单片机(三十)—— 矩阵键盘计算器
  5. JavaScript字符串操作,把短线(-)命名格式改变为驼峰命名
  6. 【2020/07/16修订】概率论与数理统计(电子科技大学) 知识梳理 · 第一版(1到8章 · 度盘)
  7. 软件工程文档编写格式要求
  8. java中三步表达式,Java中三目运算符之类型转换
  9. libusb系列-002-Windows下libusb源码编译
  10. 苹果手机调试(ios)