上篇关联博客:Launcher2分析-加载Workspace数据和绑定
Android应用列表的视图就在launcher.xml中,也就是说应用列表视图一开始就已经加载好了,只是没有显示出来,属性为invisible,它是和Workspace在同一个viewgroup中。id为apps_customize_pane,实际类型为com.android.launcher2.AppsCustomizeTabHost,继承自TabHost,而layout apps_customize_pane.xml中包含了一个id为apps_customize_pane_content,类型为AppsCustomizePagedView的child,改类型间接继承了PagedView,是应用列表的核心view。
<include layout="@layout/apps_customize_pane" android:id="@+id/apps_customize_pane" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible" />
从LauncherModel$LoaderTask#loadAndBindApps()开始分析:
private void loadAndBindAllApps() { if (DEBUG_LOADERS) { Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded); } if (!mAllAppsLoaded) {//判断是否所有app都加载过了 loadAllAppsByBatch();//加载所有app信息并且分批绑定 synchronized (LoaderTask.this) { if (mStopped) { return; } mAllAppsLoaded = true; } } else { onlyBindAllApps();//跳过加载过程,直接绑定 } }
分析loadAllAppsByBatch():
private void loadAllAppsByBatch() { final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; // Don't use these two variables in any of the callback runnables. // Otherwise we hold a reference to them. final Callbacks oldCallbacks = mCallbacks.get(); if (oldCallbacks == null) { // This launcher has exited and nobody bothered to tell us. Just bail. Log.w(TAG, "LoaderTask running with no launcher (loadAllAppsByBatch)"); return; } final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);//需要action为main的 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);//还需要category为launcher的 //跟linux多用户有关,有多少个用户,就有多少个UserHandle,但是手机一般来说就一个吧 final List<UserHandle> profiles = mUserManager.getUserProfiles(); mBgAllAppsList.clear(); final int profileCount = profiles.size(); for (int p = 0; p < profileCount; p++) { UserHandle user = profiles.get(p); List<LauncherActivityInfo> apps = null; int N = Integer.MAX_VALUE;//这个只是初始化指,后面会改变 int startIndex; int i = 0; int batchSize = -1; while (i < N && !mStopped) {//N为apps的size if (i == 0) { final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; apps = mLauncherApps.getActivityList(null, user);//获取对应用户的所有app if (DEBUG_LOADERS) { Log.d(TAG, "queryIntentActivities took " + (SystemClock.uptimeMillis()-qiaTime) + "ms"); } if (apps == null) { return; } N = apps.size();//在这里重新赋值给N,而不会是Integer的max value if (DEBUG_LOADERS) { Log.d(TAG, "queryIntentActivities got " + N + " apps"); } if (N == 0) { // There are no apps?!? return; } if (mBatchSize == 0) { batchSize = N; } else { batchSize = mBatchSize;//设置怎么分批,一批有多少个应用。应该是太多一批可能会导致anr,导致其他事件无法被及时响应 } final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; Collections.sort(apps, new LauncherModel.ShortcutNameComparator(mLabelCache));//将apps重排序 if (DEBUG_LOADERS) { Log.d(TAG, "sort took " + (SystemClock.uptimeMillis()-sortTime) + "ms"); } } final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; startIndex = i; for (int j=0; i<N && j<batchSize; j++) {//在外围的while循环作用下,for里面的内容会循环N次 // This builds the icon bitmaps. mBgAllAppsList.add(new ApplicationInfo(apps.get(i), user, mIconCache, mLabelCache));//mBgAllAppsList不是一个List,而是一个封装了List的类,后面会分析这里 i++; } final Callbacks callbacks = tryGetCallbacks(oldCallbacks); final ArrayList<ApplicationInfo> added = mBgAllAppsList.added;//将added引用个对象发出去 final boolean firstProfile = p == 0; mBgAllAppsList.added = new ArrayList<ApplicationInfo>();//将added成员指向新的对象,因为这次是新增,那么下次就不是了 mHandler.post(new Runnable() { public void run() { final long t = SystemClock.uptimeMillis(); if (callbacks != null) { if (firstProfile) {//firstProfile应该代表的是root用户,需要加加载所有app callbacks.bindAllApplications(added);//后面会重点分析这个方法 } else { callbacks.bindAppsAdded(added);//如果是其他用户,则只加载新增内容 } if (DEBUG_LOADERS) { Log.d(TAG, "bound " + added.size() + " apps in " + (SystemClock.uptimeMillis() - t) + "ms"); } } else { Log.i(TAG, "not binding apps: no Launcher activity"); } } }); if (DEBUG_LOADERS) { Log.d(TAG, "batch of " + (i-startIndex) + " icons processed in " + (SystemClock.uptimeMillis()-t2) + "ms"); } if (mAllAppsLoadDelay > 0 && i < N) { try { if (DEBUG_LOADERS) { Log.d(TAG, "sleeping for " + mAllAppsLoadDelay + "ms"); } Thread.sleep(mAllAppsLoadDelay);//每批app的绑定任务交到主线程的队列中后,要休眠一段时间再向队列放入下一批任务 } catch (InterruptedException exc) { } } } if (DEBUG_LOADERS) { Log.d(TAG, "cached all " + N + " apps in " + (SystemClock.uptimeMillis()-t) + "ms" + (mAllAppsLoadDelay > 0 ? " (including delay)" : "")); } } }
分析一下mBgAllAppsList.add(new ApplicationInfo(apps.get(i), user, mIconCache, mLabelCache));其中mBgAllAppsList是AllAppsList类型,其中add方法的源码如下:
public void add(ApplicationInfo info) { if (findActivity(data, info.componentName, info.user)) {//当对应用户已经存在这个component时,就不会添加到data和added中 return; } data.add(info);//添加到data,存放所有app added.add(info);//添加到added,存放相对于上次加载来说为新增的app }
Launcher实现了LauncherModel的Callback,下面分析Launcher#bindAllApplications()
public void bindAllApplications(final ArrayList<ApplicationInfo> apps) { Runnable setAllAppsRunnable = new Runnable() { public void run() { if (mAppsCustomizeContent != null) { //这个mAppsCustomizeContent就是一开始说的AppsCustomizePagedView类型对象,调用setApps方法后,就会去重绘这个view了。 mAppsCustomizeContent.setApps(apps); } } }; // Remove the progress bar entirely; we could also make it GONE // but better to remove it since we know it's not going to be used View progressBar = mAppsCustomizeTabHost. findViewById(R.id.apps_customize_progress_bar); if (progressBar != null) { ((ViewGroup)progressBar.getParent()).removeView(progressBar); // We just post the call to setApps so the user sees the progress bar // disappear-- otherwise, it just looks like the progress bar froze // which doesn't look great mAppsCustomizeTabHost.post(setAllAppsRunnable);//会在progressbar消失后再post到消息队列中 } else { // If we did not initialize the spinner in onCreate, then we can directly set the // list of applications without waiting for any progress bars views to be hidden. setAllAppsRunnable.run(); } }