Android7.0点击Launcher的AMS应用启动流程


因为工作中经常要用到应用的启动流程,而网上和书本上大部分都是Android5.0和6.0的文章,有些部分Android7.0又做了修改,故本篇文章主要是整理以往大牛们在Android5.0、Android6.0的基础上对Android7.0点击Launcher到应用启动完成的一个大体流程,用于工作和大家参考,如有不足之处,欢迎指正。


当点击桌面的应用图标后,会触发Launcher3的Launcher.java中的onClick(View v)方法执行,一系列判断后将点击的View作为参数,调用startAppShortcutOrInfoActivity(v),
在startAppShortcutOrInfoActivity(v)中,根据传入参数View v的v.getTag()方法得到被点击应用图标的ShortcutInfo,然后得到Intent数据。
通过final Intent intent = ((ShortcutInfo) tag).intent语句得到数据,有了一个应用的Intent就可以启动一个应用了。


    public void onClick(View v) {
	...
	Object tag = v.getTag();
        if (tag instanceof ShortcutInfo) {
            onClickAppShortcut(v);
        } else if (tag instanceof FolderInfo) {
            if (v instanceof FolderIcon) {
                onClickFolderIcon(v);
            }
        } else if (v == mAllAppsButton) {
            onClickAllAppsButton(v);
        } else if (tag instanceof AppInfo) {
            startAppShortcutOrInfoActivity(v);
        } else if (tag instanceof LauncherAppWidgetInfo) {
            if (v instanceof PendingAppWidgetHostView) {
                onClickPendingWidget((PendingAppWidgetHostView) v);
            }
        }
    }


    @Thunk void startAppShortcutOrInfoActivity(View v) {
        Object tag = v.getTag();
        final ShortcutInfo shortcut;
        final Intent intent;
        if (tag instanceof ShortcutInfo) {
            shortcut = (ShortcutInfo) tag;
            intent = shortcut.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()));

        } else if (tag instanceof AppInfo) {
            shortcut = null;
            intent = ((AppInfo) tag).intent;
        } else {
            throw new IllegalArgumentException("Input must be a Shortcut or AppInfo");
        }

        boolean success = startActivitySafely(v, intent, tag);
	...
    }

接着调用了startActivitySafely(v, intent, tag),这里调用了startActivity(v, intent, tag),参数中含有intent,intent中包含了action、category等信息。

    public boolean startActivitySafely(View v, Intent intent, Object tag) {
	boolean success = false;
        ...
        try {
            success = startActivity(v, intent, tag);
        } catch (ActivityNotFoundException e) {
            if (v instanceof TextView){
                return success;
            }
            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;
    }

Launcher继承于Activity类,在startActivity(v, intent, tag)中调用了重写的startActivity(Intent intent, @Nullable Bundle options),因此,这里就调用了Activity.startActivity方法;
在startActivity(intent, optsBundle)中参数optsBundle包含了应用启动动画的坐标,width和height。

    private boolean startActivity(View v, Intent intent, Object tag) {
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        ...
            if (user.equals(UserHandleCompat.myUserHandle())) {
                StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
                try {
                    StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
                            .penaltyLog().build());             
                    startActivity(intent, optsBundle);
                } finally {
                    StrictMode.setVmPolicy(oldPolicy);
                }
            } 
	...
    }


    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if (options != null) {
            startActivityForResult(intent, -1, options);
        } else {
            startActivityForResult(intent, -1);
        }
    }

在Activity.startActivity方法中又调用了Activity.startActivityForResult方法

    public void startActivityForResult(@RequiresPermission 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());
            }
	    ...
        } 
	...
    }

这里的mInstrumentation是Activity类的成员变量,它的类型是Intrumentation,定义在Instrumentation.java文件中,它用来监控应用程序和系统的交互。
这里的mMainThread也是Activity类的成员变量,它的类型是ActivityThread,它代表的是应用程序的主线程。这里通过mMainThread.getApplicationThread获得它里面的ApplicationThread成员变量,它是一个Binder对象,ActivityManagerService会使用它来和ActivityThread来进行进程间通信,mMainThread代表的是Launcher应用程序运行的进程。
这里的mToken也是Activity类的成员变量,它是一个Binder对象的远程接口。
接下来调用了Instrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options)

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        ...
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

可以看出,启动Activity真正的实现由ActivityManagerNative.getDefault()的.startActivity方法执行。
这里的ActivityManagerNative.getDefault()返回的是ActivityManagerService的一个代理类ActivityManagerProxy。

    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;
    }

这里的参数比较多,我们先整理一下。从上面的调用可以知道,这里的参数resolvedType、grantedUriPermissions和resultWho均为null;参数caller为ApplicationThread类型的Binder实体;参数resultTo为一个Binder实体的远程接口,我们先不关注它;参数grantedMode为0,我们也先不关注它;参数requestCode为-1;参数onlyIfNeeded和debug均空false。
由于ActivityManagerProxy是一个代理类,上面是通过IPC的Binder联系到ActivityManagerService,最后会调用ActivityManagerService的startActivity方法。
AMS(ActivityManagerServie)中提供了几个借口来启动Activity,但是,他们都会调用到ActivityStarter类的startActivityMayWait()方法,Android7.0以前是ActivityStackSupervisor类;
接下来以上面调用的startActivity方法继续分析,startActivity方法调用了startActivityAsUser方法,代码如下

    @Override
    public int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, St
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值