Android中主线程为什么不会因为Looper.loop()里的死循环卡死?

先说结论:主线程在没有消息的时候是阻塞的。主线程没有卡死,简单来说是因为有其他线程通过handler发送消息唤醒主线程。阻塞并不是卡死,阻塞可以简单理解为让出CPU,进入休眠状态,卡死就是ANR了。

Android中的主线程:就是Zygote进程通过fock自身创建的应用程序进程。

我们下面先看一个Android Application启动的简单过程。详细过程可以参考从点击桌面应用图标到MainActivity的onResume过程分析

先来一张图

Android中主线程为什么不会因为Looper.loop()里的死循环卡死?_第1张图片
Android主线程不会卡死的原因.jpg

步骤如下

  1. App进程使用ActivityManagerService的代理对象ActivityManagerProxy通过Binder的方式进行进程间通信,通知system_server进程的ActivityManagerService要启动进程。

  2. system_server进程的ActivityManagerService收到通知后,使用ApplicationThread的代理对象ApplicationThreadProxy通过Binder的方式进行进程间通信,通知App进程的ApplicationThread对象启动Application。

  3. App进程的ApplicationThread对象收到通知后,通过handler向ActivityThread发送消息来启动Application。

应用程序的入口是ActivityThread类的main方法,该方法运行在主线程中。注意
ActivityThread并不是一个Thread。

public final class ActivityThread extends ClientTransactionHandler {

    //注释3处,初始化mAppThread对象
    final ApplicationThread mAppThread = new ApplicationThread();
    //注释4处,获取当前线程的Looper对象
    final Looper mLooper = Looper.myLooper();
    //注释5处,构建一个 H对象mH
    final H mH = new H();

    public static void main(String[] args) {
         //... 
        //注释1处,准备主线程的Looper
        Looper.prepareMainLooper();

        //...
        //注释2处,初始化 ActivityThread对象
        ActivityThread thread = new ActivityThread();
        //注释6处,连接当前进程和Application
        thread.attach(false, startSeq);
        //主线程的handler
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        //
        //主线程的消息队列开始循环
        Looper.loop();
    }

}

在注释1处,先准备主线程的Looper。然后在注释2处,初始化 ActivityThread对象。
在注释2处ActivityThread对象对象初始化完毕以后,注释3,4,5,处的对象都被赋值了。

在注释3处,构建了一个ApplicationThread类的实例mAppThread。ApplicationThread类是ActivityThread的内部类。

private class ApplicationThread extends IApplicationThread.Stub {
    //...
}

ApplicationThread实现了IApplicationThread.Stub,可以用于进程间通过Binder进行通信。

这里我有个问题,ApplicationThread是运行在应用的主线程中的吗?

答:我认为,ApplicationThread是运行在应用的Binder线程池中的。

注释4处,获取当前线程的Looper对象。

注释5处,构建一个 H对象mH。H是ActivityThread的内部类,继承了Handler类。用来发送消息。

class H extends Handler {
    
}

我们接下来看一下注释6处的ActivityThread的attach方法,在这个方法中会启动Application。

private void attach(boolean system, long startSeq) {
  //...
  if (!system) {
        //...
        RuntimeInit.setApplicationObject(mAppThread.asBinder());
        //获取ActivityManagerService对象
        final IActivityManager mgr = ActivityManager.getService();
        try {
            //注释1处,调用ActivityManagerService的attachApplication方法
            mgr.attachApplication(mAppThread, startSeq);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
           //...
        } 
    //...
}

在上面的方法的注释1处,可以看出来,主线程是通过ApplicationThread和ActivityManagerService进行通信的。(ActivityManagerService运行在SystemServer进程)

ActivityManagerService的attachApplication方法

@Override
public final void attachApplication(IApplicationThread thread, long startSeq) {
    synchronized (this) {
        //...
        attachApplicationLocked(thread, callingPid, callingUid, startSeq);
        //...
    }
}

ActivityManagerService的attachApplicationLocked方法

private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid, int callingUid, long startSeq) {
    //...
    //注释1处,调用ApplicationThread的bindApplication方法
    thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                        null, null, null, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.persistent,
                        new Configuration(getGlobalConfiguration()), app.compat,
                        getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial, isAutofillCompatEnabled);

    //...
    // 要启动的Activity等待在这个进程中运行
    if (normalMode) {
        try {
            //注释2处,调用ActivityStackSupervisor的attachApplicationLocked方法
            if (mStackSupervisor.attachApplicationLocked(app)) {
                didSomething = true;
            }
        } 
    }
    //...
   return true;
}

在注释1处,调用了ApplicationThread的bindApplication方法

public final void bindApplication(String processName, ApplicationInfo appInfo,
                List providers, ComponentName instrumentationName,
                ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                IInstrumentationWatcher instrumentationWatcher,
                IUiAutomationConnection instrumentationUiConnection, int debugMode,
                boolean enableBinderTracking, boolean trackAllocation,
                boolean isRestrictedBackupMode, boolean persistent, Configuration config,
                CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
                String buildSerial, boolean autofillCompatibilityEnabled) {

      //注释1处,调用ActivityThread的sendMessage方法
      sendMessage(H.BIND_APPLICATION, data);
}

ActivityThread的sendMessage方法

void sendMessage(int what, Object obj) {
    sendMessage(what, obj, 0, 0, false);
}
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
    Message msg = Message.obtain();
    msg.what = what;
    msg.obj = obj;
    msg.arg1 = arg1;
    msg.arg2 = arg2;
    if (async) {
        msg.setAsynchronous(true);
    }
    mH.sendMessage(msg);
}

在ApplicationThread的bindApplication方法的注释1处,调用ActivityThread的sendMessage方法发送消息,注意此时我们是在ApplicationThread所在的线程(并不是主线程)发送消息的。但是因为mH是在主线程初始化的,所以mH关联的是主线程的消息队列,所以消息最终会添加到主线程的消息队列中。

我们看一下H的handleMessage方法,此方法是在主线程中调用的。

case BIND_APPLICATION:
    AppBindData data = (AppBindData)msg.obj;
    //1. 调用handleBindApplication方法
    handleBindApplication(data);
break;

ActivityThread的handleBindApplication方法

private void handleBindApplication(AppBindData data) {
  //...
  //设置应用的名字
  Process.setArgV0(data.processName);
  //创建Instrumentation对象
  mInstrumentation = new Instrumentation();
  Application app;
  //注释1处
  app = data.info.makeApplication(data.restrictedBackupMode, null);
  mInitialApplication = app;
 //...
 //注释2处启动app
 mInstrumentation.callApplicationOnCreate(app);
 //...
}

注释1处,data.info是一个LoadedApk类型的对象,我们看一下LoadedApk的makeApplication方法。

 public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        if (mApplication != null) {
            return mApplication;
        }

        Application app = null;
        //获取appClass
        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

        try {
            java.lang.ClassLoader cl = getClassLoader();
            //在这里创建了应用的context
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            //注释1处,创建Application对象
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
        } 
        mActivityThread.mAllApplications.add(app);
        //把app赋值给mApplication
        mApplication = app;
        //我们在这里传入的instrumentation是null
        if (instrumentation != null) {
            try {
                instrumentation.callApplicationOnCreate(app);
            } 
        }

        //...
        return app;
    }

在makeApplication方法中的注释1处,调用了Instrumentation的newApplication方法。

public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        //创建了Application对象
        Application app = getFactory(context.getPackageName())
                .instantiateApplication(cl, className);
        //注释1处,调用Application的attach方法。之后我们就可以获取应用的context了。
        app.attach(context);
        return app;
    }

在handleBindApplication方法的注释2处调用了Instrumentation的callApplicationOnCreate方法

public void callApplicationOnCreate(Application app) {
        //到这里app已经启动了
        app.onCreate();
    }

到这里Application的onCreate方法已经被调用了,应用已经启动了。

通过上面的分析,我们可以知道我们的应用进程和其他进程通信是通过ApplicationThread。应用进程之间通信是通过Handler,Handler在其他线程将消息发送到主线程,唤醒了主线程。

总结一下:主线程在没有消息的时候是阻塞的。主线程没有卡死,简单来说是因为ApplicationThread通过handler发送消息唤醒主线程。阻塞并不是卡死,阻塞可以简单理解为让出CPU,进入休眠状态,卡死就是ANR了。

参考链接

  1. Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
  2. Android中为什么主线程不会因为Looper.loop()里的死循环阻塞?
  3. 从点击桌面应用图标到MainActivity的onResume过程分析

你可能感兴趣的:(Android中主线程为什么不会因为Looper.loop()里的死循环卡死?)