Launcher2分析-加载和绑定AllApp列表

上篇关联博客: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(); }
 }


猜你喜欢

转载自blog.csdn.net/b1480521874/article/details/80199574