关于startActivity方法
来源:2-4 Launcher应用启动之ActivityThread源码分析
冷色幽默
2020-06-24 17:43:40
老师,看完了这节视频,突然间想到了之前的一道面试题,就是startActivity是异步还是同步的,您在视频里面讲了类似的,就是每一次IPC的时候,其实都是在Binder线程池中进行的,所以,我们就可以判定startActivity实际上就是异步的呢?
另外,我们如何判断IPC的时候是在子线程中进行的呢?这一部分要去哪里追源码呢?
1回答
很是优秀,能联想这么多。这两个问题牵扯的面比较广,我分开回答。下面的代码你可以拷贝验证一下。
问题1:startActivity分为两个阶段。
一、App跟AMS通信,这一步虽然是binder通信,但它是同步完成的。请仔细阅读下面的跨进程调用的案例。
//1. 建立adil文件 和 Service服务,APP和该Service使用Aidl通信 //这是个aidl文件,在as中可以右键选择创建aidl的文件类型即可快速完成。 interface IRemoteServiceInterface { String startActivity(); } class RemoteService extends Service() { private IRemoteServiceInterface.Stub stub = object : IRemoteServiceInterface.Stub() { @override public String startActivity() { //这个日志输出==startActivity-remote: Binder:29226_3 Log.e("startActivity-remote", Thread.currentThread().name) return "start Activity from RemoteService" } } @override public IBinder onBind(Intent intent) { return stub } } //2. 在manifest里面配置成运行在另一进程的服务 <application <service android:name=".RemoteService" android:enabled="true" android:process=":remoteService"> </service> </application> ServiceConnection connection =new ServiceConnection { @override public void onServiceDisconnected(ComponentName name) { } @override public void onServiceConnected(ComponentName name,IBinder service) { //4. 服务启动成功之后,获取到该服务的本地代理对象 IRemoteServiceInterface iRemoteServiceInterface = IRemoteServiceInterface.Stub.asInterface(service) //接着调用服务中的startActivity 方法 String result = iRemoteServiceInterface.startActivity(); //如果上面一步是异步的,那么下面三行日志输出 result应该为空。 //但根据验证这里三句日志是依次输出的,且是在主线程。 //所以结论是尽管使用aidl通信,APP获取到Service的本地代理对象之后,调用它的方法调用也是同步的(请注意 远程方法的代码执行(加载数据...各种运算啊)还是运行在子线程里面的。) //你可能会疑惑,怎么可能呢? Server的方法不是在子线程的吗?这里的日志怎么会同步返回结果,顺序打印日志呢? //这是因为当前线程会被挂起,远程方法执行完之后,也会有一个跨进程通信把结果返回给我们,此时就恢复刚才挂起的线程了 //如果 String result = iRemoteServiceInterface.startActivity();运行在子线程里面,下面的日志就不是顺序打印了 // // // //所以Android中的StartActivity,调用AMS的时候也是同步完成的。这是对第一步答案的验证。 Log.e("current thread:", Thread.currentThread().name) Log.e("startActivity:", result) Log.e("after startActivity:", "completed") } } //3.App中启动该服务 bindService( new Intent(this, RemoteService.class), connection, Service.BIND_AUTO_CREATE )
二、AMS调整任务栈完成之后,会通过ApplicaitonThread也就是APP端在Service的代理对象。通知ActivityThread 创建Activity。但这一步是异步完成的。
ActivityStackSupervisor.java //根据视频课上的分析AMS一堆检查,任务栈调整完成了之后,是在这里通知ActivityThread //启动Activity的 boolean realStartActivityLocked{ final ActivityLifecycleItem lifecycleItem; if (andResume) { lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward()); } else { lifecycleItem = PauseActivityItem.obtain(); } clientTransaction.setLifecycleStateRequest(lifecycleItem); // Schedule transaction. mService.getLifecycleManager().scheduleTransaction(clientTransaction); }
//上面最终会走到ActivityThread里面 class ActivityThread extends ClientTransactionHandler{ public abstract class { /** Prepare and schedule transaction for execution. */ void scheduleTransaction(ClientTransaction transaction) { transaction.preExecute(this); //这里会使用handler 发送一条消息,这条消息被轮训到之后, //才会触发handleLaunchActivity方法被调用,Activity才会被创建 sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction); } }
问题二、binder线程池在哪里被创建的
这个是systemserver进程被创建成功后,在ZygoteInit.zygoteInit方法中的ZygoteInit.nativeZygoteInit()。这是一个native方法。有兴趣的话可以参考【进程创建成功后binder线程池的启动】
既然你问到这里了,我感到你可能有点疑惑疑惑。binder线程池,不是说这100多个服务运行在线程池里面。而是用来接收处理APP的跨进程的请求的。有请求来了,就使用一个线程来执行本次任务。
binder线程池跟我们平时线程池没多大区别,数量都是有限的。android10.0上约定最多是31个,之前最多是15个。
相似问题