在Android的Framework中自己创建Exception并使用的方法

应用场景:

在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系统就在模拟器中启动起来了!



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值