Android應用程序內部啟動Activity過程(startActivity)的源代碼分析


   上文介紹了Android應用程序的啟動過程,即應用程序默認Activity的啟動過程,一般來說,這種默認Activity是在新的進程和任務中啟動的;本文將繼續分析在應用程序內部啟動非默認Activity的過程的源代碼,這種非默認Activity一般是在原來的進程和任務中啟動的。

《Android系統源代碼情景分析》一書正在進擊的程序員網(http://0xcc0xcd.com)中連載,點擊進入!

        這里,我們像上一篇文章Android應用程序啟動過程源代碼分析一樣,采用再上一篇文章Android應用程序的Activity啟動過程簡要介紹和學習計划所舉的例子來分析在應用程序內部啟動非默認Activity的過程。

        在應用程序內部啟動非默認Activity的過程與在應用程序啟動器Launcher中啟動另外一個應用程序的默認Activity的過程大體上一致的,因此,這里不會像上文Android應用程序啟動過程源代碼分析一樣詳細分析每一個步驟,我們着重關注有差別的地方。

        回憶一下Android應用程序的Activity啟動過程簡要介紹和學習計划一文所用的應用程序Activity,它包含兩個Activity,分別是MainActivity和SubActivity,前者是應用程序的默認Activity,后者是非默認Activity。MainActivity啟動起來,通過點擊它界面上的按鈕,便可以在應用程序內部啟動SubActivity。

        我們先來看一下應用程序的配置文件AndroidManifest.xml,看看這兩個Activity是如何配置的:

    <?xml version="1.0" encoding="utf-8"?>  
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
        package="shy.luo.activity"  
        android:versionCode="1"  
        android:versionName="1.0">  
        <application android:icon="@drawable/icon" android:label="@string/app_name">  
            <activity android:name=".MainActivity"  
                      android:label="@string/app_name">  
                <intent-filter>  
                    <action android:name="android.intent.action.MAIN" />  
                    <category android:name="android.intent.category.LAUNCHER" />  
                </intent-filter>  
            </activity>  
            <activity android:name=".SubActivity"  
                      android:label="@string/sub_activity">  
                <intent-filter>  
                    <action android:name="shy.luo.activity.subactivity"/>  
                    <category android:name="android.intent.category.DEFAULT"/>  
                </intent-filter>  
            </activity>  
        </application>  
    </manifest>  

        這里可以很清楚地看到,MainActivity被配置成了應用程序的默認Activity,而SubActivity可以通過名稱“shy.luo.activity.subactivity”隱式地啟動,我們來看一下src/shy/luo/activity/MainActivity.java文件的內容,可以清楚地看到SubActivity是如何隱式地啟動的:

    public class MainActivity extends Activity  implements OnClickListener {  
        ......  
      
        @Override  
        public void onClick(View v) {  
            if(v.equals(startButton)) {  
                Intent intent = new Intent("shy.luo.activity.subactivity");  
                startActivity(intent);  
            }  
        }  
    }  

       這里,首先創建一個名稱為“shy.luo.activity.subactivity”的Intent,然后以這個Intent為參數,通過調用startActivity函數來實現隱式地啟動SubActivity。

       有了這些背景知識后,我們就來看一下SubActivity啟動過程的序列圖:

點擊查看大圖

       與前面介紹的MainActivity啟動過程相比,這里少了中間創建新的進程的步驟;接下來,我們就詳細分析一下SubActivity與MainActivity啟動過程中有差別的地方,相同的地方請參考Android應用程序啟動過程源代碼分析一文。

       Step 1. Activity.startActivity

       這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 2大體一致,通過指定名稱“shy.luo.activity.subactivity”來告訴應用程序框架層,它要隱式地啟動SubActivity。所不同的是傳入的參數intent沒有Intent.FLAG_ACTIVITY_NEW_TASK標志,表示這個SubActivity和啟動它的MainActivity運行在同一個Task中。

       Step 2. Activity.startActivityForResult

       這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 3一致。

       Step 3. Instrumentation.execStartActivity

       這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 4一致。

       Step 4. ActivityManagerProxy.startActivity

       這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 5一致。

       Step 5. ActivityManagerService.startActivity

       這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 6一致。

       Step 6. ActivityStack.startActivityMayWait

       這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 7一致。

       Step 7. ActivityStack.startActivityLocked

       這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 8一致。

       Step 8. ActivityStack.startActivityUncheckedLocked

       這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 9有所不同,主要是當前要啟動的Activity與啟動它的Activity是在同一個Task中運行的,我們來詳細看一下。這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:

    public class ActivityStack {
     
        ......
     
        final int startActivityUncheckedLocked(ActivityRecord r,
               ActivityRecord sourceRecord, Uri[] grantedUriPermissions,
               int grantedMode, boolean onlyIfNeeded, boolean doResume) {
            final Intent intent = r.intent;
            final int callingUid = r.launchedFromUid;
     
            int launchFlags = intent.getFlags();
     
            ......
     
            if (sourceRecord == null) {
               ......
            } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
               ......
            } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
               ......
            }
     
            if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
               ......
            }
     
            boolean addingToTask = false;
            if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
               (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
               || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
               || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
                ......
            }
     
            if (r.packageName != null) {
               // If the activity being launched is the same as the one currently
               // at the top, then we need to check if it should only be launched
               // once.
               ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
               if (top != null && r.resultTo == null) {
                   if (top.realActivity.equals(r.realActivity)) {
                       ......
                   }
               }
     
            } else {
               ......
            }
     
            boolean newTask = false;
     
            // Should this be considered a new task?
            if (r.resultTo == null && !addingToTask
               && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
                ......
     
            } else if (sourceRecord != null) {
                ......
                // An existing activity is starting this new activity, so we want
                // to keep the new one in the same task as the one that is starting
                // it.
                r.task = sourceRecord.task;
                ......
     
            } else {
               ......
            }
     
            ......
     
            startActivityLocked(r, newTask, doResume);
            return START_SUCCESS;
        }
     
        ......
     
    }

        這里,參數intent的標志位Intent.FLAG_ACTIVITY_NEW_TASK沒有設置,在配置文件AndriodManifest.xml中,SubActivity也沒有配置啟動模式launchMode,於是它就默認標准模式,即ActivityInfo.LAUNCH_MULTIPLE,因此,下面if語句不會執行:

        if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
            (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
        || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
        ......
        }

        於是,變量addingToTask為false。

         繼續往下看:

        if (r.packageName != null) {
        // If the activity being launched is the same as the one currently
        // at the top, then we need to check if it should only be launched
        // once.
            ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
        if (top != null && r.resultTo == null) {
             if (top.realActivity.equals(r.realActivity)) {
            ......
             }
        }
     
        }

        這里看一下當前要啟動的Activity是否就是當前堆棧頂端的Activity,如果是的話,在某些情況下,就不用再重新啟動了。函數topRunningNonDelayedActivityLocked返回當前
堆棧頂端的Activity,這里即為MainActivity,而當前要啟動的Activity為SubActivity,因此,這二者不相等,於是跳過里面的if語句。

        接着往下執行:

        // Should this be considered a new task?
        if (r.resultTo == null && !addingToTask
        && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
        ......
     
        } else if (sourceRecord != null) {
        ......
        // An existing activity is starting this new activity, so we want
        // to keep the new one in the same task as the one that is starting
        // it.
        r.task = sourceRecord.task;
        ......
     
        } else {
        ......
        }

        前面說過參數intent的標志位Intent.FLAG_ACTIVITY_NEW_TASK沒有設置,而這里的sourceRecord即為當前執行啟動Activity操作的Activity,這里即為MainActivity,因此,它不為null,於是於MainActivity所屬的Task設置到r.task中去,這里的r即為SubActivity。看到這里,我們就知道SubActivity要和MainActivity運行在同一個Task中了,同時,變量newTask的值為false。

        最后,函數進 入startActivityLocked(r, newTask, doResume)進一步處理了。這個函數同樣是定義在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:

    public class ActivityStack {
     
        ......
     
        private final void startActivityLocked(ActivityRecord r, boolean newTask,
                boolean doResume) {
            final int NH = mHistory.size();
     
            int addPos = -1;
     
            if (!newTask) {
                // If starting in an existing task, find where that is...
                boolean startIt = true;
                for (int i = NH-1; i >= 0; i--) {
                    ActivityRecord p = (ActivityRecord)mHistory.get(i);
                    if (p.finishing) {
                        continue;
                    }
                    if (p.task == r.task) {
                        // Here it is!  Now, if this is not yet visible to the
                        // user, then just add it without starting; it will
                        // get started when the user navigates back to it.
                        addPos = i+1;
                        if (!startIt) {
                            mHistory.add(addPos, r);
                            r.inHistory = true;
                            r.task.numActivities++;
                            mService.mWindowManager.addAppToken(addPos, r, r.task.taskId,
                                r.info.screenOrientation, r.fullscreen);
                            if (VALIDATE_TOKENS) {
                                mService.mWindowManager.validateAppTokens(mHistory);
                            }
                            return;
                        }
                        break;
                    }
                    if (p.fullscreen) {
                        startIt = false;
                    }
                }
            }
     
            ......
     
            // Slot the activity into the history stack and proceed
            mHistory.add(addPos, r);
            r.inHistory = true;
            r.frontOfTask = newTask;
            r.task.numActivities++;
     
            ......
     
            if (doResume) {
                resumeTopActivityLocked(null);
            }
        }
     
        ......
     
    }

        這里傳進來的參數newTask為false,doResume為true。當newTask為false,表示即將要啟動的Activity是在原有的Task運行時,如果這個原有的Task當前對用戶不可見時,這時候就不需要繼續執行下去了,因為即使把這個Activity啟動起來,用戶也看不到,還不如先把它保存起來,等到下次這個Task對用戶可見的時候,再啟動不遲。這里,這個原有的Task,即運行MainActivity的Task當前對用戶是可見的,因此,會繼續往下執行。

        接下去執行就會把這個SubActivity通過mHistroy.add(addPos, r)添加到堆棧頂端去,然后調用resumeTopActivityLocked進一步操作。

        Step 9. ActivityStack.resumeTopActivityLocked

        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 10一致。

        但是要注意的是,執行到這個函數的時候,當前處於堆棧頂端的Activity為SubActivity,ActivityStack的成員變量mResumedActivity指向MainActivity。
        Step 10. ActivityStack.startPausingLocked
        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 11一致。

        從這里開始,ActivityManagerService通知MainActivity進入Paused狀態。

        Step 11. ApplicationThreadProxy.schedulePauseActivity

        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 12一致。

        Step 12. ApplicationThread.schedulePauseActivity

        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 13一致。

        Step 13. ActivityThread.queueOrSendMessage
        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 14一致。

        Step 14. H.handleMessage

        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 15一致。

        Step 15. ActivityThread.handlePauseActivity

        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 16一致。

        Step 16. ActivityManagerProxy.activityPaused

        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 17一致。

        Step 17. ActivityManagerService.activityPaused

        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 18一致。

        Step 18. ActivityStack.activityPaused

        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 19一致。

        Step 19. ActivityStack.completePauseLocked

        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 20一致。

        執行到這里的時候,MainActivity就進入Paused狀態了,下面就開始要啟動SubActivity了。

        Step 20. ActivityStack.resumeTopActivityLokced

        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 21一致。

        Step 21. ActivityStack.startSpecificActivityLocked

        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 22就有所不同了,這里,它不會調用mService.startProcessLocked來創建一個新的進程來啟動新的Activity,我們來看一下這個函數的實現,這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:

    public class ActivityStack {
     
        ......
     
        private final void startSpecificActivityLocked(ActivityRecord r,  
                boolean andResume, boolean checkConfig) {  
            // Is this activity's application already running?  
            ProcessRecord app = mService.getProcessRecordLocked(r.processName,  
                    r.info.applicationInfo.uid);  
     
            ......  
     
            if (app != null && app.thread != null) {  
                try {  
                    realStartActivityLocked(r, app, andResume, checkConfig);  
                    return;  
                } catch (RemoteException e) {  
                    ......  
                }  
            }  
     
            ......
     
        }  
     
        ......
     
    }

        這里由於不是第一次啟動應用程序的Activity(MainActivity是這個應用程序第一個啟動的Activity),所以下面語句:

        ProcessRecord app = mService.getProcessRecordLocked(r.processName,  
        r.info.applicationInfo.uid);  

        取回來的app不為null。在上一篇文章Android應用程序啟動過程源代碼分析中,我們介紹過,在Activity應用程序中的AndroidManifest.xml配置文件中,我們沒有指定application標簽的process屬性,於是系統就會默認使用package的名稱,這里就是"shy.luo.activity"了。每一個應用程序都有自己的uid,因此,這里uid + process的組合就可以創建一個全局唯一的ProcessRecord。這個ProcessRecord是在前面啟動MainActivity時創建的,因此,這里將它取回來,並保存在變量app中。注意,我們也可以在AndroidManifest.xml配置文件中指定SubActivity的process屬性值,這樣SubActivity就可以在另外一個進程中啟動,不過很少有應用程序會這樣做,我們不考慮這種情況。

        這個app的thread也是在前面啟動MainActivity時創建好的,於是,這里就直接調用realStartActivityLocked函數來啟動新的Activity了,新的Activity的相關信息都保存在參數r中了。

        Step 22. ActivityStack.realStartActivityLocked

        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 28一致。
        Step 23. ApplicationThreadProxy.scheduleLaunchActivity

        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 29一致。

        Step 24. ApplicationThread.scheduleLaunchActivity

        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 30一致。

        Step 25. ActivityThread.queueOrSendMessage

        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 31一致。

        Step 26. H.handleMessage

        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 32一致。

        Step 27. ActivityThread.handleLaunchActivity

        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 33一致。

        Step 28. ActivityThread.performLaunchActivity

        這一步與上一篇文章Android應用程序啟動過程源代碼分析的Step 34一致,不過,這里要從ClassLoader里面加載的類就是shy.luo.activity.SubActivity了。

        Step 29. SubAcitiviy.onCreate

        這個函數定義在packages/experimental/Activity/src/shy/luo/activity/SubActivity.java文件中,這是我們自定義的app工程文件:

    public class SubActivity extends Activity implements OnClickListener {
     
        ......
     
        @Override
        public void onCreate(Bundle savedInstanceState) {
            ......
     
            Log.i(LOG_TAG, "Sub Activity Created.");
        }
     
        ......
     
    }

       這樣,SubActivity就在應用程序Activity內部啟動起來了。
       在應用程序內部啟動新的Activity的過程要執行很多步驟,但是整體來看,主要分為以下四個階段:

       一. Step 1 - Step 10:應用程序的MainActivity通過Binder進程間通信機制通知ActivityManagerService,它要啟動一個新的Activity;
       二. Step 11 - Step 15:ActivityManagerService通過Binder進程間通信機制通知MainActivity進入Paused狀態;
       三. Step 16 - Step 22:MainActivity通過Binder進程間通信機制通知ActivityManagerService,它已經准備就緒進入Paused狀態,於是ActivityManagerService就准備要在MainActivity所在的進程和任務中啟動新的Activity了;
       四. Step 23 - Step 29:ActivityManagerService通過Binder進程間通信機制通知MainActivity所在的ActivityThread,現在一切准備就緒,它可以真正執行Activity的啟動操作了。

       和上一篇文章Android應用程序啟動過程源代碼分析中啟動應用程序的默認Activity相比,這里在應用程序內部啟動新的Activity的過程少了中間創建新的進程這一步,這是因為新的Activity是在已有的進程和任務中執行的,無須創建新的進程和任務。

      這里同樣不少地方涉及到了Binder進程間通信機制,相關資料請參考Android進程間通信(IPC)機制Binder簡要介紹和學習計划一文。

      這里希望讀者能夠把本文和上文Android應用程序啟動過程源代碼分析仔細比較一下應用程序的默認Activity和非默認Activity啟動過程的不同之處,以加深對Activity的理解。

      最后,在本文和上文中,我們多次提到了Android應用程序中任務(Task)的概念,它既不是我們在Linux系統中所理解的進程(Process),也不是線程(Thread),它是用戶為了完成某個目標而需要執行的一系列操作的過程的一種抽象。這是一個非常重要的概念,它從用戶體驗的角度出發,界定了應用程序的邊界,極大地方便了開發者利用現成的組件(Activity)來搭建自己的應用程序,就像搭積木一樣,而且,它還為應用程序屏蔽了底層的進程,即一個任務中的Activity可以都是運行在同一個進程中,也中可以運行在不同的進程中。Android應用程序中的任務的概念,具體可以參考官方文檔http://developer.android.com/guide/topics/fundamentals/tasks-and-back-stack.html,上面已經介紹的非常清楚了。
---------------------
作者:羅升陽
來源:CSDN
原文:https://blog.csdn.net/Luoshengyang/article/details/6703247
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM