android6.0默认Home(Launcher3)的启动分析

来源:互联网 发布:淘宝pc端优惠券地址 编辑:程序博客网 时间:2024/06/05 09:44

Launcher是默认的桌面应用,在系统启动后开始启动Launcher,进而才加载桌面数据。那么如何实现开机进入默认Launcher,比如把自己写的应用设置成开机默认启动的桌面呢?带着这个问题来分析Launcher是如何被选中并成为默认桌面应用而启动的。

SystemServer启动ActivityManagerService并调用了它的systemReady()函数。

ActivityManagerService

public void systemReady(final Runnable goingCallback){    //Start up initial actiivty    mBooting = true;    startHomeActivityLocked(mCurrentUserId, "systemReady");}

接着调用startHomeActivityLocked()启动Launcher

boolean startHomeActivityLocked(int userId, String reason){    if(mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) && mTopAction == null){        // We are running in factory test mode, but unable to find        // the factory test app, so just sit around displaying the        // error message and don't try to start anything.        return false;    }    //给Intent设置Category    Intent intent = getHomeIntent();    //获取ActivityInfo    ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);    if(aInfo != null){        intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));        // Don't do this if the home app is currently being instrumented.        aInfo = new ActivityInfo(aInfo);        aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);        ProcessRecord app = getProcessRecordLocked(aInfo.processName,aInfo.applicationInfo.uid,true);        if(app == null || app.instrumentationClass == null){            intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);            //启动launcher            mStackSupervisor.startHomeActivity(intent, aInfo, reason);        }    }    return true;}

这个函数主要完成的工作:函数首先创建一个CATEGORY_HOME类型的Intent,然后通过 Intent.resolveActivityInfo函数向 PackageManagerService查询Category类型为HOME的Activity。

1、getHomeIntent()给Intent设置Category:CATEGORY_HOME

mTopAction = Intent.MAINIntent getHomeIntent(){    Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);    intent.setComponent(mTopComponent);    if(mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL){        //设置category        intent.addCategory(Intent.CATEGORY_HOME);    }    return intent;}

2、resolveActivityInfo()获取ActivityInfo信息

private ActivityInfo resolveActivityInfo(Intent intent, int flags, int userId){    ActivityInfo ai = null;    ComponentName comp = intent.getComponent();    try{        if(comp != null){            //Factory test            ai = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);        }else{            ResolveInfo info = AppGlobals.getPackageManager().resolveIntent(            intent,            intent.resolveTypeIfNeed(mContext.getContentResolver()            flags,userId);            if(info != null){                ai = info.activityInfo;            }        }    }catch(RemoteException e){    }    return ai;}

2.1、AppGlobal.getPacakgeManager()返回的是IPackageManager对象,PackageManagerService extends IPacakgeManager,因此调用的是PackageManagerService的resolveIntent()函数。

public ResolveInfo resolveIntent(Intent intent, String resolvedType,        int flags, int userId) {    if (!sUserManager.exists(userId)) return null;    //检查权限    enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "resolve intent");    List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);    return chooseBestActivity(intent, resolvedType, flags, query, userId);}

这里主要完成的工作是:
(1)检查权限
(2)查找符合要求的Activities,ActivityInfo是ResolveInfo的一个变量,这里返回的是符合条件的装有ResolveInfo的集合。

public List<ResolveInfo> queryIntentActivities(Intent intent,        String resolvedType, int flags, int userId) {    if (!sUserManager.exists(userId)) return Collections.emptyList();    enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "query intent activities");    ComponentName comp = intent.getComponent();    if (comp == null) {        if (intent.getSelector() != null) {            intent = intent.getSelector();            comp = intent.getComponent();        }    }    //判断Intent指定的模块不为空,则返回list,满足条件的只有一个    if (comp != null) {        final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);        final ActivityInfo ai = getActivityInfo(comp, flags, userId);        if (ai != null) {            final ResolveInfo ri = new ResolveInfo();            ri.activityInfo = ai;            list.add(ri);        }        return list;    }    // reader    synchronized (mPackages) {        final String pkgName = intent.getPackage();        //如果Intent没有指定包名,在系统所有包中查找        if (pkgName == null) {            List<CrossProfileIntentFilter> matchingFilters =                    getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);            // Check for results that need to skip the current profile.            ResolveInfo xpResolveInfo  = querySkipCurrentProfileIntents(matchingFilters, intent,                    resolvedType, flags, userId);            if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) {                List<ResolveInfo> result = new ArrayList<ResolveInfo>(1);                result.add(xpResolveInfo);                return filterIfNotPrimaryUser(result, userId);            }            // Check for results in the current profile.            List<ResolveInfo> result = mActivities.queryIntent(                    intent, resolvedType, flags, userId);            // Check for cross profile results.            xpResolveInfo = queryCrossProfileIntents(                    matchingFilters, intent, resolvedType, flags, userId);            if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) {                result.add(xpResolveInfo);                Collections.sort(result, mResolvePrioritySorter);            }            result = filterIfNotPrimaryUser(result, userId);            if (hasWebURI(intent)) {                CrossProfileDomainInfo xpDomainInfo = null;                final UserInfo parent = getProfileParent(userId);                if (parent != null) {                    xpDomainInfo = getCrossProfileDomainPreferredLpr(intent, resolvedType,                            flags, userId, parent.id);                }                if (xpDomainInfo != null) {                    if (xpResolveInfo != null) {                        // If we didn't remove it, the cross-profile ResolveInfo would be twice                        // in the result.                        result.remove(xpResolveInfo);                    }                    if (result.size() == 0) {                        result.add(xpDomainInfo.resolveInfo);                        return result;                    }                } else if (result.size() <= 1) {                    return result;                }                result = filterCandidatesWithDomainPreferredActivitiesLPr(intent, flags, result,                        xpDomainInfo, userId);                Collections.sort(result, mResolvePrioritySorter);            }            return result;        }        final PackageParser.Package pkg = mPackages.get(pkgName);        //如果Intent指定的包名不为空,则查找安装包中指定的包        if (pkg != null) {            return filterIfNotPrimaryUser(                    mActivities.queryIntentForPackage(                            intent, resolvedType, flags, pkg.activities, userId),                    userId);        }        return new ArrayList<ResolveInfo>();    }}

这里根据Intent中指定的信息来处理。如果Intent有指定ComponentName则调用getActivityInfo()函数并返回ActivityInfo。如果Intent只指定包名,则调用queryIntentForPackage()函数在指定的安装包中查找符合条件的Activity。如果Intent没有指定包名也没有指定组件名,则调用queryIntent()函数来查找所有安装包。

(3)选择并返回返回最合适的ResolveInfo

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) {            final boolean debug = ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);            // 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 || debug) {                Slog.v(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, true, false, debug, userId);            if (ri != null) {                return ri;            }            ri = new ResolveInfo(mResolveInfo);            ri.activityInfo = new ActivityInfo(ri.activityInfo);            ri.activityInfo.applicationInfo = new ApplicationInfo(                    ri.activityInfo.applicationInfo);            if (userId != 0) {                ri.activityInfo.applicationInfo.uid = UserHandle.getUid(userId,                        UserHandle.getAppId(ri.activityInfo.applicationInfo.uid));            }            // Make sure that the resolver is displayable in car mode            if (ri.activityInfo.metaData == null) ri.activityInfo.metaData = new Bundle();            ri.activityInfo.metaData.putBoolean(Intent.METADATA_DOCK_HOME, true);            return ri;        }    }    return null;}

从查询到的符合条件的集合中选择最符合的Activity。如果符合条件的只有一个应用,则返回该应用信息(源码中确实只有Launcher3符合),否则,选出前两个,比较优先级priority、preferredOrder、isDefault三个参数,选出优先级最高的Activity。如何那三个参数都一样则查找看用户是否选择了永久使用的Activity,如果有则返回该Activity对应的ResolveInfo。

在Launcher的清单文件中设置了

<activity    android:name="com.android.launcher3.Launcher"    android:launchMode="singleTask"    android:clearTaskOnLaunch="true"    android:stateNotNeeded="true"    android:theme="@style/Theme"    android:windowSoftInputMode="adjustPan"    android:screenOrientation="nosensor"    android:resumeWhilePausing="false"    android:taskAffinity=""    android:enabled="true">    <intent-filter>        <action android:name="android.intent.action.MAIN" />        <category android:name="android.intent.category.HOME" />        <category android:name="android.intent.category.DEFAULT" />        <category android:name="android.intent.category.MONKEY"/>    </intent-filter></activity>

显然Launcher是最符合条件的应用。

3、startHomeActivity()//启动launcher

ActivityStackSupervisor.java

void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {    moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);    startActivityLocked(null /* caller */, intent, null /* resolvedType */, aInfo,            null /* voiceSession */, null /* voiceInteractor */, null /* resultTo */,            null /* resultWho */, 0 /* requestCode */, 0 /* callingPid */, 0 /* callingUid */,            null /* callingPackage */, 0 /* realCallingPid */, 0 /* realCallingUid */,            0 /* startFlags */, null /* options */, false /* ignoreTargetSecurity */,            false /* componentSpecified */,            null /* outActivity */, null /* container */,  null /* inTask */);    if (inResumeTopActivity) {        // If we are in resume section already, home activity will be initialized, but not        // resumed (to avoid recursive resume) and will stay that way until something pokes it        // again. We need to schedule another resume.        scheduleResumeTopActivities();    }}

分析到这里对默认的Launcher选定的过程就有了大概的了解。设置默认启动的Launcher方法很多,我的思路是从chooseBestActivity()这个函数入手,三种方法:
(1)在Manifest.xml设置优先级属性priority默认值为0,设置的值越大优先级越高。
(2)给默认的Launcher的清单文件配置属性: android.intent.category.DEFAULT
(3)当N > 1时做出相应处理:返回要启动的桌面应用。

else if (N > 1) {   //添加代码返回需要启动的应用信息   for(int i = 0; i < N; i++){        ResolveInfo ri = query.get(i);       if (ri.activityInfo.packageName.equals("包名")) {            return ri;        }   }}
2 0