应用场景:
在Android的Activity启动过程中,会根据Activity的启动情况返回一些值(这些返回值是ActivityManager的成员变量,如ActivityManager.ACTIVITY_PERMISSION_DENIED等等),这些返回值会在Instrumentation.checkStartActivityResult(result, intent)函数中被返回并进行处理。
该环节附近的函数调用关系如下:
(1)Activity.StartActivityAsUser调用Instrumentation.execStartActivity;
public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
if (mParent != null) {
throw new RuntimeException("Called be called from a child");
}
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, -1, options, user);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, -1, ar.getResultCode(),
ar.getResultData());
}
}
(2)Instrumentation.execStartActivity会调用Instrumentation.checkStartActivityResult(result, intent)并将ActivityManagerNative.getDefault()
.startActivity()的返回结果result传递给它进行处理;
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity 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 != null ? target.mEmbeddedID : null,
requestCode, 0, null, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
}
return null;
}
(3)Instrumentation.checkStartActivityResult(result, intent)函数中对返回结果result进行处理,处理情况如下:
/*package*/ static void checkStartActivityResult(int res, Object intent) {
if (res >= ActivityManager.START_SUCCESS) {
return;
}
switch (res) {
case ActivityManager.START_INTENT_NOT_RESOLVED:
case ActivityManager.START_CLASS_NOT_FOUND:
if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
throw new ActivityNotFoundException(
"Unable to find explicit activity class "
+ ((Intent)intent).getComponent().toShortString()
+ "; have you declared this activity in your AndroidManifest.xml?");
throw new ActivityNotFoundException(
"No Activity found to handle " + intent);
case ActivityManager.START_PERMISSION_DENIED:
throw new SecurityException("Not allowed to start activity "
+ intent);
case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
throw new AndroidRuntimeException(
"FORWARD_RESULT_FLAG used while also requesting a result");
case ActivityManager.START_NOT_ACTIVITY:
throw new IllegalArgumentException(
"PendingIntent is not an activity");
default:
throw new AndroidRuntimeException("Unknown error code "
+ res + " when starting " + intent);
}
}
说明:由上述代码可以看出,系统会根据接收到的返回结果,抛出不同的异常。
(4)如果要启动的Activity是由Launcher启动的,那么Laucher 就会捕获这些异常并进行相应的处理。
在Launcher.startActivity函数中,catch异常,并做相应的处理。
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);
if (useLaunchAnimation) {
ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
v.getMeasuredWidth(), v.getMeasuredHeight());
startActivity(intent, opts.toBundle());
} else {
startActivity(intent);
}
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;
}
需求:
我们希望自己定义一种新的返回类型,并在接受到自己定义的返回类型之后抛出自己定义的异常,并在catch到自己定义的异常之后进行自己定义的操作。
Mathod:
(1)首先,仔细研究Android源代码我们发现,Activity启动过程中返回类的数据类型都是定义在ActivityManager中的。所以我们在ActivityManager中添加上自己要定义的返回类型:
public static final int MYFLAG = -7;
(2)在Activity的启动流程中,根据自己的需要在合适的位置返回自己定义的类型:return ActivityManager.MYFLAG;或者return -7。
(3)在Instrumentation.checkStartActivityResult(result, intent)函数中增加接收到我们自己定义的返回类型时候的处理,抛出我们自己定义的异常。
仿照着增加一种case即可:
case ActivityManager.MYFLAG:
throw new MyException("MyProblem "
+ intent);
需要注意的是,我们自己定义的Exception实在frameworks/base/core/java/android/content文件夹下面创建的,因为我们发现Instrumentation.checkStartActivityResult(result, intent)函数中抛出的异常ActivityNotFoundException就是在这里定义的,所以我们仿照该Exception在此定义了我们自己的异常:MyException.java。具体实现请参考ActivityNotFoundException.java即可。
(4)在Launcher.java文件中的Launcher.startActivity函数中增加捕获我们自己定义的异常操作,仿照着添加以下代码即可:
catch (MyException e) {
//具体实现代码,自己添加
}
经过上述四步,在对Android源代码的修改就算完成了。
(5)下面是编译运行的问题。
常规操作:在Android源文件夹下执行以下代码:
source build/envsetup.sh
mmm [-B] ./frameworks/base/core/res/
mmm [-B] ./frameworks/base/
mmm [-B] ./packeges/apps/Launcher2/
(注意:有Android.mk文件的目录才可以mmm 单独编译该块儿目录下的文件)
make snod
但是实验多次,改变编译顺序等都不能编译通过,所以最后我们选择了编译所有文件,并最终成功。
直接在Android源文件夹下执行以下代码:
make update-api
这一步是编译时系统提示的,我们操作之后,发现frameworks\base\api文件夹下的current.text文件产生了修改,修改内容包括:
在package android.app中添加了:
field public static final int MYFLAG=-7;
在package android .content中添加了:
package class MyExceotion extends java.lang.os.Parcelable{
ctor public MyExceotion();
ctor public MyExceotion(java.lang.String);
}
经过对current.txt文件的进一步分析,发现文件中包含各种和硬件以及编程基础相关的包(只是列出了包内的一些重要参数和函数声明),比如字体,gsm,cdma等,当然也包含一些java编程常用的包,例如java.awt.font等。所以,我们猜测该文件是一个系统提供的可用于编程的接口集合,这些接口连接着底层硬件和上层应用。
(6)编译并完成:
经过上述准备之后,在Android额源文件下,执行:
make
make showcommands WITHDEXPREOPT=false;
lunch 1
emulator-arm -sdcard sdcard.img
这样拥有我们自己定义的Exception的Android系统就在模拟器中启动起来了!