本文学习拜读自罗升阳老师的《Android系统源代码情景分析》
本文实验环境为 Android6.0
待续…
Activity是Android应用程序的四大组件之一,他负责管理Android应用程序的用户界面。一个应用程序一般会包含若干个Activity组件,每一个Activity组件负责一个用户界面的展现,它们可能运行在同一个进程中,也可能运行在不同的进程中。运行在不同的进程中的Actity组件通过Binder进程间通信机制来协作完成应用程序的功能。
从应用程序的角度出发,我们可以将Activity组件划分为两种类型:一种是根Activity,另一种是子Activity。根Activity以跨界图标的形式显示在应用程序启动器中,它的启动过程就代表了一个Android应用程序的启动过程。子Activity由根Activity或者其他子Activity启动,它们有可能与启动它们的Activity运行在同一个进程中,也有可能运行在不同的进程中,这取决于它们的配置和启动参数。
Android组件的启动方式分为显示和隐式两种,对于显示启动Activity组件来说,我么必须事先知道用来实现它们类的名称。对于隐式启动Activity来说,我们只需要知道它们组件的名称即可,而不需要知道它们是由哪一个类来实现的。无论是显示启动Activity组件,还是隐式启动Activity组件,它们的启动过程都是类似的,唯一的区别在于系统是根据类名还是组件名称
来找到它们。但是从软件工程的角度来看,隐式启动Activity组件可以减少Android应用程序组件间的依赖,因此,本文主要分析Activity隐式。
在本文中,我们将开发一个名称为 Activity 的 Android应用程序,他由三个Activity组件 MainActivity、SubActivityInProcess和SubActivityInNewProcess组成,其中MainActivity是跟Activity,SubActivityInProcess和SubActivityInNewProcess是子Activity。SubActivityInProcess与MainActivity运行在同一个进程中,而SubActivityInNewProcess运行在一个独立的进程中。
应用程序 Activity 的目录结构如下:
Android/packages/apps/Activity
-----AndroidManifest.xml
-----Android.mk
-----src
-----com/android/activity
-----MainActivity.java
-----SubActivityInProcess.java
-----SubActivityInNewProcess.java
-----res
-----layout
-----main.xml
-----sub.xml
-----values
-----strings.xml
-----drawable
-----icon.png
MainActivity是应用程序 Activity的根Activity组件,它的用户界面有两个按钮,分别用来启动 SubActivityInProcess和SubActivityInNewProcess这两个子Activity。SubActivityInProcess和SubActivityInNewProcess的组件名称分别被配置为 “haoran.ma.activity.in.process” 和 “haoran.ma.activity.in.new.process” ,因此代码中调用 startActivity()启动它们时,只需要分别指定这两个名称即可,而不需要知道它们是哪一个类实现的。
package com.android.activity;
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 = "haoran.ma.activity.MainActivity";
private Button startInProcessButton = null;
private Button startInNewProcessButton = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
startInProcessButton = (Button)findViewById(R.id.button_start_in_process);
startInNewProcessButton = (Button)findViewById(R.id.button_start_in_new_process);
startInProcessButton.setOnClickListener(this);
startInNewProcessButton.setOnClickListener(this);
Log.i(LOG_TAG, "Main Activity Created.");
}
@Override
public void onClick(View v) {
if(v.equals(startInProcessButton)) {
Intent intent = new Intent("haoran.ma.activity.subactivity.in.process");
startActivity(intent);
}else if(v.equals(startInNewProcessButton)){
Intent intent = new Intent("haoran.ma.activity.subactivity.in.new.process");
startActivity(intent);
}
}
}
SubActivityInProcess是应用程序 Activity 的一个子 Activity组件,它是由 MainActivity组件启动起来的,当我们点击它的用户界面上的一个 finish按钮时,就可以返回到 MainActivity 组件中。
package com.android.activity;
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 SubActivityInProcess extends Activity implements OnClickListener {
private final static String LOG_TAG = "haoran.ma.activity.SubActivityInProcess";
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 In Process Created.");
}
@Override
public void onClick(View v) {
if(v.equals(finishButton)) {
finish();
}
}
}
SubActivityInNewProcess是应用程序Activity的另外一个子Activity组件,它也是由MainActivity组件启动起来的,不过它是在一个新的进程中启动,当我们点击它的用户界面 finish时候,同样可以返回MainActivity组件中。
package com.android.activity;
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 SubActivityInNewProcess extends Activity implements OnClickListener {
private final static String LOG_TAG = "haoran.ma.activity.SubActivityInNewProcess";
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 In New Process Created.");
}
@Override
public void onClick(View v) {
if(v.equals(finishButton)) {
finish();
}
}
}
这是 MainActivity组件的界面配置文件,它在屏幕中间显示两个按钮
android:orientation=“vertical” :指定是 垂直布局
android:gravity=“center”> :指的是上下居中
这是 SubActivityInProcess组件和SubActivityInNewProcess组件的界面配置文件,它在屏幕中间显示一个按钮。
这是应用程序Activity的字符串资源文件,定义了在应用程序中用到的各个字符串。
Activity
Sub Activity
Start sub-activity in process
Start sub-activity in new process
Finish Activity
这是应用程序Activity的图标,可以根据需要来放置不同的图片文件
这是应用程序Activity的配置文件。由于在程序中使用到了三个Activity组件 :MainActivity、SubActivityInProcess和SubActivityInNewProcess,因此我们需要对他们进行配置。
我们将MainActivity组件的 action 名称和 category名称分别设置为"android.intent.action.MAIN" 和 “android.intent.category.LAUNCHER”,使得它们可以作为应用程序Activity的根Activity组件。
将SubActivityInProcess和SubActivityInNewProcess组件的action分别设置为自定义的"haoran.ma.activity.subactivity.in.process"和"haoran.ma.activity.subactivity.in.new.process"因此,可以通过这两个名称来隐式的启动它们。
此外,我们还将 MainActivity组件和SubActivityInProcess组件的 android:process 属性 设置为为"haoran.ma.activity.mainprocess",以便它们可以运行在同一个进程中。而将SubActivityInNewProcess组件的 android:process 属性设置为 “haoran.ma.activity.newprocess”,以便它可以运行在另外一个进程中。
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := Activity
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
# Use the folloing include to make our test apk.
include $(call all-makefiles-under,$(LOCAL_PATH))
此为RK3128 Android6.0平台 需要在 build/target/product/core.mk中添加 应用名称
最后执行 mmm ./packages/experimental/MhrActivity/
注意
rk3288_7.1_mid\packages\experimental\Activity\Android.mk
LOCAL_PATH:= $(call my-dir) //后面不能以后空格!!!! 否则报错,举一反三 尽量都不要有空格
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := Activity
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
# Use the folloing include to make our test apk.
include $(call all-makefiles-under,$(LOCAL_PATH))
xml 文件开头的
开头不能有空格 否则报错
启动界面:
以上面的应用程序 Activity的MainActivity组件的启动过程为例,分析Android应用程序的根Activity组件的启动过程,即Android应用程序
的驱动过程。
由于根Activity组件代表了一个Android应用程序,因此,它一般是在一个新的进程中启动起来的。由于 MainActivity组件的 android:process设置
成了 “haoran.ma.activity.mainprocess”,因此,Activity管理服务 ActivityManagerService 在启动 MainActivity组件时,就会发现系统中并不存在
一个名称为 "haoran.ma.activity.mainprocess"的应用程序进程,这时候它会先创建这个应用程序进程,然后再将 MainActivity组件启动起来。
MainActivity组件是由Launcher组件来启动的,而Launcher组件又是通过Activity管理服务ActivityManagerService来启动MainActivity组件的。由
于MainActivity组件、Launcher组件、ActivityManagerService 是分别运行在不同的进程中的,因此,MainActivity组件的驱动过程就涉及到了三个进程。
这三个进程是通过Binder进程间通信机制来完成 MainActivity组件的启动过程的。
Launcher组件启动MainActivity组件的过程如下所示:
1 Launcher组件向ActivityManagerService发送一个启动MainActivity组件的进程间通信请求。
2 ActivityManagerService首先将要启动的MainActivity组件的信息保存下来,然后向Launcher组件发送一个进入中止状态的进程间通信请求。
3 Launcher组件进入中止状态之后,就会向ActivityManagerService发送一个已经进入中止状态的进程间通信请求,以便ActivityManagerService可以继续
执行启动MainActivity组件的操作。
4 ActivityManagerService发现用来运行MainActivity组件的应用程序进程不存在,因此,它就会先启动一个新的应用程序进程。
5 新的应用程序进程启动完成之后,就会向ActivityManagerService发送一个完成的进程间通信请求,以便ActivityManagerService可以继续执行启动
MainActivity组件的操作。
6 ActivityManagerService将第二步保存下来的MainActivity组件的信息发送个第四步创建的应用程序进程,以便它可以将 MainActivity组件启动起来。
注意:ActivityManagerService是一个系统关键服务,它运行在系统进程System中,负责启动和调度应用程序组件。
当我们在应用程序启动器Launcher的界面上点击一个应用程序的快捷图标的时候,Launcher组件的成员函数startActivitySafely就会被调用来启动这个应用程序的根Avctivity,其中
要启动的根Activity的信息包含在参数intent中。
packages/apps/Launcher2/src/com/android/launcher2/Launcher.java
public final class Launcher extends Activity
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,View.OnTouchListener {
......
boolean startActivitySafely(View v, Intent intent, Object tag) {
boolean success = false;
try {
/*
调用父类Activity的成员函数 startActivity()来启动单数所描述的 Activity组件
*/
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;
}
......
}
当应用程序Activity的ManiActivity组件被Launcher组件启动时,参数intent所包含的Activity组件信息如下:
action android:name=“android.intent.action.MAIN” 和 category android:name=“android.intent.category.LAUNCHER” 表示要启动的Activity组件的Action名称和Category名称。
cmd = “haoran.ma.activity.MainActivity” 表示Activity组件是由 haoran.ma.activity.MainActivity 类来实现的。
action android:name="android.intent.action.MAIN"
category android:name="android.intent.category.LAUNCHER"
cmd = "haoran.ma.activity.MainActivity"
那么Launcher组件是如何获得这些信息的呢?系统在启动时,回启动一个Package管理服务PackageManagerService,并通过它来安装系统中的应用程序。PackageManagerService在
安装一个应用程序的过程中,会对它的配置文件 AndroidManifest.xml进行解析,从而得到它里面的组件信息。系统在启动完成之后,就会将Launcher组件启动起来。Launcher组件在启
动过程中,会向PackageManagerService查询所有Action名称等于 xxx.Intent.ACTION_MAIN,以及 Category名称等于 "xxx.Intent.CATEGORY_LAUNCHER"的Activity组件,最后为每一个
Activity创建一个快捷图标,并且将它们的信息与各自的快捷图标关联起来,以便用户点击它们时可以将对应的Activity组件启动起来。
Launcher组件的成员函数 startActivitySafely()中,调用父类Activity的成员函数 startActivity()来启动单数所描述的 Activity组件。
frameworks\base\core\java\android\app\Activity.java
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback {
......
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
/*
启动参数intent所描述的Activity组件,第二个参数 -1 表示Launcher组件不需要知道即将启动的Activity组件的执行结果
*/
startActivityForResult(intent, -1);
}
}
......
public void startActivityForResult(String who, Intent intent, int requestCode, @Nullable Bundle options) {
Uri referrer = onProvideReferrer();
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, who,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, who, requestCode,
ar.getResultCode(), ar.getResultData());
}
cancelInputsAndStartExitTransition(options);
}
}
…待续…