为什么Activity生命周期函数是运行在UI线程

Posted _houzhi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么Activity生命周期函数是运行在UI线程相关的知识,希望对你有一定的参考价值。

这是我自己给自己提的问题,或者说是Activity的生命周期函数是怎样运行在主线程的?下面简单分析一下,讨论的问题其实远远不止于这个问题。会涉及到进程的启动,Binder的线程池,ActivityThread中的消息处理。

进程开启

我们最开始接触android的时候,都知道主线程和非主线程区别,我们可以用Handler来将代码运行在主线程中。而主线程是如何开启的呢?在ActivityThread当中有个公有静态main方法,每次ActivityManagerService请求Zygote进程fork一个新的进程的时候,ActivityManagerService会同时发送一个指定新进程启动哪一个类的参数,ActivityManagerService指定的就是android.app.ActivityThread类。看看ActivityManagerService启动新进程的代码:


if (entryPoint == null) entryPoint = "android.app.ActivityThread";
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
       app.processName);
checkTime(startTime, "startProcess: asking zygote to start proc");
Process.ProcessStartResult startResult = Process.start(entryPoint,
       app.processName, uid, uid, gids, debugFlags, mountExternal,
       app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
       app.info.dataDir, entryPointArgs);

里面的entryPoint就是指定了zygote会启动的类。Process.start的第一个参数。可以看看Process.start函数,它会直接调用Process的startViaZygote私有静态方法,两个方法的参数是一致的:


private static ProcessStartResult startViaZygote(final String processClass,
                             final String niceName,
                             final int uid, final int gid,
                             final int[] gids,
                             int debugFlags, int mountExternal,
                             int targetSdkVersion,
                             String seInfo,
                             String abi,
                             String instructionSet,
                             String appDataDir,
                             String[] extraArgs)
                             throws ZygoteStartFailedEx 
   synchronized(Process.class) 
       ArrayList<String> argsForZygote = new ArrayList<String>();

       // --runtime-args, --setuid=, --setgid=,
       // and --setgroups= must go first
       argsForZygote.add("--runtime-args");
       argsForZygote.add("--setuid=" + uid);
       argsForZygote.add("--setgid=" + gid);
       if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) 
           argsForZygote.add("--enable-jni-logging");
       
       if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) 
           argsForZygote.add("--enable-safemode");
       
       if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) 
           argsForZygote.add("--enable-debugger");
       
       if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) 
           argsForZygote.add("--enable-checkjni");
       
       if ((debugFlags & Zygote.DEBUG_ENABLE_JIT) != 0) 
           argsForZygote.add("--enable-jit");
       
       if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) 
           argsForZygote.add("--generate-debug-info");
       
       if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) 
           argsForZygote.add("--enable-assert");
       
       if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) 
           argsForZygote.add("--mount-external-default");
        else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) 
           argsForZygote.add("--mount-external-read");
        else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) 
           argsForZygote.add("--mount-external-write");
       
       argsForZygote.add("--target-sdk-version=" + targetSdkVersion);

       //TODO optionally enable debuger
       //argsForZygote.add("--enable-debugger");

       // --setgroups is a comma-separated list
       if (gids != null && gids.length > 0) 
           StringBuilder sb = new StringBuilder();
           sb.append("--setgroups=");
           int sz = gids.length;
           for (int i = 0; i < sz; i++) 
               if (i != 0) 
                   sb.append(',');
               
               sb.append(gids[i]);
           
           argsForZygote.add(sb.toString());
       
       if (niceName != null) 
           argsForZygote.add("--nice-name=" + niceName);
       
       if (seInfo != null) 
           argsForZygote.add("--seinfo=" + seInfo);
       
       if (instructionSet != null) 
           argsForZygote.add("--instruction-set=" + instructionSet);
       
       if (appDataDir != null) 
           argsForZygote.add("--app-data-dir=" + appDataDir);
       
       argsForZygote.add(processClass);
       if (extraArgs != null) 
           for (String arg : extraArgs) 
               argsForZygote.add(arg);
           
       
       return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
   

为了节省空间,我把一些空格给去掉了。这个方法就是将参数组装到argsForZygote当中,存到数组里面。而zygoteSendArgsAndGetResult方法则是通过socket将这些内容发送给Zygote进程:


/**
* Sends an argument list to the zygote process, which starts a new child
* and returns the child's pid. Please note: the present implementation
* replaces newlines in the argument list with spaces.
*
* @throws ZygoteStartFailedEx if process start failed for any reason
*/
private static ProcessStartResult zygoteSendArgsAndGetResult(
       ZygoteState zygoteState, ArrayList<String> args)
       throws ZygoteStartFailedEx 
   try 
       /**
        * See com.android.internal.os.ZygoteInit.readArgumentList()
        * Presently the wire format to the zygote process is:
        * a) a count of arguments (argc, in essence)
        * b) a number of newline-separated argument strings equal to count
        *
        * After the zygote process reads these it will write the pid of
        * the child or -1 on failure, followed by boolean to
        * indicate whether a wrapper process was used.
        */
       final BufferedWriter writer = zygoteState.writer;
       final DataInputStream inputStream = zygoteState.inputStream;

       writer.write(Integer.toString(args.size()));
       writer.newLine();

       int sz = args.size();
       for (int i = 0; i < sz; i++) 
           String arg = args.get(i);
           if (arg.indexOf('\\n') >= 0) 
               throw new ZygoteStartFailedEx(
                       "embedded newlines not allowed");
           
           writer.write(arg);
           writer.newLine();
       

       writer.flush();

       // Should there be a timeout on this?
       ProcessStartResult result = new ProcessStartResult();
       result.pid = inputStream.readInt();
       if (result.pid < 0) 
           throw new ZygoteStartFailedEx("fork() failed");
       
       result.usingWrapper = inputStream.readBoolean();
       return result;
    catch (IOException ex) 
       zygoteState.close();
       throw new ZygoteStartFailedEx(ex);
   

上面就是跟zygote进行socket通信的部分,相信用过socket的看着都非常熟悉。关于zygote启动,并且ActivityManagerService如何建立通信的,想了解的更详细的可以看看老罗这两篇文章Android系统进程Zygote启动过程的源代码分析,[Android应用程序进程启动过程的源代码分析](http://blog.csdn.net/luoshengyang/article/details/6747696)。当然直接看源码是最合适的。

而zygote如何回应的?看看zygote部分启动一个新进程后的代码,zygote中fork之后,子进程会调用RuntimeInit.zygoteInit方法,所以直接从RuntimeInit.zygoteInit开始看。


public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
       throws ZygoteInit.MethodAndArgsCaller 
   if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");

   Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
   redirectLogStreams();

   commonInit();
   nativeZygoteInit();
   applicationInit(targetSdkVersion, argv, classLoader);

nativeZygoteInit方法会调用ProcessState开启Binder线程池,供与Binder驱动通信使用。而applicationInit会调用invokeStaticMain方法调用ActivityManagerService传入的ActivityThread类的main方法。

其实整个流程就跟我们使用java命令运行程序是一样的。

main方法里面会设置主线程loop,然后调用Looper.loop()处理消息:


public static void main(String[] args) 
   Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
   SamplingProfilerIntegration.start();

   // CloseGuard defaults to true and can be quite spammy.  We
   // disable it here, but selectively enable it later (via
   // StrictMode) on debug builds, but using DropBox, not logs.
   CloseGuard.setEnabled(false);

   Environment.initForCurrentUser();

   // Set the reporter for event logging in libcore
   EventLogger.setReporter(new EventLoggingReporter());

   AndroidKeyStoreProvider.install();

   // Make sure TrustedCertificateStore looks in the right place for CA certificates
   final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
   TrustedCertificateStore.setDefaultUserDirectory(configDir);

   Process.setArgV0("<pre-initialized>");

   Looper.prepareMainLooper();

   ActivityThread thread = new ActivityThread();
   thread.attach(false);

   if (sMainThreadHandler == null) 
       sMainThreadHandler = thread.getHandler();
   

   if (false) 
       Looper.myLooper().setMessageLogging(new
               LogPrinter(Log.DEBUG, "ActivityThread"));
   

   // End of event ActivityThreadMain.
   Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
   Looper.loop();  //消息loop

   throw new RuntimeException("Main thread loop unexpectedly exited");

Handler机制的使用

而ActivityManagerService使用binder机制(IApplicationThread)调用某个进程的四大组件时,ActivityThread部分,一开始是运行在Binder线程池的,然后通过Handler机制发送消息给主线程,运行相关内容,比如说启动Activity,IApplicationThread的方法scheduleLaunchActivity实现就如此:


private class ApplicationThread extends ApplicationThreadNative 



@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
       ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
       CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
       int procState, Bundle state, PersistableBundle persistentState,
       List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
       boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) 

   updateProcessState(procState, false);

   ActivityClientRecord r = new ActivityClientRecord();

   r.token = token;
   r.ident = ident;
   r.intent = intent;
   r.referrer = referrer;
   r.voiceInteractor = voiceInteractor;
   r.activityInfo = info;
   r.compatInfo = compatInfo;
   r.state = state;
   r.persistentState = persistentState;

   r.pendingResults = pendingResults;
   r.pendingIntents = pendingNewIntents;

   r.startsNotResumed = notResumed;
   r.isForward = isForward;

   r.profilerInfo = profilerInfo;

   r.overrideConfig = overrideConfig;
   updatePendingConfiguration(curConfig);

   sendMessage(H.LAUNCH_ACTIVITY, r);



最终消息处理的地方是在类ActivityThread.H(Handler的子类)的handleMessage方法中,ActivityThread.H的looper就是主线程的looper,这里就已经是运行在主线程了:


public void handleMessage(Message msg) 
   if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
   switch (msg.what) 
       case LAUNCH_ACTIVITY: 
           Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
           final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

           r.packageInfo = getPackageInfoNoCheck(
                   r.activityInfo.applicationInfo, r.compatInfo);
           handleLaunchActivity(r, null);
           Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;


其他的四大组件也是一样的

以上是关于为什么Activity生命周期函数是运行在UI线程的主要内容,如果未能解决你的问题,请参考以下文章

Activity的生命周期

Activity启动过程全解析

Android Activity生命周期解析

Activity-运行状态及生命周期

Activity 的生命周期是啥?

Android Activity组件及其生命周期