android 4.1 JellyBean 跟踪应用程序选择框弹出流程

来源:互联网 发布:alias软件 汉化版 编辑:程序博客网 时间:2024/05/22 16:44

一:简述

        在android中,如果一个intent可以被多个activity匹配那么,ActivityManagerSevice(AMS)会启动一个应用程序选择框供用户选择使用那个activity来执行这个Intent。比如,如果手机里面装有两个图片显示activity,可以执行图片显示的Intent。显示图片的intent发出后,我们会看到有一个弹出框列出了相关的activity供用户选择。

二:代码流程

        根据activity的启动过程,startActivity的intent发出后,该信息会由AMS接收并解析。解析完后得到该activity的详细信息,然后调用该activity的组建启动。这个启动过程网上有很多介绍,这里就不赘述了。本文主要讨论那个多应用程序选择框的弹出机制。

1. AMS处理的相关代码如下,位于:frameworks/base/services/java/com/android/server/am/ActivityStack.java

    final int startActivityMayWait(IApplicationThread caller, int callingUid,            Intent intent, String resolvedType, IBinder resultTo,            String resultWho, int requestCode, int startFlags, String profileFile,            ParcelFileDescriptor profileFd, WaitResult outResult, Configuration config,            Bundle options, int userId) {        // Refuse possible leaked file descriptors        if (intent != null && intent.hasFileDescriptors()) {            throw new IllegalArgumentException("File descriptors passed in Intent");        }        boolean componentSpecified = intent.getComponent() != null;        // Don't modify the client's object!        intent = new Intent(intent);        // Collect information about the target of the Intent.        ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,                profileFile, profileFd, userId);        if (aInfo != null && mService.isSingleton(aInfo.processName, aInfo.applicationInfo)) {            userId = 0;        }        aInfo = mService.getActivityInfoForUser(aInfo, userId);.....}

系统中,启动activity的intent都是在这里加工,转换成需要的ActivityInfo的。重要的调用函数是resolveActivity(intent, resolvedType, startFlags, profileFile, profileFd, userId);
这就是Intent信息到Activity信息的匹配过程。

2.这个匹配信息是怎么来的呢,根据下面的resolveActivity函数代码,我们知道,所有这些信息保存在PackageManagerService(PMS)中。PMS中的信息是activity在装载到手机上的时候保存的。找到就返回,否则返回null。

    ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,            String profileFile, ParcelFileDescriptor profileFd, int userId) {        // Collect information about the target of the Intent.        ActivityInfo aInfo;        try {            ResolveInfo rInfo =                AppGlobals.getPackageManager().resolveIntent(                        intent, resolvedType,                        PackageManager.MATCH_DEFAULT_ONLY                                    | ActivityManagerService.STOCK_PM_FLAGS, userId);            aInfo = rInfo != null ? rInfo.activityInfo : null;        } catch (RemoteException e) {            aInfo = null;        }。。。。}

3.接下载自然是去PackageManagerService里面查找resolveIntent函数了。

    public ResolveInfo resolveIntent(Intent intent, String resolvedType,            int flags, int userId) {        if (!sUserManager.exists(userId)) return null;        List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);        return chooseBestActivity(intent, resolvedType, flags, query, userId);    }

这里面调用一个queryIntentActivities函数,返回可以匹配该intent的所有activity列表。然后,PMS接着调用chooseBestActivity函数,选择最佳activity,然后返回。

4.最佳activity选择过程

    private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,            int flags, List<ResolveInfo> query, int userId) {        if (query != null) {            final int N = query.size();            if (N == 1) {                return query.get(0);            } else if (N > 1) {                // If there is more than one activity with the same priority,                // then let the user decide between them.                ResolveInfo r0 = query.get(0);                ResolveInfo r1 = query.get(1);                if (DEBUG_INTENT_MATCHING) {                    Log.d(TAG, r0.activityInfo.name + "=" + r0.priority + " vs "                            + r1.activityInfo.name + "=" + r1.priority);                }                // If the first activity has a higher priority, or a different                // default, then it is always desireable to pick it.                if (r0.priority != r1.priority                        || r0.preferredOrder != r1.preferredOrder                        || r0.isDefault != r1.isDefault) {                    return query.get(0);                }                // If we have saved a preference for a preferred activity for                // this Intent, use that.                ResolveInfo ri = findPreferredActivity(intent, resolvedType,                        flags, query, r0.priority, userId);                if (ri != null) {                    return ri;                }                return mResolveInfo;            }        }        return null;    }

这里,如果只有一个activty匹配,那么直接返回该acivity的ResolveInfo。如果选择出来的activity大于一个,那么会挑出前两个进行比较,默认情况下优先级、preferredOrder和isDefault都是一样的。本函数返回mResolveInfo。该resolveinfo包含一个叫做ResolveActivity的activity信息。把这个信息返回给AMS去启动,那就变成启动这个activity了。这个activity是一个AlerActivity,它收集和显示匹配的所有activity信息,供用户选择。

5.再者,选择框中,如果用户选择了某个应用程序,而且指定永久使用该activity来执行相关intent,这时候,系统也是会跑上面的流程,只不过在findPreferredActivity函数中做进一步处理。

6.findPreferredActivity的主要工作原理是:在setting里面保存着一个用户勾选过的mPreferredActivities列表,然后比较是否传入的参数是否在mPreferredActivities中。

List<PreferredActivity> prefs =
                    mSettings.mPreferredActivities.queryIntent(intent, resolvedType,
                            (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);