Activity启动流程和源码解析

↓双击 Markdown插件,就可以开始编辑啦。

APP进程

ActivityThread的启动

public static void main(String[] args) {
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
    }

在attach方法中主要做了以下重要的事情:

final IActivityManager mgr = ActivityManager.getService();
            try {
                mgr.attachApplication(mAppThread, startSeq);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }

attach方法把应用进程的ApplicationThread通过IActivityManager传递到ActivityManagerService,而IActivityManager是ActivityManagerService系统服务在APP进程的一个代理,这个是一个跨进程的调用,最终会调用到ActivityManagerService的attachApplication方法:

public final void attachApplication(IApplicationThread thread, long startSeq) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
            Binder.restoreCallingIdentity(origId);
        }
    }

attachApplication方法里面又调用到:
在这里插入图片描述
IApplicationThread是APP进程ApplicationThread在系统进程的一个代理对象,bindApplication是一个系统进程到APP进程的跨进层调用,负责把一些初始化的数据回传到APP进程,例如系统服务的各种Binder代理对象,bindApplication最后会调用到ApplicationThread的bindApplication方法,这里面主要做了三件事:

  • 一个是把系统服务的Binder对象缓存到ServiceManager中
  • 第二个是创建LoadedApk对象,LoadedApk是应用在内存上的一个体现,包含了应用信息,资源路径,内部文件路径,apk路径,receiver信息,Service信息
  • 第三个是通过Handler类对象H来实现Application的创建,onCreate的回调
ActivityThread的启动流程

在这里插入图片描述
App进程与AMS进程的通信过程如图所示
在这里插入图片描述

系统进程

SystemServer启动

/**
     * The main entry point from zygote.
     */
    public static void main(String[] args) {
        new SystemServer().run();
    }

在system_process进程启动的时候就会初始化一些系统服务,例如ActivityManagerService:
在这里插入图片描述

APP进程启动

在这里插入图片描述
通过LocalSocket来用命令的方式启动Process
在这里插入图片描述

Activity启动

Activity启动的核心类

  • ActivityThread:应用进程的启动初始化和四大组件在应用进程的调度
  • ApplicationThread:应用进程和系统进程夸进程通信的服务端
  • IActivityManager:ActivityManagerService在应用进程的binder代理对象,系统进程和应用进程进行跨进程通信的客户端
  • SystemServer:系统进程的启动初始化
  • ActivityManagerService:四大组件在系统进程的调度,系统进程和应用进程进行夸进程通信的服务端
  • IApplicationThread:ApplicationThread在系统进程的binder代理对象,系统进程和应用进程进行夸进程通信的客户端
  • ServiceManager:系统服务的管理

Activity启动过程

在这里插入图片描述

  • 通过Context的startActivity方法发起Activity的启动
  • 然后进入ContextImpl中的startActivity方法,并在该方法中通过ActivityThread获取到Instrumentation对象,并调用execStartActivity方法
  • 在execStartActivity方法中会通过IActivityManager代理对象触发夸进程调用,把Activity的一些信息参数传递到系统进程ActivityManagerService中
  • 在ActivityManagerService会逐层调用,然后进入ActivityStarter类中的startActivityMayWait方法,ActivityStarter会进行一些权限检查,并创建ActivityRecord对象把Activity信息存到该对象中去
  • 加下来会执行到ActivityStack中,顾名思义,这个是对Activity栈进行管理的操作
  • 接着继续执行到ActivityStackSupervisor类的startSpecificActivityLocked方法,在这个方法里面就会去判断当前需要启动的Activity所在的进程是否已经存在如果存在则调用realStartActivityLocked方法,否则就startProcessAsync方法优先启动进程
  • 接下来Android8.0以后的代码和8.0以前的代码就有一些小差异,但是做的事情都是一样的:通过IApplicationThread跨进程通知应用进程,并继续在应用进程执行下一步操作
  • 应用进程在收到通知后会在Handler类H中去执行Activity的创建以及回掉各个Activity的生命周期的方法(通过状态模式来实现Activity的生命周期的切换)
private void performLifecycleSequence(ActivityClientRecord r, IntArray path,
            ClientTransaction transaction) {
        final int size = path.size();
        for (int i = 0, state; i < size; i++) {
            state = path.get(i);
            if (DEBUG_RESOLVER) {
                Slog.d(TAG, tId(transaction) + "Transitioning activity: "
                        + getShortActivityName(r.token, mTransactionHandler)
                        + " to state: " + getStateName(state));
            }
            switch (state) {
                case ON_CREATE:
                    mTransactionHandler.handleLaunchActivity(r, mPendingActions,
                            null /* customIntent */);
                    break;
                case ON_START:
                    mTransactionHandler.handleStartActivity(r, mPendingActions);
                    break;
                case ON_RESUME:
                    mTransactionHandler.handleResumeActivity(r.token, false /* finalStateRequest */,
                            r.isForward, "LIFECYCLER_RESUME_ACTIVITY");
                    break;
                case ON_PAUSE:
                    mTransactionHandler.handlePauseActivity(r.token, false /* finished */,
                            false /* userLeaving */, 0 /* configChanges */, mPendingActions,
                            "LIFECYCLER_PAUSE_ACTIVITY");
                    break;
                case ON_STOP:
                    mTransactionHandler.handleStopActivity(r.token, false /* show */,
                            0 /* configChanges */, mPendingActions, false /* finalStateRequest */,
                            "LIFECYCLER_STOP_ACTIVITY");
                    break;
                case ON_DESTROY:
                    mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
                            0 /* configChanges */, false /* getNonConfigInstance */,
                            "performLifecycleSequence. cycling to:" + path.get(size - 1));
                    break;
                case ON_RESTART:
                    mTransactionHandler.performRestartActivity(r.token, false /* start */);
                    break;
                default:
                    throw new IllegalArgumentException("Unexpected lifecycle state: " + state);
            }
        }
    }
  • 这里注意一个误区,Activity的启动计时是以onWindowFocused为准而不是以onResume为准,应为onResume执行完了之后,Activity才添加到Window中并进行进行界面布局的绘制,至此Activity的启动基本完成
Activity启动流程总结
  • Activity启动过程中其实就是应用进程和系统进程通过AIDL来进行双向跨进程通信的一个过程
    在这里插入图片描述

解决ActivityNotFoundException问题

为什么会有ActivityNotFoundException这个异常
  • Activity需要在配置文件中注册,然后在系统进程中对配置文件中的Activity进行检测,如果发现Activity没有注册就会返回一个错误的结果,然后在Instrumentation类的checkStartActivityResult方法中会抛出这个异常
    在这里插入图片描述
实现插件化启动Activity如何解决这个问题?
  • Activity的信息类是被装在Intent中传递给ActivityManagerService中的,然后在ActivityManagerService去对Activity信息进行检测,如果我们想启动一个没有注册过的Activity可以在应用进程的某一处节点hook掉,把我们要启动的目标Activity替换成已经注册好的Activity,这样在系统进程进行Activity检测的时候就可以骗过去
  • 系统进程完成Activity检测后会再回传到应用进程,然后根据Activity信息来创建Activity类并执行生命周期的方法,所以我们可以在应用进程的某一处节点再hook一下,把Activity类信息替换回我们想要启动的目标Activity,这样就可以启动未注册过的Activity而不报错
如何寻找Hook点?
  • 由于我们锁需要hook的地方都是在应用进程的,所以应用进程的hook我们是可以轻松的通过Java反射,动态代理来进行拦截的
  • Hook的原则是静态变量或者单例对象,尽量Hook pulic的对象和方法,非public不保证每个版本都一样,需要适配
Activity启动流程的hook点(针对Android9.0以上)
  • ActivityManager的IActivityManagerSingleton属性
    在这里插入图片描述
    在这里插入图片描述
    hook代码:
Method getServiceMethod = ActivityManager.class.getDeclaredMethod("getService");
        getServiceMethod.setAccessible(true);
        Object iActivityManagerObj = getServiceMethod.invoke(ActivityManager.class, null);

        Field IActivityManagerSingleton = ActivityManager.class.getDeclaredField("IActivityManagerSingleton");
        IActivityManagerSingleton.setAccessible(true);
        Object IActivityManagerSingletonObj = IActivityManagerSingleton.get(ActivityManager.class);

        Class<?> singletonCls = Class.forName("android.util.Singleton");
        Field mInstance = singletonCls.getDeclaredField("mInstance");
        mInstance.setAccessible(true);

        Class<?> iActivityManagerInterface = Class.forName("android.app.IActivityManager");
        Object iActivityManagerProxy = Proxy.newProxyInstance(ActivityManager.class.getClassLoader()
                , new Class<?>[]{iActivityManagerInterface}, new IActivityManagerHandler(iActivityManagerObj));
        mInstance.set(IActivityManagerSingletonObj, iActivityManagerProxy);
  • 用Java动态代理把Activity信息替换成代理Activity
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5C9W5HaD-1587118235169)(/download/attachments/126756629/%E4%BC%81%E4%B8%9A%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_dcad1e9d-499a-48b8-a034-cbf08215c2ec.png?version=1&modificationDate=1587079653666&api=v2 '企业微信截图_dcad1e9d-499a-48b8-a034-cbf08215c2ec.png')]

  • 通过hook Handle的Callback来拦截回传Activity的信息,并换回我们要启动的Activity
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xtU1sAAh-1587118235169)(/download/attachments/126756629/%E4%BC%81%E4%B8%9A%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_e3953cb0-a42f-44d6-a6db-604666da5dfb.png?version=1&modificationDate=1587079782787&api=v2 '企业微信截图_e3953cb0-a42f-44d6-a6db-604666da5dfb.png')]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值