相關文章
Android系統架構與系統源碼目錄
Android系統啓動流程(一)解析init進程啓動過程
Android系統啓動流程(二)解析Zygote進程啓動過程
Android系統啓動流程(三)解析SyetemServer進程啓動過程
Android系統啓動流程(四)Launcher啓動過程與系統啓動流程javascript
在此前我講過Android系統的啓動流程,系統啓動後,咱們就比較關心應用程序是如何啓動的,這一篇咱們來一塊兒學習Android7.0 應用程序進程啓動過程,須要注意的是「應用程序進程啓動過程」,而不是應用程序啓動過程。關於應用程序啓動過程,我會在後續系列的文章中講到。但願閱讀這篇文章前先閱讀本文列出的相關文章,要不你一臉矇蔽,就別怪我了。java
要想啓動一個應用程序,首先要保證這個應用程序所須要的應用程序進程已經被啓動。ActivityManagerService在啓動應用程序時會檢查這個應用程序須要的應用程序進程是否存在,不存在就會請求Zygote進程將須要的應用程序進程啓動。在Android系統啓動流程(二)解析Zygote進程啓動過程這篇文章中,我提到了Zygote的Java框架層中,會建立一個Server端的Socket,這個Socket用來等待ActivityManagerService來請求Zygote來建立新的應用程序進程的。咱們知道Zygote進程經過fock自身建立的應用程序進程,這樣應用程序程序進程就會得到Zygote進程在啓動時建立的虛擬機實例。固然,在應用程序建立過程當中除了獲取虛擬機實例,還能夠得到Binder線程池和消息循環,這樣運行在應用進程中應用程序就能夠方便的使用Binder進行進程間通訊以及消息處理機制了。先給出應用程序進程啓動過程的時序圖,而後對每個步驟進行詳細分析,以下圖所示。android
ActivityManagerService會經過調用startProcessLocked函數來向Zygote進程發送請求,以下所示。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java數組
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
...
try {
try {
final int userId = UserHandle.getUserId(app.uid);
AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, userId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
int uid = app.uid;//1
int[] gids = null;
int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
if (!app.isolated) {
...
/** * 2 對gids進行建立和賦值 */
if (ArrayUtils.isEmpty(permGids)) {
gids = new int[2];
} else {
gids = new int[permGids.length + 2];
System.arraycopy(permGids, 0, gids, 2, permGids.length);
}
gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
gids[1] = UserHandle.getUserGid(UserHandle.getUserId(uid));
}
...
if (entryPoint == null) entryPoint = "android.app.ActivityThread";//3
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
app.processName);
checkTime(startTime, "startProcess: asking zygote to start proc");
/** * 4 */
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);
...
} catch (RuntimeException e) {
...
}
}
...
}複製代碼
在註釋1處的達到建立應用程序進程的用戶ID,在註釋2處對用戶組ID:gids進行建立和賦值。註釋3處若是entryPoint 爲null則賦值爲"android.app.ActivityThread"。在註釋4處調用Process的start函數,將此前獲得的應用程序進程用戶ID和用戶組ID傳進去,第一個參數entryPoint咱們得知是"android.app.ActivityThread",後文會再次提到它。接下來咱們來查看Process的start函數,以下所示。
frameworks/base/core/java/android/os/Process.java微信
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
...
}
}複製代碼
start函數中只調用了startViaZygote函數:
frameworks/base/core/java/android/os/Process.java架構
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) {
/** * 1 */
ArrayList<String> argsForZygote = new ArrayList<String>();
argsForZygote.add("--runtime-args");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
...
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());
}
...
argsForZygote.add(processClass);
if (extraArgs != null) {
for (String arg : extraArgs) {
argsForZygote.add(arg);
}
}
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
}複製代碼
在註釋1處建立了字符串列表argsForZygote ,並將啓動應用進程的啓動參數保存在argsForZygote中,函數的最後會調用zygoteSendArgsAndGetResult函數,須要注意的是,zygoteSendArgsAndGetResult函數中第一個參數中調用了openZygoteSocketIfNeeded函數,而第二個參數是保存應用進程的啓動參數的argsForZygote。zygoteSendArgsAndGetResult函數以下所示。
frameworks/base/core/java/android/os/Process.javaapp
private static ProcessStartResult zygoteSendArgsAndGetResult(
ZygoteState zygoteState, ArrayList<String> args)
throws ZygoteStartFailedEx {
try {
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);
}
}複製代碼
zygoteSendArgsAndGetResult函數主要作的就是將傳入的應用進程的啓動參數argsForZygote,寫入到ZygoteState中,結合上文咱們知道ZygoteState實際上是由openZygoteSocketIfNeeded函數返回的,那麼咱們接着來看openZygoteSocketIfNeeded函數,代碼以下所示。
frameworks/base/core/java/android/os/Process.java框架
private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
try {
primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET);//1
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
}
}
if (primaryZygoteState.matches(abi)) {//2
return primaryZygoteState;
}
// The primary zygote didn't match. Try the secondary.
if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
try {
secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET);//3
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
}
}
if (secondaryZygoteState.matches(abi)) {
return secondaryZygoteState;
}
throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);複製代碼
在講到Zygote進程啓動過程時咱們得知,在Zygote的main函數中會建立name爲「zygote」的Server端Socket。在註釋1處會調用ZygoteState的connect函數與名稱爲ZYGOTE_SOCKET的Socket創建鏈接,這裏ZYGOTE_SOCKET的值爲「zygote」。註釋2處若是鏈接name爲「zygote」的Socket返回的primaryZygoteState與當前的abi不匹配,則會在註釋3處鏈接name爲「zygote_secondary」的Socket。這兩個Socket區別就是:name爲"zygote」的Socket是運行在64位Zygote進程中的,而name爲「zygote_secondary」的Socket則運行在32位Zygote進程中。既然應用程序進程是經過Zygote進程fock產生的,當要鏈接Zygote中的Socket時,也須要保證位數的一致。socket
Socket進行鏈接成功並匹配abi後會返回ZygoteState類型對象,咱們在分析zygoteSendArgsAndGetResult函數中講過,會將應用進程的啓動參數argsForZygote寫入到ZygoteState中,這樣Zygote進程就會收到一個建立新的應用程序進程的請求,咱們回到ZygoteInit的main函數,以下所示。ide
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
...
try {
...
//註冊Zygote用的Socket
registerZygoteSocket(socketName);//1
...
//預加載類和資源
preload();//2
...
if (startSystemServer) {
//啓動SystemServer進程
startSystemServer(abiList, socketName);//3
}
Log.i(TAG, "Accepting command socket connections");
//等待客戶端請求
runSelectLoop(abiList);//4
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}複製代碼
這些內容在Android系統啓動流程(二)解析Zygote進程啓動過程講過,但爲了更好的理解我再講一遍。註釋1處經過registerZygoteSocket函數來建立一個Server端的Socket,這個name爲」zygote」的Socket用來等待ActivityManagerService來請求Zygote來建立新的應用程序進程。註釋2處用來預加載類和資源。註釋3處用來啓動SystemServer進程,這樣系統的關鍵服務也會由SystemServer進程啓動起來。註釋4處調用runSelectLoop函數來等待ActivityManagerService的請求。咱們就來查看runSelectLoop函數:
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();//2
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
while (true) {
...
for (int i = pollFds.length - 1; i >= 0; --i) {
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
boolean done = peers.get(i).runOnce();//1
if (done) {
peers.remove(i);
fds.remove(i);
}
}
}
}
}複製代碼
當有ActivityManagerService的請求數據到來時會調用註釋1處的代碼,結合註釋2處的代碼,咱們得知註釋1處的代碼實際上是調用ZygoteConnection的runOnce函數來處理請求的數據:
frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
args = readArgumentList();//1
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
Log.w(TAG, "IOException on command socket " + ex.getMessage());
closeSocket();
return true;
}
...
try {
parsedArgs = new Arguments(args);//2
...
/** * 3 */
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
parsedArgs.appDataDir);
} catch (ErrnoException ex) {
....
}
try {
if (pid == 0) {
// in child
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
return true;
} else {
// in parent...pid of < 0 means failure
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}複製代碼
在註釋1處調用readArgumentList函數來獲取應用程序進程的啓動參數,並在註釋2處將readArgumentList函數返回的字符串封裝到Arguments對象parsedArgs中。註釋3處調用Zygote的forkAndSpecialize函數來建立應用程序進程,參數爲parsedArgs中存儲的應用進程啓動參數,返回值爲pid。forkAndSpecialize函數主要是經過fork當前進程來建立一個子進程的,若是pid等於0,則說明是在新建立的子進程中執行的,就會調用handleChildProc函數來啓動這個子進程也就是應用程序進程,以下所示。
frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {
...
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs, null /* classLoader */);
}
}複製代碼
handleChildProc函數中調用了RuntimeInit的zygoteInit函數,以下所示。
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
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();//1
applicationInit(targetSdkVersion, argv, classLoader);//2
}複製代碼
註釋1處會在新建立的應用程序進程中建立Binder線程池,這個在下一篇文章會詳細介紹。在註釋2處調用了applicationInit函數:
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
...
final Arguments args;
try {
args = new Arguments(argv);
} catch (IllegalArgumentException ex) {
Slog.e(TAG, ex.getMessage());
return;
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
invokeStaticMain(args.startClass, args.startArgs, classLoader);//1
}複製代碼
在applicationInit中會在註釋1處調用invokeStaticMain函數,須要注意的是第一個參數args.startClass,這裏指的就是此篇文章開頭提到的參數:android.app.ActivityThread。接下來咱們查看invokeStaticMain函數,以下所示。
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
Class<?> cl;
try {
cl = Class.forName(className, true, classLoader);//1
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}
Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });//2
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
}
...
throw new ZygoteInit.MethodAndArgsCaller(m, argv);//3
}複製代碼
能夠看到註釋1處經過反射來得到android.app.ActivityThread類,接下來在註釋2處來得到ActivityThread的main函數,並將main函數傳入到註釋3處的ZygoteInit中的MethodAndArgsCaller類的構造函數中,MethodAndArgsCaller類內部會經過反射調用ActivityThread的main函數,這樣應用程序進程就建立完成了。
咱們首先來看RuntimeInit類的zygoteInit函數,以下所示
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
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();//1
applicationInit(targetSdkVersion, argv, classLoader);
}複製代碼
註釋1處會在新建立的應用程序進程中建立Binder線程池,來查看nativeZygoteInit函數:
private static final native void nativeZygoteInit();複製代碼
很明顯nativeZygoteInit是一個jni方法,它對應的函數是什麼呢。在 AndroidRuntime.cpp的JNINativeMethod數組中咱們得知它對應的函數是com_android_internal_os_RuntimeInit_nativeZygoteInit,以下所示。
frameworks/base/core/jni/AndroidRuntime.cpp
static const JNINativeMethod gMethods[] = {
{ "nativeFinishInit", "()V",
(void*) com_android_internal_os_RuntimeInit_nativeFinishInit },
{ "nativeZygoteInit", "()V",
(void*) com_android_internal_os_RuntimeInit_nativeZygoteInit },
{ "nativeSetExitWithoutCleanup", "(Z)V",
(void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup },
};複製代碼
接着來查看 com_android_internal_os_RuntimeInit_nativeZygoteInit函數:
frameworks/base/core/jni/AndroidRuntime.cpp
static void com_android_internal_os_RuntimeInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
{
gCurRuntime->onZygoteInit();
}複製代碼
gCurRuntime是在AndroidRuntime初始化就建立的。以下所示。
frameworks/base/core/jni/AndroidRuntime.cpp
AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) :
mExitWithoutCleanup(false),
mArgBlockStart(argBlockStart),
mArgBlockLength(argBlockLength)
{
...
gCurRuntime = this;
}複製代碼
在Android系統啓動流程(二)解析Zygote進程啓動過程這篇文章咱們得知AppRuntime繼承AndroidRuntime,AppRuntime建立時就會調用AndroidRuntime的構造函數,gCurRuntime就會被初始化,它指向的是AppRuntime,所以咱們來查看AppRuntime的onZygoteInit函數,AppRuntime的實如今app_main.cpp中,以下所示。
frameworks/base/cmds/app_process/app_main.cpp
virtual void onZygoteInit()
{
sp<ProcessState> proc = ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}複製代碼
最後一行會調用ProcessState的startThreadPool函數:
frameworks/native/libs/binder/ProcessState.cpp
void ProcessState::startThreadPool()
{
AutoMutex _l(mLock);
if (!mThreadPoolStarted) {
mThreadPoolStarted = true;
spawnPooledThread(true);
}
}複製代碼
支持Binder通訊的進程中都有一個ProcessState類,它裏面有一個mThreadPoolStarted 變量,來表示Binder線程池是否已經被啓動過,默認值爲false。在每次調用這個函數時都會先去檢查這個標記,從而確保Binder線程池只會被啓動一次。若是Binder線程池未被啓動則設置mThreadPoolStarted爲true,最後調用spawnPooledThread函數來建立線程池中的第一個線程,也就是線程池的main線程,以下所示。
frameworks/native/libs/binder/ProcessState.cpp
void ProcessState::spawnPooledThread(bool isMain)
{
if (mThreadPoolStarted) {
String8 name = makeBinderThreadName();
ALOGV("Spawning new pooled thread, name=%s\n", name.string());
sp<Thread> t = new PoolThread(isMain);
t->run(name.string());//1
}
}複製代碼
能夠看到Binder線程爲一個PoolThread。註釋1調用PoolThread的run函數來啓動一個啓動一個新的線程。來查看PoolThread類裏作了什麼:
frameworks/native/libs/binder/ProcessState.cpp
class PoolThread : public Thread
{
..
protected:
virtual bool threadLoop()
{
IPCThreadState::self()->joinThreadPool(mIsMain);//1
return false;
}
const bool mIsMain;
};複製代碼
PoolThread類繼承了Thread類。註釋1處會將調用IPCThreadState的joinThreadPool函數,將當前線程註冊到Binder驅動程序中,這樣咱們建立的線程就加入了Binder線程池中,這樣新建立的應用程序進程就支持Binder進程間通訊了,Binder線程池啓動過程就講到這,接下來咱們來學習消息循環建立過程。
首先咱們回到上篇最後講到的RuntimeInit的invokeStaticMain函數,代碼以下所示。
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
Class<?> cl;
...
throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}複製代碼
invokeStaticMain函數在上篇已經講過,這裏再也不贅述,主要是看最後一行,會拋出一個MethodAndArgsCaller異常,這個異常會被ZygoteInit的main函數捕獲,以下所示。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
...
try {
...
} catch (MethodAndArgsCaller caller) {
caller.run();//1
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}複製代碼
註釋1處捕獲到MethodAndArgsCaller 時會執行caller的run函數,以下所示。
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
public static class MethodAndArgsCaller extends Exception implements Runnable {
private final Method mMethod;
private final String[] mArgs;
public MethodAndArgsCaller(Method method, String[] args) {
mMethod = method;
mArgs = args;
}
public void run() {
try {
mMethod.invoke(null, new Object[] { mArgs });//1
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
}
...
throw new RuntimeException(ex);
}
}
}複製代碼
根據上一篇文章咱們得知,mMethod指的就是ActivityThread的main函數,mArgs 指的是應用程序進程的啓動參數。在註釋1處調用ActivityThread的main函數,代碼以下所示。
frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
...
Looper.prepareMainLooper();//1
ActivityThread thread = new ActivityThread();//2
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();//3
throw new RuntimeException("Main thread loop unexpectedly exited");
}複製代碼
註釋1處在當前應用程序進程中建立消息循環,註釋2處建立ActivityThread,註釋3處調用Looper的loop,使得Looper開始工做,開始處理消息。能夠看出,系統在應用程序進程啓動完成後,就會建立一個消息循環,用來方便的使用Android的消息處理機制。
歡迎關注個人微信公衆號,第一時間得到博客更新提醒,以及更多成體系的Android相關原創技術乾貨。
掃一掃下方二維碼或者長按識別二維碼,便可關注。