在Android開發中,我們經常會遇到界面的跳轉和回退,在開發中與之聯系比較緊密的概念是Task(任務)和Back Stack(回退棧)。我們在本文中所要講的Activity的啟動模式會影響Task和Back Stack的狀態,進而影響用戶體驗。
??首先我們對幾個概念做以說明:
一.?Application,Task和Process
1.1Application
Appliction可以翻譯為“應用”或者"應用程序",Androdi是一個在應用層組件化程度很高的系統,android的基礎就是四大組件。任何一個Android Application基本上都是由一個個基礎的組件組成,當我們寫完了多個組件,并在manifest文件中注冊了這些組件之后,這些捆綁在一起的組件就成了一個處理特定需求的Application,并且以“.apk”作為后綴名存在于文件系統。Android平臺下默認的應用程序,如:Email,Calendar,Camera等都是一個個獨立的App。Application和組件的關系可以在mainfest文件中清晰的表示出來:
<?xml version="1.0" encoding="utf-8"?>
<manifest android:versionCode="1"
android:versionName="1"
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.myapp">
<application android:label="@string/app_name">
<activity android:name=".MyActivity" android:label="@string/app_nam">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".MyReceiver"/>
<provider android:name=".MyProvider"/>
<service android:name=".MyService"/>
</application>
</manifest>
由此可見,Application是由四大組件組成:Activity,Service,Content Provider和Broadcast Receiver,其中Activity是實現應用的主體。在安裝APP的時候,系統會讀取mainfest的信息,將所有組件解析出來,以便在運行的時候對組件進行實例化和調度。
????Activity是Applications的主要組成部分,我們呢可以將Application理解為一個抽象標簽,他將系統內的一部分Activityes聯系在一起,協同完成用戶的特定需求。安裝Application的過程也可以簡單的理解為將其所包含的一系列Acticities導入到當前系統中,如果系統中已經存在了相同的Activity,那么將會自動的將其關聯,而不會重復的安裝Activities,避免資源的浪費;同理卸載的過程也會檢查當前所關聯的Activies是否被其他Application標簽所關聯,如果僅僅是提供當前的Application使用,那么他將被徹底的刪除,相反不做任何操作。
????用戶與Application的交互行為大部分是通過GUI來完成,在Android平臺可以有兩種方式定義GUI,一種是在XML文件中靜態的設置GUI元素,另一種是在JAVA代碼中動態的設置。這兩種方式都是Activity作為驅動和響應用戶交互時間愛你的主體。當Application啟動之后,至少需要一個包含GUI信息的Activity實例被創建(manifest中帶 category android:name="android.intent.category.LAUNCHER"標簽的那個Activity)。
1.2Task與Back Stack
Task是程序在運行的過程中,只針對Activity的概念。說白了,Task是一組相互關聯的activity的集合,他是FrameWork層的一個概念,控制界面的跳轉和返回。這個Task存在于一個叫Back Stack的數據結構中,也就是說,FrameWork是以棧的形式管理用戶開啟的Activity。這個棧的基本行為是,當用戶在多個Activity之間跳轉的時候,執行壓棧操作,當用戶按返回鍵時,執行出棧操作。當一個Activity啟動了另外一個Activity的時候,新的Activity就會被放置到返回棧的棧頂并將獲得焦點。前一個Activity仍然保留在返回棧當中,但會處于停止狀態。當用戶按下Back鍵的時候,棧中最頂端的Activity會被移除掉,然后前一個Activity則會得重新回到最頂端的位置,重復下去,直到任務棧為空,系統就會回收這個任務棧。返回棧(Back Stack)中的Activity的順序永遠都不會發生改變,我們只能向棧頂添加Activity,或者將棧頂的Activity移除掉。因此,返回棧(Back Stack)是一個典型的后進先出(last in, first out)的數據結構。
Task是可以跨應用的,這正是Task存在的一個重要原因。有的Activity,雖然不再同一個app中,但是為了保持用戶操作的連貫性,可以把他們放在同一個Task中。比如我們在app的Activity A中執行一個添加圖片的操作,此時會調用系統相冊的Activity B,這兩個Activity是在不同的app中的,但是被系統放在同一個任務(Task)中,前面我們已經說過這個Task所在的Back Stack是一個先進先出的任務棧,其中的Activity順序不會改變,因此,此時我們執行完添加圖片的操作之后,點Back返回(一般在程序中是finish,不過道理是一樣的)時,Activty B銷毀,直接退回到我們app中的Activity A,這樣就保證了用戶體驗的連貫性。
????關于更多Task和Back Stack的知識點可以參考谷歌原文,https://developer.android.com/guide/components/tasks-and-back-stack.html或者郭霖大神的這篇譯文:http://blog.csdn.net/guolin_blog/article/details/41087993
1.3process
process一般翻譯成進程,進程是操作系統內核中的一個概念,表示直接受內核調度的執行單位。每個App在啟動之前必須先創建一個進程,該進程是由Zygote?fork出來的,該進程具有獨立的資源空間,用于承載App上運行的各種Activity/Service等組件。在默認情況下,一個應用程序中的所有組件都運行在同一個進程中。除非我們在manifest中用process屬性指定組件所運行的進程的名字:
<activity android:name=".MyActivity"
android:label="@string/app_nam"
android:process=":remote">
</activity>
1.4Android系統應用框架中的Activity,Task,process
在Android系統的應用框架中,ActivityManagerService(AMS)負責啟動Activity,在整個啟動的過程中,ActivityManagerService用于管理Activity的生命周期。這里我們可以先不管AMS是什么,后面會有詳細的介紹。
????AMS提供了一個ArrayList mHistory來管理所有的activity:
首先我們來了解一下幾個概念:
- [ ] ActivityStack
????Activity在AMS的棧管理,用來記錄已經啟動的Activity先后順序,狀態信息等。通過ActivityStack決定是否需要啟動新的進程。 - [ ] ActivityRecord
????ActivityStack的管理對象,每個Activity在AMS對應的一個ActiivtyRecord,來記錄Activity的狀態及其他管理信息。 - [ ] TaskRecord
????AMS抽象出來的一個“任務”的概念,是記錄ActivityRecord的棧,一個“Task”包含若干個ActivityRecord。AMS用TaskRecord確保Activity的q啟動和退出順序。 - [ ] ProcessRecord
????一個APK文件運行時會對應一個進程,ProcessRecord正是記錄一個進程相關的信息。
二.StartActivity流程
在Android系統中,應用程序是由Activity組成,因此,應用程序的啟動實際上是應用程序中默認的Activity啟動過程。啟動Activity有兩種情景:
????第一,在android屏幕上點擊應用程序圖標啟動默認的Activity(就是在manifest中設為Launcher的那個Activity)。這種啟動方式的特點就是會啟動一個新的進程來加載相應的Activity。
????第二,應用程序內部啟動非默認Activity的過程。這種非默認的Activity一般是在原來的進程和任務中啟動的。在Android的Activity管理機制中,當退出Actiivty的時候,在某些情況下并沒有立刻把Aactivity殺死,而是將其暫時保存起來,當第二次啟動他的時候,就不需要再創建該Activity的實例直接恢復即可(比如在應用中選一張相片,跳到相冊界面中后又返回應用中,那么在這個過程中應用本身的那個Actiivty并沒有被銷毀)。
2.1在新的進程中啟動Activity
1.Launcher是什么
在新的進程中啟動Actiivity,也就是由Launcher啟動Activity,當我們點擊桌面的圖標的時候,App就由Launcher啟動了。我們在Android的源碼中找到Launcher類(筆者用源碼版本的是Android6.0),源碼目錄:
packages/apps/Launcher2/src/com/android/launcher2/Launcher.java
public final class Launcher extends Activity
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
View.OnTouchListener {
其實從目錄文件中我們可以看出:他位于packages/apps這個目錄下,也就是說他本質上也是一個APP,我們看他的第一句,發現這個Launcher和普通的App一樣,也是繼承自Activity。根據開發經驗我們得知既然繼承自Activity那么應唉就有布局文件,我么用Android Studio的ctrl+F搜素setContentView方法,在源碼的390行真的找到了這個方法:setContentView(R.layout.launcher);
我們來看看這個布局文件(packages/apps/Launcher2/res/layout-land/launcher.xml):
2.Launcher.xml解析
<!-- Full screen view projects under the status bar and contains the background -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
android:id="@+id/launcher"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/workspace_bg">
<com.android.launcher2.DragLayer
android:id="@+id/drag_layer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<!-- The workspace contains 5 screens of cells -->
<!-- Workspace即存放手機圖標的桌面,默認系統是包含可翻轉5頁,其中launcher:defaultScreen="2"
這句代表默認有兩個菜單界面,一個存放所有已安裝的應用程序圖標,另一個存放小部件-->
<com.android.launcher2.Workspace
android:id="@+id/workspace"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="@dimen/workspace_left_padding"
android:paddingEnd="@dimen/workspace_right_padding"
android:paddingTop="@dimen/workspace_top_padding"
android:paddingBottom="@dimen/workspace_bottom_padding"
launcher:defaultScreen="2"
launcher:cellCountX="@integer/cell_count_x"
launcher:cellCountY="@integer/cell_count_y"
launcher:pageSpacing="@dimen/workspace_page_spacing"
launcher:scrollIndicatorPaddingLeft="@dimen/qsb_bar_height"
launcher:scrollIndicatorPaddingRight="@dimen/button_bar_height">
<!-- Workspace總共可翻轉5個頁面,一個 workspace_screen定義一個頁面布局-->
<include android:id="@+id/cell1" layout="@layout/workspace_screen" />
<include android:id="@+id/cell2" layout="@layout/workspace_screen" />
<include android:id="@+id/cell3" layout="@layout/workspace_screen" />
<include android:id="@+id/cell4" layout="@layout/workspace_screen" />
<include android:id="@+id/cell5" layout="@layout/workspace_screen" />
</com.android.launcher2.Workspace>
<!-- dock_divider為左桌面分隔線,將hotseat和Workspace分隔,注意筆者這里的源碼是衡屏時的源碼,
所以出現了左分隔線和右分隔線,如果是豎屏時的源碼(比如面三張說明圖)應當是上下分隔線-->
<include
android:id="@+id/qsb_divider"
layout="@layout/workspace_divider"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/qsb_bar_height"
android:layout_gravity="start" />
<!-- dock_divider為右喲桌面分隔線,將hotseat和Workspace分隔 -->
<include
android:id="@+id/dock_divider"
layout="@layout/workspace_divider"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/button_bar_height"
android:layout_gravity="end" />
<!-- 桌面分隔線的上指示器,Workspace翻頁的時候顯示 -->
<include
android:id="@+id/paged_view_indicator"
layout="@layout/scroll_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom" />
<!-- hotseat為桌面分隔線下的界面, 不隨Workspace翻頁操作而移動 -->
<include layout="@layout/hotseat"
android:id="@+id/hotseat"
android:layout_width="@dimen/button_bar_height_plus_padding"
android:layout_height="match_parent"
android:layout_gravity="end" />
<!-- qsb_bar布局包含桌面上的可搜索框 以及長按桌面上圖標時顯示刪除和應用信息的操作框-->
<include
android:id="@+id/qsb_bar"
layout="@layout/qsb_bar" />
<!-- The Workspace cling must appear under the AppsCustomizePagedView below to ensure
that it is still visible during the transition to AllApps and doesn't overlay on
top of that view. -->
<!-- 手機剛開機,或者對launcher應用清空數據第一次進入到workspace時彈出的操作介紹界面 -->
<include layout="@layout/workspace_cling"
android:id="@+id/workspace_cling"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<!-- 手機剛開機,或者對launcher應用清空數據,第一次打開將workspace上兩個以上的圖標拖到一起形成 的文件夾時彈出的操作界面-->
<include layout="@layout/folder_cling"
android:id="@+id/folder_cling"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<com.android.launcher2.DrawableStateProxyView
android:id="@+id/voice_button_proxy"
android:layout_width="@dimen/qsb_bar_height"
android:layout_height="@dimen/app_icon_size"
android:layout_gravity="top|start"
android:layout_marginTop="64dp"
android:clickable="true"
android:onClick="onClickVoiceButton"
android:importantForAccessibility="no"
launcher:sourceViewId="@+id/voice_button" />
<!-- 點擊hotseat中心圖標進入的界面,該界面顯示所有應用和小部件 -->
<include layout="@layout/apps_customize_pane"
android:id="@+id/apps_customize_pane"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
</com.android.launcher2.DragLayer>
</FrameLayout>
我們通過這幾張網上的圖片來說明Launcher的布局:
圖1:Launcher的桌面布局(從上到下為搜索框qsb_bar、Workspace、分割線dock_divider、hotseat)。
圖2:菜單界面(apps_customize_pane)之一的顯示所有已安裝的應用程序;
圖3:菜單界面(apps_customize_pane)之一的顯示所有已創建的widget(小部件)
注:菜單界面整體是一個TabHost,由兩個子Tab組成;一個就是顯示圖2界面的子Tab,另一個就是顯示圖3界面的子Tab
下面我們重點來看xml文件中的這段代碼:com.android.launcher2.Workspace:
public class Workspace extends SmoothPagedView
implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener {
public abstract class SmoothPagedView extends PagedView {
public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeListener {
該子視圖為自定義類Workspace,Workspace繼承自SmoothPagedView,SmoothPagedView繼承自PagedView,PagedView繼承自ViewGroup;所以Workspace的終極父類也是ViewGroup;即該子視圖為ViewGroup類型的自定義容器視圖,也就是用來存放APP圖標的桌面;
????在Workspace視圖中又包含了5個id分別為cell1、cell2、cell3、cell4、cell5的子視圖,也就是多個存放圖片的自頁面,類似于ViewPager中的Fragment。它們對應的布局均為workspace_screen,workspace_screen.xml文件的代碼如下:
<com.android.launcher2.CellLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="@dimen/cell_layout_left_padding"
android:paddingEnd="@dimen/cell_layout_right_padding"
android:paddingTop="@dimen/cell_layout_top_padding"
android:paddingBottom="@dimen/cell_layout_bottom_padding"
android:hapticFeedbackEnabled="false"
launcher:cellWidth="@dimen/workspace_cell_width"
launcher:cellHeight="@dimen/workspace_cell_height"
launcher:widthGap="@dimen/workspace_width_gap"
launcher:heightGap="@dimen/workspace_height_gap"
launcher:maxGap="@dimen/workspace_max_gap" />
可以看到,這里workspace_screen的布局文件就是一個cellLayout(上面有說過這一點),既然CellLayout可以用來從房圖標,那么我們可以猜想他是繼承子ViewGroup或者其子類的,實際上cellLayout確實繼承自ViewGroup,我們呢點進去cellLayout的源碼,由于我們在這里不會分析該類的源碼,所以我們先不貼出。我們要關注的是其中的元素:ShortcutAndWidgetContainer與BubbleTextView
????ShortcutAndWidgetContainer繼承自ViewGroup類,他是cellLayout中唯一的自View。其實看名字我們也能猜得出,他是一個用來放置快捷圖標和Widget小部件的View。
????而我們重點要看的就是BubbleTextView這個東西,我們繼續上源碼:
/**
* TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
* because we want to make the bubble taller than the text and TextView's clip is
* too aggressive.
*/
public class BubbleTextView extends TextView {
......
public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache) {
Bitmap b = info.getIcon(iconCache);
setCompoundDrawablesWithIntrinsicBounds(null,
new FastBitmapDrawable(b),
null, null);
setText(info.title);
if (info.contentDescription != null) {
setContentDescription(info.contentDescription);
}
setTag(info);
}
......
我們首先可以看到他是繼承了TextView的一個類,也就是說他是一個強化版的TextView,而他有TextView的基本屬性——添加文本。我們再看他的注釋:“TextView是在文本后面繪制一個氣泡。我們不能使用LineBackgroundSpan,因為我們想讓氣泡比文本更高,TextView的剪輯太激進了。”也就是說,TextView=文本+氣泡View,這個氣泡View是在文本之后的,我們現在需要把這個氣泡View移動到文本上面去。
????然后我們又看到applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache)這個方法中,可以看到他獲得了icon,又進行了一些設置(setCompoundDrawablesWithIntrinsicBounds(null, new FastBitmapDrawable(b), null, null);
)結合上面的注釋,我們猜也猜的出來,這個BubbleTextView就是我們桌面上的一個個APP的圖標。
????OK,到這一步之后,我們已經找到了桌面的圖標了,那么下一步就是點擊啟動他了。那么BubbleTextView的點擊事件在哪里呢?我們注意到上面applyFromShortcutInfo這個方法,在Android Studio中ctrl+左鍵可以看到有兩個調用他的類:launcher和workspace,我們重新回到Launcher類(packages/apps/Launcher2/src/com/android/launcher2/Launcher.java):
/**
* Creates a view representing a shortcut inflated from the specified resource.
*
* @param layoutResId The id of the XML layout used to create the shortcut.
* @param parent The group the shortcut belongs to.
* @param info The data structure describing the shortcut.
*
* @return A View inflated from layoutResId.
*/
View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false);
favorite.applyFromShortcutInfo(info, mIconCache); //給BubbleTextView設置相應App的icon
favorite.setOnClickListener(this); //給BubbleTextView設置點擊事件
return favorite;
}
/**
* Launches the intent referred by the clicked shortcut.
*
* @param v The view representing the clicked shortcut.
*/
public void onClick(View v) {
// Make sure that rogue clicks don't get through while allapps is launching, or after the
// view has detached (it's possible for this to happen if the view is removed mid touch).
if (v.getWindowToken() == null) {
return;
}
if (!mWorkspace.isFinishedSwitchingState()) {
return;
}
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
// Open shortcut
final Intent intent = ((ShortcutInfo) tag).intent;
int[] pos = new int[2];
v.getLocationOnScreen(pos);
intent.setSourceBounds(new Rect(pos[0], pos[1],
pos[0] + v.getWidth(), pos[1] + v.getHeight()));
boolean success = startActivitySafely(v, intent, tag);
if (success && v instanceof BubbleTextView) {
mWaitingForResume = (BubbleTextView) v;
mWaitingForResume.setStayPressed(true);
}
} else if (tag instanceof FolderInfo) {
if (v instanceof FolderIcon) {
FolderIcon fi = (FolderIcon) v;
handleFolderClick(fi);
}
} else if (v == mAllAppsButton) {
if (isAllAppsVisible()) {
showWorkspace(true);
} else {
onClickAllAppsButton(v);
}
}
}
可以看到在Launcher中我們終于實現了BubbleTextView的onClick方法,然后在在這個方法中調用了startActivitySafely(v, intent, tag)
方法,我們呢繼續看源碼:
boolean startActivitySafely(View v, Intent intent, Object tag) {
boolean success = false;
try {
success = startActivity(v, intent, tag);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
}
return success;
}
調用了startActivity(v, intent, tag);
:
boolean startActivity(View v, Intent intent, Object tag) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
// Only launch using the new animation if the shortcut has not opted out (this is a
// private contract between launcher and may be ignored in the future).
boolean useLaunchAnimation = (v != null) &&
!intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
UserHandle user = (UserHandle) intent.getParcelableExtra(ApplicationInfo.EXTRA_PROFILE);
LauncherApps launcherApps = (LauncherApps)
this.getSystemService(Context.LAUNCHER_APPS_SERVICE);
if (useLaunchAnimation) {
ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
v.getMeasuredWidth(), v.getMeasuredHeight());
if (user == null || user.equals(android.os.Process.myUserHandle())) {
// Could be launching some bookkeeping activity
startActivity(intent, opts.toBundle());
} else {
launcherApps.startMainActivity(intent.getComponent(), user,
intent.getSourceBounds(),
opts.toBundle());
}
} else {
if (user == null || user.equals(android.os.Process.myUserHandle())) {
startActivity(intent);
} else {
launcherApps.startMainActivity(intent.getComponent(), user,
intent.getSourceBounds(), null);
}
}
return true;
} catch (SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Launcher does not have the permission to launch " + intent +
". Make sure to create a MAIN intent-filter for the corresponding activity " +
"or use the exported attribute for this activity. "
+ "tag="+ tag + " intent=" + intent, e);
}
return false;
}
調用了startActivity(intent, opts.toBundle());
這句就是我們平常在開發中調用的Activity.startActivity(Intent)重載方法。并且由于設置了intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
所以這個Actiivity就會添加到一個新的Task棧中。
????我們接著看源碼(frameworks/base/core/java/android/app/Activity.java):
@Override
public void startActivity(Intent intent) {
this.startActivity(intent, null);
}
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}
調用了startActivityForResult():
public void startActivityForResult(Intent intent, int requestCode) {
startActivityForResult(intent, requestCode, null);
}
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
if (mParent == null) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// If this start is requesting a result, we can avoid making
// the activity visible until the result is received. Setting
// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden during this time, to avoid flickering.
// This can only be done when a result is requested because
// that guarantees we will get information back when the
// activity is finished, no matter what happens to it.
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
// TODO Consider clearing/flushing other event sources and events for child windows.
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
// Note we want to go through this method for compatibility with
// existing applications that may have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}
注意這里出現了一個新的東西:Instrumentation,每個Activity都持有一個Instrumentation對象的引用,但是整個進程只會存在一個Instrumentation對象。當StartActivityForResult()調用之后,實際上還是調用了Instrumentation.execStartActivity()。
????Instrumentation意為“儀器”的意思,這個類里邊的方法大多數和Application和Activity有關。準確的說,這個類就是完成對Application和Activity的初始化和生命周期的調控。
????在上面這段代碼中,我們掉用了mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);
方法,這里的第二個參數mMainThread.getApplicationThread()就是ApplicationThread類型;mToken就是IBinder類型的。我們接著看源碼(frameworks/base/core/java/android/app/Instrumentation.java):
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, String target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
for (int i=0; i<N; i++) {
final ActivityMonitor am = mActivityMonitors.get(i);
if (am.match(who, null, intent)) {
am.mHits++;
if (am.isBlocking()) {
return requestCode >= 0 ? am.getResult() : null;
}
break;
}
}
}
}
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess();
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target, requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
這里我們需要注意到這兩句代碼:
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target, requestCode, 0, null, options);
checkStartActivityResult(result, intent);
我們呢看看這個ActivityManagerNative.getDefault()的源碼(frameworks/base/core/java/android/app/ActivityManagerNative.java):
/**
* Retrieve the system's default/global activity manager.
*/
static public IActivityManager getDefault() {
return gDefault.get();
}
gDefault源碼:
//通過單例模式獲取一個IActivityManager對象,這個對象通過asInterface(b)獲得
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
asInterface(b)源碼,其中參數b為IBinder類型的數據:
/**
* Cast a Binder object into an activity manager interface, generating
* a proxy if needed.
*/
//最終返回的還是一個ActivityManagerProxy對象
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
//這里面的Binder類型的obj參數會作為ActivityManagerProxy的成員變量保存為mRemote成員變量,負責進行IPC通信
return new ActivityManagerProxy(obj);
}
繞了一圈,到這里我們可以看到,execStartActivity()中的ActivityManagerNative.getDefault()返回的實際上就是通過單例模式返回的一個ActivityManagerProxy的對象,而在這個過程中,我們傳遞了一個IBinder類型的對象:new ActivityManagerProxy(obj),這個IBinder數據是通過IBinder b = ServiceManager.getService("activity");產生的,也就是說該變量是一個與activity相關的信息,具體是什么信息我們暫時不做探討。
????我們點進去ActivityManagerProxy這個類看下(frameworks/base/core/java/android/app/ActivityManagerNative.java):
class ActivityManagerProxy implements IActivityManager
{
public ActivityManagerProxy(IBinder remote)
{
mRemote = remote;
}
public IBinder asBinder()
{
return mRemote;
}
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
data.writeString(callingPackage);
intent.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeStrongBinder(resultTo);
data.writeString(resultWho);
data.writeInt(requestCode);
data.writeInt(startFlags);
if (profilerInfo != null) {
data.writeInt(1);
profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
data.writeInt(0);
}
if (options != null) {
data.writeInt(1);
options.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
int result = reply.readInt();
reply.recycle();
data.recycle();
return result;
}
......
}
這里的的startActivity()方法就是前面我們說道重要的兩句代碼中的int result = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target, requestCode, 0, null, options);
方法。我們首先看一下這個方法中的參數:
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flags,
ProfilerInfo profilerInfo, Bundle options) throws RemoteException;
caller: 當前應用的ApplicationThread對象mAppThread;
callingPackage: 調用當前ContextImpl.getBasePackageName(),獲取當前Activity所在包名;
intent: 這便是啟動Activity時,傳遞過來的參數;
resolvedType: 調用intent.resolveTypeIfNeeded而獲取;
resultTo: 來自于當前Activity.mToken
resultWho: 來自于當前Activity.mEmbeddedID
requestCode = -1;
startFlags = 0;
profilerInfo = null;
options = null;
這個方法中要做的事情就是IPC通信,利用Binder對象,調用mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);方法,把所需要的參數封裝成Parcel對象,向AMS通信。這里我們打包成Parcel對象的有兩個重要的信息:caller——當前應用的的ApplicationThread對象,callingPackage——調用當前ContextImpl.getBasePackageName(),獲取當前Activity所在包名。
????這里不得不多說一點,這個ActivityManagerProxy是干什么的?實際上ActivityManagerProxy就是ActivityManagerService在客戶端的代理。嗯,可能到這里有些同學就有些糊涂了——客戶端是什么鬼?嗯,我們一定聽說過C/S框架吧?實際上,服務器客戶端的概念不僅僅存在于Web開發中,在Android的框架設計中使用的也是這種模式。服務其端指的是所有App共用的系統服務,比如我們在這里提到的ActivityManagerService,WindowManagerService以及PackageManagerService等等,這些基礎的系統服務是被所有的APP公用的,當某個App想實現某個操作的時候,就告訴這些系統服務,然后由這些系統服務調用對應APP的具體方法來實現,而這些App或者更具體的Activity就是對應的概念上的客戶端。再具體一點,在我們本篇文章的分析中,AMS就是服務端,而AactiviyThread以及具體的Aactiviy就是客戶端。
????我們接著看源碼,在ActivityManagerNative.getDefault().startActivity()中,我們需要注意到最后他調用了mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);方法,這里mRemote是IBinder類的對象,而IBinder類是一個接口,其中定義了該方法:
/**
* Perform a generic operation with the object.
*
* @param code The action to perform. This should
* be a number between {@link #FIRST_CALL_TRANSACTION}
* {@link #LAST_CALL_TRANSACTION}.
* @param data Marshalled data to send to the target. Must not be null.
* If you are not sending any data, you must create an empty Parcel
* that is given here.
* @param reply Marshalled data to be received from the target. May be
* null if you are not interested in the return value.
* @param flags Additional operation flags. Either 0 for a normal
* RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
*/
public boolean transact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException;
而在Binder類中實現了IBinder接口,并通過下列方法傳遞了參數:
/**
* Default implementation rewinds the parcels and calls onTransact. On
* the remote side, transact calls into the binder to do the IPC.
*/
public final boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}
/**
* Default implementation is a stub that returns false. You will want
* to override this to do the appropriate unmarshalling of transactions.
*
* <p>If you want to call this, call transact().
*/
protected boolean onTransact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
if (code == INTERFACE_TRANSACTION) {
reply.writeString(getInterfaceDescriptor());
return true;
} else if (code == DUMP_TRANSACTION) {
ParcelFileDescriptor fd = data.readFileDescriptor();
String[] args = data.readStringArray();
if (fd != null) {
try {
dump(fd.getFileDescriptor(), args);
} finally {
try {
fd.close();
} catch (IOException e) {
// swallowed, not propagated back to the caller
}
}
}
// Write the StrictMode header.
if (reply != null) {
reply.writeNoException();
} else {
StrictMode.clearGatheredViolations();
}
return true;
}
return false;
}
可以看到,int result = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target, requestCode, 0, null, options);
方法中封裝成Parcel對象的一系列參數最終出傳到了Binder類中的boolean onTransact(int code, Parcel data, Parcel reply,
int flags)方法中,為什么要說這些呢?因為接下來,我們的startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target, requestCode, 0, null, options);
方法所在的ActivityManagerNative類是繼承自Binder的:
public abstract class ActivityManagerNative extends Binder implements IActivityManager{
而在這個類中我們找到了如下方法:
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
case START_ACTIVITY_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
IApplicationThread app = ApplicationThreadNative.asInterface(b);
String callingPackage = data.readString();
Intent intent = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
IBinder resultTo = data.readStrongBinder();
String resultWho = data.readString();
int requestCode = data.readInt();
int startFlags = data.readInt();
ProfilerInfo profilerInfo = data.readInt() != 0
? ProfilerInfo.CREATOR.createFromParcel(data) : null;
Bundle options = data.readInt() != 0
? Bundle.CREATOR.createFromParcel(data) : null;
int result = startActivity(app, callingPackage, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profilerInfo, options);
reply.writeNoException();
reply.writeInt(result);
return true;
}
case START_ACTIVITY_AS_USER_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
IApplicationThread app = ApplicationThreadNative.asInterface(b);
String callingPackage = data.readString();
Intent intent = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
IBinder resultTo = data.readStrongBinder();
String resultWho = data.readString();
int requestCode = data.readInt();
int startFlags = data.readInt();
ProfilerInfo profilerInfo = data.readInt() != 0
? ProfilerInfo.CREATOR.createFromParcel(data) : null;
Bundle options = data.readInt() != 0
? Bundle.CREATOR.createFromParcel(data) : null;
int userId = data.readInt();
int result = startActivityAsUser(app, callingPackage, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profilerInfo, options, userId);
reply.writeNoException();
reply.writeInt(result);
return true;
}
......
//省略的代碼是多個case,針對不同的標志符做出一系列處理
可以看到搞了半天,參數又回調了這個類中,而在上面的方法中不同的case分別調用了:
case START_ACTIVITY_TRANSACTION:
{
int result = startActivity(app, callingPackage, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profilerInfo, options);
}
case START_ACTIVITY_AS_USER_TRANSACTION:
{
int result = startActivityAsUser(app, callingPackage, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profilerInfo, options, userId);
}
case START_ACTIVITY_AS_CALLER_TRANSACTION:
{
int result = startActivityAsCaller(app, callingPackage, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profilerInfo, options,
ignoreTargetSecurity, userId);
}
case START_ACTIVITY_AND_WAIT_TRANSACTION:
{
WaitResult result = startActivityAndWait(app, callingPackage, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profilerInfo, options, userId);
}
......
等等,這些各種各樣的startActivity實際上都是IActivityManager接口中封裝的方法,而真正實現這些方法的是在ActivityManagerService中:
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
public abstract class ActivityManagerNative extends Binder implements IActivityManager{
到此為止,結合上面我們所說的客戶—服務(C/S)端的理念,我們可以看到,由Launcher發起的啟動App默認Activity的請求已經成功的發送到了我們的服務端ActivityManagerService(AMS)中。到這里,ctivity啟動的第一個階段就結束了。