您的位置:首页 > 移动开发 > Android开发

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

2016-06-18 16:26 483 查看


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

标签: androidlauncher
2014-04-22 15:23 2987人阅读 评论(0) 收藏 举报


 分类:
 

android(37) 


版权声明:本文为博主原创文章,未经博主允许不得转载。

       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
plain copy

 





<activity  

            android:name="com.android.launcher2.Launcher"  

            android:launchMode="singleTask"  

            android:clearTaskOnLaunch="true"  

            android:stateNotNeeded="true"  

            android:theme="@style/Theme"  

            android:configChanges="mcc|mnc"  

            android:windowSoftInputMode="adjustPan"  

            android:screenOrientation="nosensor">  

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

      最终,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
plain copy

 





final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);  

mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);  

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

[java] view
plain copy

 





final PackageManager packageManager = mContext.getPackageManager();  

       下一步就是通过这个PackageManagerService.queryIntentActivities接口来查询所有Action
类型为Intent.ACTION_MAIN,并且Category类型为Intent.CATEGORY_LAUNCHER的Activity了。

[java] view
plain copy

 





final PackageManager packageManager = mContext.getPackageManager();  

  

List<ResolveInfo> apps = null;  

  

...  

...  

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
plain copy

 





for (int j=0; i<N && j<batchSize; j++) {  

     // This builds the icon bitmaps.  

     mBgAllAppsList.add(new ApplicationInfo(packageManager, apps.get(i),  

     mIconCache, mLabelCache));  

     i++;  

   

}  

[java] view
plain copy

 





final ArrayList<ApplicationInfo> added = mBgAllAppsList.added;  

mBgAllAppsList.added = new ArrayList<ApplicationInfo>();  

[java] view
plain copy

 





 if (first) {  

     callbacks.bindAllApplications(added);  

} else {  

      callbacks.bindAppsAdded(added);  

}  

     各个app的入口activity信息将会被用于构造ApplicationInfo对象。上面的new ApplicationInfo

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

[java] view
plain copy

 





public ApplicationInfo(PackageManager pm, ResolveInfo info, IconCache iconCache,  

           HashMap<Object, CharSequence> labelCache) {  

       final String packageName = info.activityInfo.applicationInfo.packageName;  

  

       this.componentName = new ComponentName(packageName, info.activityInfo.name);  

       this.container = ItemInfo.NO_ID;  

       this.setActivity(componentName,  

               Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);  

  

       try {  

           int appFlags = pm.getApplicationInfo(packageName, 0).flags;  

           if ((appFlags & android.content.pm.ApplicationInfo.FLAG_SYSTEM) == 0) {  

               flags |= DOWNLOADED_FLAG;  

  

               if ((appFlags & android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {  

                   flags |= UPDATED_SYSTEM_APP_FLAG;  

               }  

           }  

           firstInstallTime = pm.getPackageInfo(packageName, 0).firstInstallTime;  

       } catch (NameNotFoundException e) {  

           Log.d(TAG, "PackageManager.getApplicationInfo failed for " + packageName);  

       }  

  

       iconCache.getTitleAndIcon(this, info, labelCache);  

   }  

[java] view
plain copy

 





public void getTitleAndIcon(ApplicationInfo application, ResolveInfo info,  

           HashMap<Object, CharSequence> labelCache) {  

       synchronized (mCache) {  

           CacheEntry entry = cacheLocked(application.componentName, info, labelCache);  

  

           application.title = entry.title;  

           application.iconBitmap = entry.icon;  

       }  

   }  

[java] view
plain copy

 





private CacheEntry cacheLocked(ComponentName componentName, ResolveInfo info,  

           HashMap<Object, CharSequence> labelCache) {  

       if (LauncherLog.DEBUG_LAYOUT) {  

           LauncherLog.d(TAG, "cacheLocked: componentName = " + componentName  

                   + ", info = " + info + ", HashMap<Object, CharSequence>:size = "  

                   +  ((labelCache == null) ? "null" : labelCache.size()));  

       }  

  

       CacheEntry entry = mCache.get(componentName);  

       if (entry == null) {  

           entry = new CacheEntry();  

  

           mCache.put(componentName, entry);  

  

           ComponentName key = LauncherModel.getComponentNameFromResolveInfo(info);  

           if (labelCache != null && labelCache.containsKey(key)) {  

               entry.title = labelCache.get(key).toString();  

               if (LauncherModel.DEBUG_LOADERS) {  

                   LauncherLog.d(TAG, "CacheLocked get title from cache: title = " + entry.title);  

               }                  

           } else {  

               entry.title = info.loadLabel(mPackageManager).toString();  

               if (LauncherModel.DEBUG_LOADERS) {  

                   LauncherLog.d(TAG, "CacheLocked get title from pms: title = " + entry.title);  

               }                  

               if (labelCache != null) {  

                   labelCache.put(key, entry.title);  

               }  

           }  

           if (entry.title == null) {  

               entry.title = info.activityInfo.name;  

               if (LauncherModel.DEBUG_LOADERS) {  

                   LauncherLog.d(TAG, "CacheLocked get title from activity information: entry.title = " + entry.title);  

               }  

           }  

  

           entry.icon = Utilities.createIconBitmap(  

                   getFullResIcon(info), mContext);  

       }  

       return entry;  

   }  

       看来是通过在cacheLocked里调用这个createIconBitmap实现的啊。

    有了这些ApplicationInfo实例之后,就可以在桌面上展示系统中所有的应用程序了。当我们点击
“HOME"按键的时候,各个应用图标就会被显示。现在我们来看看Launcher::onClick的处理流程:

[java] view
plain copy

 





public void onClick(View v) {  

...  

...  

  

if (tag instanceof ShortcutInfo) {  

            // Open shortcut  

            final Intent intent = ((ShortcutInfo) tag).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()));  

  

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

  

            if (success && v instanceof BubbleTextView) {  

                mWaitingForResume = (BubbleTextView) v;  

                mWaitingForResume.setStayPressed(true);  

            }  

        } else if (tag instanceof FolderInfo) {  

            if (v instanceof FolderIcon) {  

                FolderIcon fi = (FolderIcon) v;  

                handleFolderClick(fi);  

            }  

        } else if (v == mAllAppsButton) {  

            if (isAllAppsVisible()) {  

                showWorkspace(true);  

            } else {  

                onClickAllAppsButton(v);  

            }  

}  

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

[java] view
plain copy

 





public void onClickAllAppsButton(View v) {  

    showAllApps(true);  

}  

[java] view
plain copy

 





void showAllApps(boolean animated) {  

...  

...  

 /// M: Call the appropriate callback for the IMTKWidget on the current page when enter all apps list.  

        mWorkspace.startCovered(mWorkspace.getCurrentPage());  

        showAppsCustomizeHelper(animated, false);  

        mAppsCustomizeTabHost.requestFocus();  

  

...  

...  

}  

     里面具体怎么画出来的,我还真不清楚。只能帮大家引路到这里了。

     当我们点击应用程序图标的时候,执行的是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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: