Android系统默认Home(Launcher)的启动过程小结
来源:互联网 发布:合肥网络优化公司 编辑:程序博客网 时间:2024/04/26 08:32
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文件):
<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:
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
接着从mContext变量中获得PackageManagerService的接口:
final PackageManager packageManager = mContext.getPackageManager();
下一步就是通过这个PackageManagerService.queryIntentActivities接口来查询所有Action
类型为Intent.ACTION_MAIN,并且Category类型为Intent.CATEGORY_LAUNCHER的Activity了。
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了吧??
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++; }
final ArrayList<ApplicationInfo> added = mBgAllAppsList.added;mBgAllAppsList.added = new ArrayList<ApplicationInfo>();
if (first) { callbacks.bindAllApplications(added);} else { callbacks.bindAppsAdded(added);}各个app的入口activity信息将会被用于构造ApplicationInfo对象。上面的new ApplicationInfo
通过调用构造函数,将icon设置。
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); }
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; } }
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的处理流程:
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 :
public void onClickAllAppsButton(View v) { showAllApps(true);}
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。
更多源码分析,请参考:Android系统默认Home应用程序(Launcher)的启动过程源代码分析
- Android系统默认Home(Launcher)的启动过程小结
- Android系统默认Home(Launcher)的启动过程小结
- Android系统默认Home(Launcher)的启动过程小结
- Android系统默认Home(Launcher)的启动过程小结
- Android默认Home应用程序(Launcher)的启动过程学习
- Android系统默认Home应用程序(Launcher)的启动过程源代码分析
- Android系统默认Home应用程序(Launcher)的启动过程源代码分析
- Android系统默认Home应用程序(Launcher)的启动过程源代码分析
- Android系统默认Home应用程序(Launcher)的启动过程源代码分析
- Android系统默认Home应用程序(Launcher)的启动过程源代码分析
- Android系统默认Home应用程序(Launcher)的启动过程源代码分析
- Android系统默认Home应用程序(Launcher)的启动过程源代码分析
- Android系统默认Home应用程序(Launcher)的启动过程源代码分析
- Android系统默认Home应用程序(Launcher)的启动过程源代码分析
- Android系统默认Home应用程序(Launcher)的启动过程源代码分析
- Android系统默认Home应用程序(Launcher)的启动过程源代码分析
- Android系统默认Home应用程序(Launcher)的启动过程源代码分析
- Android系统默认HOME(Launcher)应用程序启动
- spark0.9.1集群模式运行graphx测试程序(LiveJournalPageRank,新增Connected Components)
- snort.conf分析(中文)
- ubuntu telnet 配置
- 寐督僖本堵苑嫡突,核冶坷籽姆!
- Python+Django+SAE系列教程10-----Django的模板
- Android系统默认Home(Launcher)的启动过程小结
- python中的字典、元组、列表
- ActionBarCompat+ViewPager搭建微信框架(一)
- .Net学员分享广州传智播客培训心得
- prim + kruskal 实现
- 关于random的一些例子
- 生成n个Txt文件
- [LeetCode] Path Sum
- SET IDENTITY_INSERT