深入剖析Android四大元件(七)——Activity啟動的4個階段
在Activity的啟動流程中,共有4個階段,下面將一一道來。
1.第一階段——啟動資訊翻譯以及服務呼叫
這一階段的工作主要是在應用程式本地完成的,主要為啟動Activity做一些引數上的準備,然後服務的代理將這些引數轉發到服務,開始Activity啟動的流程,如下圖:
2.第二階段——Activity的相關處理
到了這個階段,工作就已經由ActivityManagerService處理了。在此階段,它將完成對ActivityStack及應用程式程序的相關處理,如下圖:
在以上流程中,我們完成了對Activity任務,Activity記錄和視窗的處理等。接下來,由startPausingLock()方法來開始真正的啟動流程,所以這裡將其稱為預處理流程。這裡,它丟擲了一個PAUSE_TIMEOUT_MSG訊息到處理程式,由處理程式啟動activityPaused()方法開始真正的啟動流程。
至此,應用程式就被新增到啟動歷史列表(mHistory)並生成相應的棧資訊。但要注意的是,這時應用程式還沒有自己的程序,所以需要做的下一件事情就是通知孵化程序為我們的應用程式孵化一個程序。
3.第三階段——處理應用程式程序
建立並啟動應用程式的入口是Process的start()方法,該方法的原型如下所示:
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid,int gid,int[] gids,
int debugFlags,int targetSdkVersion,
String[] zygoteArgs){
try{
return startViazygote(processClass,niceName,uid,gid,gids,debugFlags,targetSdkVersion,zygoteArgs);
}catch(ZygoteStartFailedEx ex){
Log.e(LOG_TAG,"Starting VM process through Zygote failed");
throw new RuntimeException("Starting VM process through Zygote failed",ex);
}
}
通過上面的程式碼可以看到,使用該方法時需要提供一個processClass引數。當應用程式程序建立完畢後,需要呼叫processClass類的main()方法繼續完成未完成的工作,這裡未完成的工作是指繼續完成啟動Activity的工作。
在ActivityManagerService()中,是這樣呼叫start()方法的:
private final void startProcessLocked(ProcessRecord app,String hostingType,String hostingNameStr){
..........
Process.ProcessStartResult startResult=Process.start("android.app.ActivityThread",app.processName,uid,gid,gids,debugFlags,app.info.targetSdkVersion,null);
.........
}
這裡start()方法的第一個引數為android.app.ActivityThread,也就是說,程序建立完成以後,將呼叫android.app.ActivityThread類的main()靜態方法完成啟動Activity的剩餘流程。
接下來,Process的startViaZygote()方法將處理這些輸入引數,將其變成一個引數陣列argsForZygote。下面的程式碼片段展示了這個過程,這裡我們需要關注一些標誌以及程序名字的處理:
private static ProcessStartResult startViaZygeote(......)throws ZygoteStartFailedEx{
Synchronized(Process.class){
......
//如果啟動了VM安全模式標誌(android:vmSafeMode設定為true)
if((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE)!=0){
argsForZygote.add("--enable-safemode");
}
//如果啟動了可除錯標誌(android:debuggable設定為true)
if((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER)!=0){
argsForZygote.add("--enable-debugger");
}
........
argsForZygote.add("--target-sdk-version="+targetSdkVersion);
........
if(niceName!=null){
//niceName一般為需要啟動的應用程式的包名
argsForZygote.add("--nice-name="+niceName);
}
argsForZygote.add(processClass);
return zygoteSendArgsAndGetResult(argForZygote);
}
}
完成引數處理後,就需要使用一個socket連線來與孵化程序通訊,這通過zygoteSendArgAndGetResult()方法來完成,該方法的程式碼如下所示:
private static ProcessStartResult
zygoteSendArgsAndGetResult(ArrayList<String> args)
throws ZygoteStartFailedEx{
openZygoteSocketIfNeeded();//開啟與孵化程序連線的socket
.......
int sz=arg.size();
for(int i=0;i<sz;i++){
Stirng arg=args.get(i);
........
sZygoteWriter.write(arg);
sZygoteWriter,newLine();
}
sZygoteWriter.flush();
........
return result;
}
這裡ZygoteConnection將會檢測到這些輸入,它的runOnce()方法將會讀取這些引數並生成一個應用程式程序,然後呼叫android.app.ActivityThread類的main()方法完成這些任務,相關程式碼如下:
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller{
........
try{
args=readArgumentList();
}catch(IOException ex){
.........
}
.......
pid=Zygote.forkAndSpecialize(parsedArgs.uid,parsedArgs.gid,parsedArgs.gids,parsedArgs.debugFlags,rlimits);
.......
if(pid==0){
handleChildProc(parsedArgs,descriptors,childPipeFd,newStderr);
return true;
}
.......
}
在上述程式碼中,readArgumentList()方法用於從socket中讀取引數資訊並提供runOnce()方法處理。等到程序生成完畢以後,呼叫handleChildProc()方法回到指定類的main()方法:
private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors,FileDescriptor pipeFd,
PrintStream newStdrr)
throws ZygoteInit.MethodAndArgsCaller{
........
try{
ZygoteInit.invokeStaticMain(cloader,className,mainArgs);
}cathc(RuntimeException ex){
logAndPrintError(newStderr,"Error starting.",ex);
}
.....
}
在上面的程式碼中,最後呼叫的是ZygoteInit的invokeStaticMain()方法,這個方法負責呼叫ActivityThread的maiin()方法以完成剩餘的工作。
至此,孵化程序就建立了一個應用程式的程序。
4.第四階段——顯示應用程式並處理當前當前顯示的Activity的生命週期
在這個階段,流程回到了ActivityThread的main()方法中,其主要工作是顯示目標Activity並且調整Launcher應用程式中Activity的生命週期,將Launcher的生命週期置為paused,其核心程式碼如下:
public static void main(String[] args){
........
ActivityThread thread=new ActivityThread();
thread.attach(false);
.........
}
在上述程式碼中,attach()方法的輸入引數為false,表示當前不是系統處理,它將啟動另一個分支處理這個請求,具體程式碼如下所示:
private void attach(boolean system){
........
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>");
RuntimeInit.setApplicationObject(mAppThread.asBinder());
IActivityManager mgr=ActivityManagerNative.getDefault();
try{
mgr.attachApplication(mAppThread);
}catch(RemoteException ex){
......
}
.......
}
在這裡,我們呼叫ActivityManagerService的attachApplication()方法來實現Activity的排程工作。這時,ActivityThread的scheduleLaunchActivity()方法將被呼叫。scheduleLaunchActivity()方法的定義如下:
public final void scheduleLaunchActivity(......){
ActivityClientRecord r=new ActivityClientRecord();
.......
queueOrSendMessage(H.LAUNCH_ACTIVITY,r);
}
在上述程式碼中,queueOrSendMessage(H.LAUNCH_ACTIVITY,r)方法實際上傳送了一個LAUNCH_ACTIVITY訊息,這樣做將會啟動handleLaunchActivity()方法。當此方法執行完畢後,目標Activity將會顯示出來,而Launcher將會被隱藏到它的後面處於paused狀態。現在我們來看看handleLaunchActivity()方法的流程,如下圖:
在這裡,performLaunchActivity()方法負責例項化並建立需要顯示的Activity,並回調Activity的部分生命週期函式。performLaunchActivity()方法的部分程式碼如下所示:
private Activity performLaunchActivity(ActivityClientRecord r,
Intent customIntent){
.......
//建立Activity例項
java.lang.ClassLoader cl=r.packageInfo.getClassLoader();
activity=mInstrumentation.newActivity(cl,component.getClassName(),r.intent);
.......
//建立應用程式
Application app=r.packageInfo.makeApplication(false,mInstrumentation);
.......
//建立應用程式上下文
ContextImpl appContext=new ContextImpl();
appContext.init(r.packageInfo,r.token,this);
appContext.setOuterContext(activity);
//設定應用程式標題
CharSequence title=r.activityInfo.loadLabel(appContext.getPackageManager());
//設定應用程式的配置
Configuration config=new Configuration(mCompatConfiguration);
//附加到Activity
activity.attach(appContext,this,getInstrumentation(),r.token,
r.ident,app,r.intent,r.activityInfo,title,r.parent,
r.embeddedID,r.lastNonConfigurationInstances,config);
.........
//如果我們設定了android:theme屬性,那麼這裡需要設定該Activity的風格
if(theme!=0){
activity.setTheme(theme);
}
//呼叫Activity的onCreate()回撥介面
//一般來說,這裡應用程式會使用setContentView()方法設定介面的UI
mInstrumentation.callActivityOnCreate(activity,r.state);
........
//呼叫Activity的onStart()回撥介面
//這時Activity處於started狀態
activity.performStart();
........
//如果之前曾經儲存了Activity狀態
//這裡需要呼叫onRestoreInstanceState()方法恢復之前儲存的狀態
if(r.state!=null){
mInstarumentation.callActivityOnRestoreInstanceState(activity,r.state);
}
.......
//回撥Activity的onPostCraete()方法
mInstrumentation.callActivityOnPostCreate(activity,r.state);
........
return activity;
}
完成這個函式時,Activity處於生命週期的started狀態。
接下來,handleResumeActivity()方法將負責顯示這個Activity,具體程式碼如下:
final void handleResumeActivity(IBinder token,boolean clearHide,
boolean isForward){
ActivityClientRecord r=performResumeActivity(token,clearHide):
........
//顯示Activity檢視
if(r.window==null && !a.mFinished && willBeVisible){
r.window=r.activity.getWindow();
View decor=r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager vm=a.getWindowManager();
WindowManager.LayoutParams l=r.window.getAttributes();
a.mDecor=decor;
l.type=WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |=forwardBit;
if(a.mVisibleFromClient){
a.mWindowAdded=true;
wm.addView(decor,l);
}
}
........
}
在上述程式碼中,performResumeActivity()方法負責回撥Activity的一些生命週期回撥方法以完成介面顯示,具體程式碼如下:
public final ActivityClientRecord performResumeActivity(IBinder token,
boolean clearHide){
ActivityClientRecord r=mActivities.get(token);
.......
r.activity.performResume();
........
return r;
}
其中Activity的performResume()方法的程式碼如下:
final void performResume(){
........
mInstrumentation.callActivityOnResume(this);
.......
}
在Instrumentation的callActivityOnResume()方法中,將會回撥activity的onResume()方法,具體如下列程式碼所示:
public void callActivityOnResume(Activity activity){
activity.mResumed=true;
activity.onResume();
........
}
當完成了handleResumeActivity()方法後,Activity就處於可見生命週期中(Activity的resumed狀態)。
在完成這4個階段之後,我們就看到了目標Activity,原先的Activity被此Activity覆蓋。
要注意的是,當我們退出這個Activity的時候,孵化程序為此應用程式孵化出來的程序依然存在,除非Activity因為異常而被強行關閉或者使用其他手段(比如DDMS或者KILL等工具)強行殺掉程序。因此,當我們重新啟動這個應用程式的時候,就不會再次為此應用程式建立程序。相關程式碼如下所示:
private final void startSpecificActivityLocked(ActivityRecord r,
boolean andResume,boolean checkConfig){
ProcessRecord app=mService.getProcessRecordLocked(r.processName,
r.info.application.uid);
........
if(app!=null && app.thread!=null){
try{
app.addPackage(r.info.packageName);
realStartActivityLocked(r,app,andResume,checkConfig);
return;
}cathc(RemoteException e){
Slog.w(TAG,"Exception when starting activity"
+r.intent.getComponent().flattenToShortString(),e);
}
}
mService.startProcessLocked(r.processName,r.info.applicationInfo,true,0,"activity",r.intent.getComponent(),false);
}
本博文中,我們用了一個簡單的例子描述了Android啟動Activity過程。此外,Android框架還為開發者提供了一系列控制應用程式,Activity行為(比如android:launchMode等)以及應用程式程序性質的標誌(比如android:debuggable等)。在使用這些標誌時,Android的ActivityManagerService將會按照不同的規則排程Activity。