<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                原文出處——>[解開Android應用程序組件Activity的"singleTask"之謎](http://blog.csdn.net/luoshengyang/article/details/6714543) 在Android應用程序中,可以配置Activity以四種方式來啟動,其中最令人迷惑的就是"singleTask"這種方式了,官方文檔稱以這種方式啟動的Activity總是屬于一個任務的根Activity。果真如此嗎?本文將為你解開Activity的"singleTask"之謎。 在解開這個謎之前,我們先來簡單了解一下在Android應用程序中,任務(Task)是個什么樣的概念。我們知道,Activity是Android應用程序的基礎組件之一,在應用程序運行時,每一個Activity代表一個用戶操作。用戶為了完成某個功能而執行的一系列操作就形成了一個Activity序列,這個序列在Android應用程序中就稱之為任務,它是從用戶體驗的角度出發,把一組相關的Activity組織在一起而抽象出來的概念。 對初學者來說,在開發Android應用程序時,對任務的概念可能不是那么的直觀,一般我們只關注如何實現應用程序中的每一個Activity。事實上,Android系統中的任務更多的是體現是應用程序運行的時候,因此,它相對于Activity來說是動態存在的,這就是為什么我們在開發時對任務這個概念不是那么直觀的原因。不過,我們在開發Android應用程序時,還是可以配置Activity的任務屬性的,即告訴系統,它是要在新的任務中啟動呢,還是在已有的任務中啟動,亦或是其它的Activity能不能與它共享同一個任務,具體配置請參考官方文檔:http://developer.android.com/guide/topics/fundamentals/tasks-and-back-stack.html 它是這樣介紹以"singleTask"方式啟動的Activity的: The system creates a new task and instantiates the activity at the root of the new task. However, if an instance of the activity already exists in a separate task, the system routes the intent to the existing instance through a call to its onNewIntent() method, rather than creating a new instance. Only one instance of the activity can exist at a time. 它明確說明,以"singleTask"方式啟動的Activity,全局只有唯一個實例存在,因此,當我們第一次啟動這個Activity時,系統便會創建一個新的任務,并且初始化一個這樣的Activity的實例,放在新任務的底部,如果下次再啟動這個Activity時,系統發現已經存在這樣的Activity實例,就會調用這個Activity實例的onNewIntent成員函數,從而把它激活起來。從這句話就可以推斷出,以"singleTask"方式啟動的Activity總是屬于一個任務的根Activity。 但是文檔接著舉例子說明,當用戶按下鍵盤上的Back鍵時,如果此時在前臺中運行的任務堆棧頂端是一個"singleTask"的Activity,系統會回到當前任務的下一個Activity中去,而不是回到前一個Activity中去,如下圖所示: ![](http://hi.csdn.net/attachment/201108/23/0_13141106978dkE.gif) 真是坑爹啊!有木有!前面剛說"singleTask"會在新的任務中運行,并且位于任務堆棧的底部,這里在Task B中,一個赤裸裸的帶著"singleTask"標簽的箭頭無情地指向Task B堆棧頂端的Activity Y,剛轉身就翻臉不認人了呢! 事實勝于雄辯,我們來做一個實驗吧,看看到底在啟動這個"singleTask"的Activity的時候,它是位于新任務堆棧的底部呢,還是在已有任務的頂部。 首先在Android源代碼工程中創建一個Android應用程序工程,名字就稱為Task吧。關于如何獲得Android源代碼工程,請參考在Ubuntu上下載、編譯和安裝Android最新源代碼一文;關于如何在Android源代碼工程中創建應用程序工程,請參考在Ubuntu上為Android系統內置Java應用程序測試Application Frameworks層的硬件服務一文。這個應用程序工程定義了一個名為shy.luo.task的package,這個例子的源代碼主要就是實現在這里了。下面,將會逐一介紹這個package里面的文件。 應用程序的默認Activity定義在src/shy/luo/task/MainActivity.java文件中: ~~~ package shy.luo.task; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity implements OnClickListener { private final static String LOG_TAG = "shy.luo.task.MainActivity"; private Button startButton = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); startButton = (Button)findViewById(R.id.button_start); startButton.setOnClickListener(this); Log.i(LOG_TAG, "Main Activity Created."); } @Override public void onClick(View v) { if(v.equals(startButton)) { Intent intent = new Intent("shy.luo.task.subactivity"); startActivity(intent); } } } ~~~ 它的實現很簡單,當點擊它上面的一個按鈕的時候,就會啟動另外一個名字為“shy.luo.task.subactivity”的Actvity。 名字為“shy.luo.task.subactivity”的Actvity實現在src/shy/luo/task/SubActivity.java文件中: ~~~ package shy.luo.task; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class SubActivity extends Activity implements OnClickListener { private final static String LOG_TAG = "shy.luo.task.SubActivity"; private Button finishButton = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.sub); finishButton = (Button)findViewById(R.id.button_finish); finishButton.setOnClickListener(this); Log.i(LOG_TAG, "Sub Activity Created."); } @Override public void onClick(View v) { if(v.equals(finishButton)) { finish(); } } } ~~~ 它的實現也很簡單,當點擊上面的一個銨鈕的時候,就結束自己,回到前面一個Activity中去。 再來看一下應用程序的配置文件AndroidManifest.xml: ~~~ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="shy.luo.task" 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" android:launchMode="singleTask"> <intent-filter> <action android:name="shy.luo.task.subactivity"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> </application> </manifest> ~~~ 注意,這里的SubActivity的launchMode屬性配置為"singleTask"。 再來看界面配置文件,它們定義在res/layout目錄中,main.xml文件對應MainActivity的界面: ~~~ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center"> <Button android:id="@+id/button_start" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:text="@string/start" > </Button> </LinearLayout> ~~~ 而sub.xml對應SubActivity的界面: ~~~ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center"> <Button android:id="@+id/button_finish" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:text="@string/finish" > </Button> </LinearLayout> ~~~ 字符串文件位于res/values/strings.xml文件中: ~~~ <?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Task</string> <string name="sub_activity">Sub Activity</string> <string name="start">Start singleTask activity</string> <string name="finish">Finish activity</string> </resources> ~~~ 最后,我們還要在工程目錄下放置一個編譯腳本文件Android.mk: ~~~ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_PACKAGE_NAME := Task include $(BUILD_PACKAGE) ~~~ 這樣,原材料就準備好了,接下來就要編譯了。有關如何單獨編譯Android源代碼工程的模塊,以及如何打包system.img,請參考如何單獨編譯Android源代碼中的模塊一文。 執行以下命令進行編譯和打包: ~~~ USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/Task USER-NAME@MACHINE-NAME:~/Android$ make snod ~~~ 這樣,打包好的Android系統鏡像文件system.img就包含我們前面創建的Task應用程序了。 再接下來,就是運行模擬器來運行我們的例子了。關于如何在Android源代碼工程中運行模擬器,請參考在Ubuntu上下載、編譯和安裝Android最新源代碼一文。 執行以下命令啟動模擬器: ~~~ USER-NAME@MACHINE-NAME:~/Android$ emulator ~~~ 模擬器啟動起,就可以App Launcher中找到Task應用程序圖標,接著把它啟動起來: ![](http://hi.csdn.net/attachment/201108/23/0_1314113476bX47.gif) 點擊中間的按鈕,就會以"singleTask"的方式來啟動SubActivity: ![](http://hi.csdn.net/attachment/201108/23/0_1314113546u5P1.gif) 現在,我們如何來確認SubActivity是不是在新的任務中啟動并且位于這個新任務的堆棧底部呢?Android源代碼工程為我們準備了adb工具,可以查看模擬器上系統運行的狀況,執行下面的命令查看; ~~~ USER-NAME@MACHINE-NAME:~/Android$ adb shell dumpsys activity ~~~ 這個命令輸出的內容比較多,這里我們只關心TaskRecord部分: ~~~ Running activities (most recent first): TaskRecord{4070d8f8 #3 A shy.luo.task} Run #2: HistoryRecord{406a13f8 shy.luo.task/.SubActivity} Run #1: HistoryRecord{406a0e00 shy.luo.task/.MainActivity} TaskRecord{4067a510 #2 A com.android.launcher} Run #0: HistoryRecord{40677518 com.android.launcher/com.android.launcher2.Launcher} ~~~ 果然,SubActivity和MainActivity都是運行在TaskRecord#3中,并且SubActivity在MainActivity的上面。這是怎么回事呢?碰到這種情況,Linus Torvalds告誡我們:Read the fucking source code;去年張麻子又說:槍在手,跟我走;我們沒有槍,但是有source code,因此,我要說:跟著代碼走。 前面我們在兩篇文章Android應用程序啟動過程源代碼分析和Android應用程序內部啟動Activity過程(startActivity)的源代碼分析時,分別在Step 9和Step 8中分析了Activity在啟動過程中與任務相關的函數ActivityStack.startActivityUncheckedLocked函數中,它定義在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(); ...... ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null; ...... if (sourceRecord == null) { ...... } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { ...... } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) { // The activity being started is a single instance... it always // gets launched into its own task. launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK; } ...... 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 bring to front is requested, and no result is requested, and // we can find a task that was started with this same // component, then instead of launching bring that one to the front. if (r.resultTo == null) { // See if there is a task to bring to the front. If this is // a SINGLE_INSTANCE activity, there can be one and only one // instance of it in the history, and it is always in its own // unique task, so we do a special search. ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE ? findTaskLocked(intent, r.info) : findActivityLocked(intent, r.info); if (taskTop != null) { ...... if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0 || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { // In this situation we want to remove all activities // from the task up to the one being started. In most // cases this means we are resetting the task to its // initial state. ActivityRecord top = performClearTaskLocked( taskTop.task.taskId, r, launchFlags, true); if (top != null) { ...... } else { // A special case: we need to // start the activity because it is not currently // running, and the caller has asked to clear the // current task to have this activity at the top. addingToTask = true; // Now pretend like this activity is being started // by the top of its task, so it is put in the // right place. sourceRecord = taskTop; } } else if (r.realActivity.equals(taskTop.task.realActivity)) { ...... } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) { ...... } else if (!taskTop.task.rootWasReset) { ...... } ...... } } } ...... 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)) { if (top.app != null && top.app.thread != null) { ...... } } } } else { ...... } boolean newTask = false; // Should this be considered a new task? if (r.resultTo == null && !addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { // todo: should do better management of integers. mService.mCurTask++; if (mService.mCurTask <= 0) { mService.mCurTask = 1; } r.task = new TaskRecord(mService.mCurTask, r.info, intent, (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0); if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " + r.task); newTask = true; if (mMainStack) { mService.addRecentTaskLocked(r.task); } } else if (sourceRecord != null) { if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) { ...... } else if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) { ...... } // 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; } ...... } ~~~ 首先是獲得用來啟動Activity的Intent的Flags,并且保存在launchFlags變量中,這里,launcFlags的Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP位沒有置位,因此,notTop為null。 接下來的這個if語句: ~~~ if (sourceRecord == null) { ...... } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { ...... } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) { // The activity being started is a single instance... it always // gets launched into its own task. launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK; } ~~~ 這里變量r的類型為ActivityRecord,它表示即將在啟動的Activity,在這個例子中,即為SubActivity,因此,這里的r.launchMode等于ActivityInfo.LAUNCH_SINGLE_TASK,于是,無條件將launchFlags的Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP位置為1,表示這個SubActivity要在新的任務中啟動,但是別急,還要看看其它條件是否滿足,如果條件都滿足,才可以在新的任務中啟動這個SubActivity。 接下將addingToTask變量初始化為false,這個變量也將決定是否要將SubActivity在新的任務中啟動,從名字我們就可以看出,默認不增加到原有的任務中啟動,即要在新的任務中啟動。這里的r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK條成立,條件r.resultTo == null也成立,它表這個Activity不需要將結果返回給啟動它的Activity。于是會進入接下來的if語句中,執行: ~~~ ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE ? findTaskLocked(intent, r.info) : findActivityLocked(intent, r.info) ~~~ 這里的條件r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE成立,于是執行findTaskLocked函數,這個函數也是定義在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中: ~~~ public class ActivityStack { ...... /** * Returns the top activity in any existing task matching the given * Intent. Returns null if no such task is found. */ private ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) { ComponentName cls = intent.getComponent(); if (info.targetActivity != null) { cls = new ComponentName(info.packageName, info.targetActivity); } TaskRecord cp = null; final int N = mHistory.size(); for (int i=(N-1); i>=0; i--) { ActivityRecord r = (ActivityRecord)mHistory.get(i); if (!r.finishing && r.task != cp && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) { cp = r.task; //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString() // + "/aff=" + r.task.affinity + " to new cls=" // + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity); if (r.task.affinity != null) { if (r.task.affinity.equals(info.taskAffinity)) { //Slog.i(TAG, "Found matching affinity!"); return r; } } else if (r.task.intent != null && r.task.intent.getComponent().equals(cls)) { //Slog.i(TAG, "Found matching class!"); //dump(); //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); return r; } else if (r.task.affinityIntent != null && r.task.affinityIntent.getComponent().equals(cls)) { //Slog.i(TAG, "Found matching class!"); //dump(); //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); return r; } } } return null; } ...... } ~~~ 這個函數無非就是根據即將要啟動的SubActivity的taskAffinity屬性值在系統中查找這樣的一個Task:Task的affinity屬性值與即將要啟動的Activity的taskAffinity屬性值一致。如果存在,就返回這個Task堆棧頂端的Activity回去。在上面的AndroidManifest.xml文件中,沒有配置MainActivity和SubActivity的taskAffinity屬性,于是它們的taskAffinity屬性值就默認為父標簽application的taskAffinity屬性值,這里,標簽application的taskAffinity也沒有配置,于是它們就默認為包名,即"shy.luo.task"。由于在啟動SubActivity之前,MainActivity已經啟動,MainActivity啟動的時候,會在一個新的任務里面啟動,而這個新的任務的affinity屬性就等于它的第一個Activity的taskAffinity屬性值。于是,這個函數會動回表示MainActivity的ActivityRecord回去. 回到前面的startActivityUncheckedLocked函數中,這里的taskTop就表示MainActivity,它不為null,于是繼續往前執行。由于條件r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK成立,于是執行下面語句: ~~~ ActivityRecord top = performClearTaskLocked( kTop.task.taskId, r, launchFlags, true); ~~~ 函數performClearTaskLocked也是定義在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中: ~~~ public class ActivityStack { ...... /** * Perform clear operation as requested by * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the * stack to the given task, then look for * an instance of that activity in the stack and, if found, finish all * activities on top of it and return the instance. * * @param newR Description of the new activity being started. * @return Returns the old activity that should be continue to be used, * or null if none was found. */ private final ActivityRecord performClearTaskLocked(int taskId, ActivityRecord newR, int launchFlags, boolean doClear) { int i = mHistory.size(); // First find the requested task. while (i > 0) { i--; ActivityRecord r = (ActivityRecord)mHistory.get(i); if (r.task.taskId == taskId) { i++; break; } } // Now clear it. while (i > 0) { i--; ActivityRecord r = (ActivityRecord)mHistory.get(i); if (r.finishing) { continue; } if (r.task.taskId != taskId) { return null; } if (r.realActivity.equals(newR.realActivity)) { // Here it is! Now finish everything in front... ActivityRecord ret = r; if (doClear) { while (i < (mHistory.size()-1)) { i++; r = (ActivityRecord)mHistory.get(i); if (r.finishing) { continue; } if (finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "clear")) { i--; } } } // Finally, if this is a normal launch mode (that is, not // expecting onNewIntent()), then we will finish the current // instance of the activity so a new fresh one can be started. if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE && (launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) { if (!ret.finishing) { int index = indexOfTokenLocked(ret); if (index >= 0) { finishActivityLocked(ret, index, Activity.RESULT_CANCELED, null, "clear"); } return null; } } return ret; } } return null; } ...... } ~~~ 這個函數中作用無非就是找到ID等于參數taskId的任務,然后在這個任務中查找是否已經存在即將要啟動的Activity的實例,如果存在,就會把這個Actvity實例上面直到任務堆棧頂端的Activity通過調用finishActivityLocked函數將它們結束掉。在這個例子中,就是要在屬性值affinity等于"shy.luo.task"的任務中看看是否存在SubActivity類型的實例,如果有,就把它上面的Activity都結束掉。這里,屬性值affinity等于"shy.luo.task"的任務只有一個MainActivity,而且它不是SubActivity的實例,所以這個函數就返回null了。 回到前面的startActivityUncheckedLocked函數中,這里的變量top就為null了,于是執行下面的else語句: ~~~ if (top != null) { ...... } else { // A special case: we need to // start the activity because it is not currently // running, and the caller has asked to clear the // current task to have this activity at the top. addingToTask = true; // Now pretend like this activity is being started // by the top of its task, so it is put in the // right place. sourceRecord = taskTop; } ~~~ 于是,變量addingToTask值就為true了,同時將變量sourceRecord的值設置為taskTop,即前面調用findTaskLocked函數的返回值,這里,它就是表示MainActivity了。 繼續往下看,下面這個if語句: ~~~ 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)) { if (top.app != null && top.app.thread != null) { ...... } } } } else { ...... } ~~~ 它是例行性地檢查當前任務頂端的Activity,是否是即將啟動的Activity的實例,如果是否的話,在某些情況下,它什么也不做,就結束這個函數調用了。這里,當前任務頂端的Activity為MainActivity,它不是SubActivity實例,于是繼續往下執行: ~~~ 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) { if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) { ...... } else if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) { ...... } // 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 { ...... } ~~~ 這里首先將newTask變量初始化為false,表示不要在新的任務中啟動這個SubActivity。由于前面的已經把addingToTask設置為true,因此,這里會執行中間的else if語句,即這里會把r.task設置為sourceRecord.task,即把SubActivity放在MainActivity所在的任務中啟動。 最后,就是調用startActivityLocked函數繼續進行啟動Activity的操作了。后面的操作這里就不跟下去了,有興趣的讀者可以參考兩篇文章Android應用程序啟動過程源代碼分析和Android應用程序內部啟動Activity過程(startActivity)的源代碼分析。 到這里,思路就理清了,雖然SubActivity的launchMode被設置為"singleTask"模式,但是它并不像官方文檔描述的一樣:The system creates a new task and instantiates the activity at the root of the new task,而是在跟它有相同taskAffinity的任務中啟動,并且位于這個任務的堆棧頂端,于是,前面那個圖中,就會出現一個帶著"singleTask"標簽的箭頭指向一個任務堆棧頂端的Activity Y了。 那么,我們有沒有辦法讓一個"singleTask"的Activity在新的任務中啟動呢?答案是肯定的。從上面的代碼分析中,只要我們能夠進入函數startActivityUncheckedLocked的這個if語句中: ~~~ if (r.resultTo == null && !addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { // todo: should do better management of integers. mService.mCurTask++; if (mService.mCurTask <= 0) { mService.mCurTask = 1; } r.task = new TaskRecord(mService.mCurTask, r.info, intent, (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0); if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " + r.task); newTask = true; if (mMainStack) { mService.addRecentTaskLocked(r.task); } } ~~~ 那么,這個即將要啟動的Activity就會在新的任務中啟動了。進入這個if語句需要滿足三個條件,r.resultTo為null,launchFlags的Intent.FLAG_ACTIVITY_NEW_TASK位為1,并且addingToTask值為false。從上面的分析中可以看到,當即將要啟動的Activity的launchMode為"singleTask",并且調用startActivity時不要求返回要啟動的Activity的執行結果時,前面兩個條件可以滿足,要滿足第三個條件,只要當前系統不存在affinity屬性值等于即將要啟動的Activity的taskAffinity屬性值的任務就可以了。 我們可以稍微修改一下上面的AndroidManifest.xml配置文件來做一下這個實驗: ~~~ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="shy.luo.task" 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" android:taskAffinity="shy.luo.task.main.activity"> <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" android:launchMode="singleTask" android:taskAffinity="shy.luo.task.sub.activity"> <intent-filter> <action android:name="shy.luo.task.subactivity"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> </application> </manifest> ~~~ 注意,這里我們設置MainActivity的taskAffinity屬性值為"shy.luo.task.main.activity",設置SubActivity的taskAffinity屬性值為"shy.luo.task.sub.activity"。重新編譯一下程序,在模擬器上把這個應用程序再次跑起來,用“adb shell dumpsys activity”命令再來查看一下系統運行的的任務,就會看到: ~~~ Running activities (most recent first): TaskRecord{4069c020 #4 A shy.luo.task.sub.activity} Run #2: HistoryRecord{40725040 shy.luo.task/.SubActivity} TaskRecord{40695220 #3 A shy.luo.task.main.activity} Run #1: HistoryRecord{406b26b8 shy.luo.task/.MainActivity} TaskRecord{40599c90 #2 A com.android.launcher} Run #0: HistoryRecord{40646628 com.android.launcher/com.android.launcher2.Launcher} ~~~ 這里就可以看到,SubActivity和MainActivity就分別運行在不同的任務中了。 至此,我們總結一下,設置了"singleTask"啟動模式的Activity的特點: 1. 設置了"singleTask"啟動模式的Activity,它在啟動的時候,會先在系統中查找屬性值affinity等于它的屬性值taskAffinity的任務存在;如果存在這樣的任務,它就會在這個任務中啟動,否則就會在新任務中啟動。因此,如果我們想要設置了"singleTask"啟動模式的Activity在新的任務中啟動,就要為它設置一個獨立的taskAffinity屬性值。 2. 如果設置了"singleTask"啟動模式的Activity不是在新的任務中啟動時,它會在已有的任務中查看是否已經存在相應的Activity實例,如果存在,就會把位于這個Activity實例上面的Activity全部結束掉,即最終這個Activity實例會位于任務的堆棧頂端中。 看來,要解開Activity的"singleTask"之謎,還是要自力更生啊,不過,如果我們仔細閱讀官方文檔,在http://developer.android.com/guide/topics/manifest/activity-element.html 中,有這樣的描述: As shown in the table above, standard is the default mode and is appropriate for most types of activities. SingleTop is also a common and useful launch mode for many types of activities. The other modes — singleTask and singleInstance —are not appropriate for most applications, since they result in an interaction model that is likely to be unfamiliar to users and is very different from most other applications. Regardless of the launch mode that you choose, make sure to test the usability of the activity during launch and when navigating back to it from other activities and tasks using the BACK key. 這樣看,官方文檔也沒有坑我們呢,它告誡我們:make sure to test the usability of the activity during launch。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看