关于startActivity方法

来源:2-4 Launcher应用启动之ActivityThread源码分析

冷色幽默

2020-06-24 17:43:40

老师,看完了这节视频,突然间想到了之前的一道面试题,就是startActivity是异步还是同步的,您在视频里面讲了类似的,就是每一次IPC的时候,其实都是在Binder线程池中进行的,所以,我们就可以判定startActivity实际上就是异步的呢?

另外,我们如何判断IPC的时候是在子线程中进行的呢?这一部分要去哪里追源码呢?

写回答

1回答

LovelyChubby

2020-06-25

很是优秀,能联想这么多。这两个问题牵扯的面比较广,我分开回答。下面的代码你可以拷贝验证一下。

问题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个。

5

移动端架构师

亲历日活千万级APP全流程落地,成为技术强+思维深+视野广 的P7级移动端架构师

577 学习 · 452 问题

查看课程