上篇关联博客: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。
从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 profiles = mUserManager.getUserProfiles();
mBgAllAppsList.clear();
final int profileCount = profiles.size();
for (int p = 0; p < profileCount; p++) {
UserHandle user = profiles.get(p);
List 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 added = mBgAllAppsList.added;//将added引用个对象发出去
final boolean firstProfile = p == 0;
mBgAllAppsList.added = new ArrayList();//将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 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(); }
}