Android系统默认Home(Launcher)的启动过程小结

来源:互联网 发布:007pipi最新域名升级 编辑:程序博客网 时间:2024/04/19 04:41
Android系统开机,各个应用是如何加载并被显示到桌面上的呢?带着这份好奇,阅读了在

Android应用程序安装过程源代码分析 一文中,我们看到应用程序的apk归档文件中的配置文件

AndroidManifest.xml 会被解析,解析得到的application,service和activity等信息保存在

PackageManagerService中。


      但是我们进入HOME界面,是要看到各个Android app的快捷图标和名称的。显示app的这些信息,

就是我们的HOME,也就是Launcher干的事情了。代码流程是从SystemServer 开始的,调用栈为:

ServerThread::run ( SystemServer.java) ——>  ActivityManagerService::main (ActivityManagerService.java)

——>ActivityManagerService::startRunning ——> ActivityManagerService::systemReady ——>

ActivityStack::resumeTopActivityLocked——>ActivityManagerService::startHomeActivityLocked

其中startHomeActivityLocked函数首先创建一个CATEGORY_HOME类型的Intent,然后通过

Intent.resolveActivityInfo函数向PackageManagerService查询Category类型为HOME的Activity。

这里只有系统自带的Launcher应用程序注册了HOME类型的Activity

(见packages/apps/Launcher2/AndroidManifest.xml文件):


[html] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. <activity 
  2.             android:name="com.android.launcher2.Launcher" 
  3.             android:launchMode="singleTask" 
  4.             android:clearTaskOnLaunch="true" 
  5.             android:stateNotNeeded="true" 
  6.             android:theme="@style/Theme" 
  7.             android:configChanges="mcc|mnc" 
  8.             android:windowSoftInputMode="adjustPan" 
  9.             android:screenOrientation="nosensor"> 
  10.             <intent-filter> 
  11.                 <actionandroid:name="android.intent.action.MAIN"/> 
  12.                 <categoryandroid:name="android.intent.category.HOME"/> 
  13.                 <categoryandroid:name="android.intent.category.DEFAULT"/> 
  14.                 <categoryandroid:name="android.intent.category.MONKEY"/> 
  15.             </intent-filter> 
  16. </activity> 

      最终,com.android.launcher2.Launcher被启动起来,其onCreate函数被调用。具体可参考

Android应用程序启动过程源代码分析  一文。 在activity start流程中,performLaunchActivity

调用。里面的mInstrumentation.callActivityOnCreate(activity, r.state); 调用的就是Instrumentation

类的callActivityOnCreate方法。调用堆栈为:ActivityThread::performLaunchActivity ——>

Instrumentation::callActivityOnCreate——>  Activity::performCreate——> onCreate(icicle);

最后这个就是创建的Launcher 这个Activity覆盖的onCreate方法。至此,Launcher.onCreate

被调用了。接下来的调用流程为:Launcher.onCreate ——>LauncherModel.startLoader ——>

LoaderTask.run ——>LoaderTask.loadAndBindAllApps ——>LoaderTask.loadAllAppsByBatch

      函数首先构造一个CATEGORY_LAUNCHER类型的Intent:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. final Intent mainIntent =new Intent(Intent.ACTION_MAIN, null); 
  2. mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 

     接着从mContext变量中获得PackageManagerService的接口:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. final PackageManager packageManager = mContext.getPackageManager(); 

       下一步就是通过这个PackageManagerService.queryIntentActivities接口来查询所有Action

类型为Intent.ACTION_MAIN,并且Category类型为Intent.CATEGORY_LAUNCHER的Activity了。

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. final PackageManager packageManager = mContext.getPackageManager(); 
  2.  
  3. List<ResolveInfo> apps = null
  4.  
  5. ... 
  6. ... 
  7. apps = packageManager.queryIntentActivities(mainIntent, 0); 

      PackageManagerService会把系统中的应用程序都解析一遍,然后把解析得到的Activity都保存在

mActivities变量中,这里通过这个mActivities变量的queryIntent函数返回符合条件intent的Activity,这里

返回的便是Action类型为Intent.ACTION_MAIN,并且Category类型为Intent.CATEGORY_LAUNCHER

的Activity了。

  终于知道我们自己写的app的入口activity为啥要设置这样的action和Category了吧??   

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. for (int j=0; i<N && j<batchSize; j++) { 
  2.      // This builds the icon bitmaps. 
  3.      mBgAllAppsList.add(new ApplicationInfo(packageManager, apps.get(i), 
  4.      mIconCache, mLabelCache)); 
  5.      i++; 
  6.   
[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. final ArrayList<ApplicationInfo> added = mBgAllAppsList.added; 
  2. mBgAllAppsList.added = new ArrayList<ApplicationInfo>(); 
[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. if (first) { 
  2.      callbacks.bindAllApplications(added); 
  3. } else
  4.       callbacks.bindAppsAdded(added); 
     各个app的入口activity信息将会被用于构造ApplicationInfo对象。上面的new ApplicationInfo

通过调用构造函数,将icon设置。

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public ApplicationInfo(PackageManager pm, ResolveInfo info, IconCache iconCache, 
  2.            HashMap<Object, CharSequence> labelCache) { 
  3.        final String packageName = info.activityInfo.applicationInfo.packageName; 
  4.  
  5.        this.componentName =new ComponentName(packageName, info.activityInfo.name); 
  6.        this.container = ItemInfo.NO_ID; 
  7.        this.setActivity(componentName, 
  8.                Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 
  9.  
  10.        try
  11.            int appFlags = pm.getApplicationInfo(packageName,0).flags; 
  12.            if ((appFlags & android.content.pm.ApplicationInfo.FLAG_SYSTEM) ==0) { 
  13.                flags |= DOWNLOADED_FLAG; 
  14.  
  15.                if ((appFlags & android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) !=0) { 
  16.                    flags |= UPDATED_SYSTEM_APP_FLAG; 
  17.                } 
  18.            } 
  19.            firstInstallTime = pm.getPackageInfo(packageName,0).firstInstallTime; 
  20.        } catch (NameNotFoundException e) { 
  21.            Log.d(TAG, "PackageManager.getApplicationInfo failed for " + packageName); 
  22.        } 
  23.  
  24.        iconCache.getTitleAndIcon(this, info, labelCache); 
  25.    } 

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public void getTitleAndIcon(ApplicationInfo application, ResolveInfo info, 
  2.            HashMap<Object, CharSequence> labelCache) { 
  3.        synchronized (mCache) { 
  4.            CacheEntry entry = cacheLocked(application.componentName, info, labelCache); 
  5.  
  6.            application.title = entry.title; 
  7.            application.iconBitmap = entry.icon; 
  8.        } 
  9.    } 

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. private CacheEntry cacheLocked(ComponentName componentName, ResolveInfo info, 
  2.            HashMap<Object, CharSequence> labelCache) { 
  3.        if (LauncherLog.DEBUG_LAYOUT) { 
  4.            LauncherLog.d(TAG, "cacheLocked: componentName = " + componentName 
  5.                    + ", info = " + info +", HashMap<Object, CharSequence>:size = " 
  6.                    +  ((labelCache == null) ?"null" : labelCache.size())); 
  7.        } 
  8.  
  9.        CacheEntry entry = mCache.get(componentName); 
  10.        if (entry == null) { 
  11.            entry = new CacheEntry(); 
  12.  
  13.            mCache.put(componentName, entry); 
  14.  
  15.            ComponentName key = LauncherModel.getComponentNameFromResolveInfo(info); 
  16.            if (labelCache != null && labelCache.containsKey(key)) { 
  17.                entry.title = labelCache.get(key).toString(); 
  18.                if (LauncherModel.DEBUG_LOADERS) { 
  19.                    LauncherLog.d(TAG, "CacheLocked get title from cache: title = " + entry.title); 
  20.                }                 
  21.            } else
  22.                entry.title = info.loadLabel(mPackageManager).toString(); 
  23.                if (LauncherModel.DEBUG_LOADERS) { 
  24.                    LauncherLog.d(TAG, "CacheLocked get title from pms: title = " + entry.title); 
  25.                }                 
  26.                if (labelCache != null) { 
  27.                    labelCache.put(key, entry.title); 
  28.                } 
  29.            } 
  30.            if (entry.title == null) { 
  31.                entry.title = info.activityInfo.name; 
  32.                if (LauncherModel.DEBUG_LOADERS) { 
  33.                    LauncherLog.d(TAG, "CacheLocked get title from activity information: entry.title = " + entry.title); 
  34.                } 
  35.            } 
  36.  
  37.            entry.icon = Utilities.createIconBitmap( 
  38.                    getFullResIcon(info), mContext); 
  39.        } 
  40.        return entry; 
  41.    } 
       看来是通过在cacheLocked里调用这个createIconBitmap实现的啊。

    有了这些ApplicationInfo实例之后,就可以在桌面上展示系统中所有的应用程序了。当我们点击

“HOME"按键的时候,各个应用图标就会被显示。现在我们来看看Launcher::onClick的处理流程:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public void onClick(View v) { 
  2. ... 
  3. ... 
  4.  
  5. if (tag instanceof ShortcutInfo) { 
  6.             // Open shortcut 
  7.             final Intent intent = ((ShortcutInfo) tag).intent; 
  8.             int[] pos = newint[2]; 
  9.             v.getLocationOnScreen(pos); 
  10.             intent.setSourceBounds(new Rect(pos[0], pos[1], 
  11.                     pos[0] + v.getWidth(), pos[1] + v.getHeight())); 
  12.  
  13.             boolean success = startActivitySafely(v, intent, tag); 
  14.  
  15.             if (success && vinstanceof BubbleTextView) { 
  16.                 mWaitingForResume = (BubbleTextView) v; 
  17.                 mWaitingForResume.setStayPressed(true); 
  18.             } 
  19.         } else if (taginstanceof FolderInfo) { 
  20.             if (v instanceof FolderIcon) { 
  21.                 FolderIcon fi = (FolderIcon) v; 
  22.                 handleFolderClick(fi); 
  23.             } 
  24.         } else if (v == mAllAppsButton) { 
  25.             if (isAllAppsVisible()) { 
  26.                 showWorkspace(true); 
  27.             } else
  28.                 onClickAllAppsButton(v); 
  29.             } 

     这里我们点击的是HOME按键,对应的是 v == mAllAppsButton这个case。且看onClickAllAppsButton :

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. public void onClickAllAppsButton(View v) { 
  2.     showAllApps(true); 
[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. void showAllApps(boolean animated) { 
  2. ... 
  3. ... 
  4. /// M: Call the appropriate callback for the IMTKWidget on the current page when enter all apps list. 
  5.         mWorkspace.startCovered(mWorkspace.getCurrentPage()); 
  6.         showAppsCustomizeHelper(animated, false); 
  7.         mAppsCustomizeTabHost.requestFocus(); 
  8.  
  9. ... 
  10. ... 
     里面具体怎么画出来的,我还真不清楚。只能帮大家引路到这里了。

     当我们点击应用程序图标的时候,执行的是tag instanceof ShortcutInfo这个case。最终通过调用

final Intent intent = ((ShortcutInfo) tag).intent; 和 boolean success = startActivitySafely(v, intent, tag);

来启动对应app的入口activity。


    Launcher的流程暂且分析到这里。我们回过头来看,总共有

(1)PackageManagerService解析app的AndroidManifest.xml。PackageManagerService将应用程序

的apk归档文件中的配置文件AndroidManifest.xml 解析,得到的application,service和activity等信息

保存在PackageManagerService中。

(2)启动Launcher这个app的入口activity,调用其onCreate方法。调用startHomeActivityLocked流程中,

向PackageManagerService查询Category类型为HOME的Activity,发现只有Launcher;接着进入

startActivity的流程。在performLaunchActivity调用Instrumentation类的callActivityOnCreate方法。

最后,调用到Launcher 这个app对应的onCreate方法。

(3)构造每个app的入口activity信息对应的ApplicationInfo对象,设置应用程序图标。

Launcher.onCreate调用流程中,通过调用PackageManagerService.queryIntentActivities接口来查询

所有Action类型为Intent.ACTION_MAIN,并且Category类型为Intent.CATEGORY_LAUNCHER的Activity。

(4)Launcher::onClick中调用onClickAllAppsButton来显示布满app的页面(HOME)。

(5)点击应用程序图标时,在Launcher::onClick中调用startActivitySafely启动该应用的入口activity。

   

    要是给咱们自己整个简单的Launcher,只需要保存各个app配置文件AndroidManifest.xml 的各个

重要信息(例如入口activity),然后通过读取配置文件,将应用程序的图标和名称读出来保存起来,

当响应HOME按键时,画出各个应用程序图标和名称等信息。当点击应用程序图标时,获取其

入口Activity等信息,调用startActivity等函数去启动入口Activity。



  更多源码分析,请参考:Android系统默认Home应用程序(Launcher)的启动过程源代码分析

0 0
原创粉丝点击