在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的布局:
![ ![](https://img.haomeiwen.com/i2179030/b6d4b1c77e79180e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ](https://img.haomeiwen.com/i2179030/1d71a1fe73210bd9.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)图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启动的第一个阶段就结束了。
网友评论