android7.x Launcher3源代码解析(3)---workspace和allapps载入流程

Launcher系列目录:

一、android7.x Launcher3源代码解析(1)—启动流程

二、android7.x Launcher3源代码解析(2)—框架结构

三、android7.x Launcher3源代码解析(3)—workspace和allapps载入流程

前两篇博客分别对Lancher的启动和Launcher的框架结构进行了一些分析。这一篇。将着重開始分析界面的载入流程。

1、总体流程

先上一张总体的流程图吧。(图片看不清能够下载下来看或者右击新开个页面查看图片)

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvUGljYXNzb19M/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">

先从Launcher.java的onCreate方法開始。

protected void onCreate(Bundle savedInstanceState) {
    ......
    //建立LauncherAppState对象
    LauncherAppState.setApplicationContext(getApplicationContext());
    LauncherAppState app = LauncherAppState.getInstance();

    ......
    //建立LauncherModel对象
    mModel = app.setLauncher(this);

    //一些其它对象初始化
    ......
    setContentView(R.layout.launcher);
    setupViews();
    if (!mRestoring) {
            if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
                // If the user leaves launcher, then we should just load items asynchronously when
                // they return.
                mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
            } else {
                // We only load the page synchronously if the user rotates (or triggers a
                // configuration change) while launcher is in the foreground
                mModel.startLoader(mWorkspace.getRestorePage());
            }
    }
    ......
}

重点调用了LauncherModel的startLoader的方法,startLoader里面。最重要的就是启动了LoaderTask。mLoaderTask = new LoaderTask(mApp.getContext(), synchronousBindPage);

我们接着分析LoaderTask的run方法。

public void run() {
            ......
            keep_running: {
                if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
                loadAndBindWorkspace();

                if (mStopped) {
                    break keep_running;
                }

                waitForIdle();

                // second step
                if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
                loadAndBindAllApps();

                waitForIdle();

                // third step
                if (DEBUG_LOADERS) Log.d(TAG, "step 3: loading deep shortcuts");
                loadAndBindDeepShortcuts();
            }

            ......
        }

这里一共就几步,loadAndBindWorkspace–>waitForIdle()—>loadAndBindAllApps()—>waitForIdle()—>loadAndBindDeepShortcuts()

3步载入流程里面都穿插了waitForIdle,这种方法是干嘛的呢?

private void waitForIdle() {
          ......
            synchronized (LoaderTask.this) {
                final long workspaceWaitTime = DEBUG_LOADERS ?

SystemClock.uptimeMillis() : 0;

                mHandler.postIdle(new Runnable() {
                        public void run() {
                            synchronized (LoaderTask.this) {
                                mLoadAndBindStepFinished = true;
                                if (DEBUG_LOADERS) {
                                    Log.d(TAG, "done with previous binding step");
                                }
                                LoaderTask.this.notify();
                            }
                        }
                    });

                while (!mStopped && !mLoadAndBindStepFinished) {
                    try {
                        // Just in case mFlushingWorkerThread changes but we aren‘t woken up,
                        // wait no longer than 1sec at a time
                        this.wait(1000);
                    } catch (InterruptedException ex) {
                        // Ignore
                    }
                }
               ......
            }
        }

load数据时我们是去UI线程中处理的,UI线程正常情况下是不能堵塞的,否则有可能产生ANR。这将严重影响用户体验。

全部这里LoaderTask在将结果发送给UI线程之后,为了保证界面绑定任务能够高效的完毕,往往会将自己的任务暂停下来,等待UI线程处理完毕。

分析下这种方法:

首先。创建一个UI线程闲时运行的任务,这个任务负责设置某些关键的控制标志。并将其通过PostIdle方法增加处理器的消息队列中。一旦任务得到运行。就会将mLoadAndBindStepFinished 置为true,以控制即将来临的有条件的无限等待。 最后 设置一个有条件的无限等待,等待来自UI线程的指示。

2、workspace的载入流程

从总流程图上能够看到,workspace的载入流程主要分为loadWorkspace();bindWorkspace(mPageToBindFirst);

a、loadWorkspace()

loadWorkspace()的代码实在是太多了,这里就不全部贴出来了。主要功能就是负责从数据库表中读取数据并转译为Launcher桌面项的数据结构。

以下为步骤:

1、进行一些预处理

2、载入默认值

LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary()

之前Launcher2loadDefaultFavoritesIfNecessary这种方法,是这样载入默认布局的:

workspaceResId = sp.getInt(DEFAULT_WORKSPACE_RESOURCE_ID, R.xml.default_workspace);

可是Launcher3中,是这样载入的:

int workspaceResId = partnerRes.getIdentifier(Partner.RES_DEFAULT_LAYOUT,
                            "xml", partner.getPackageName());

这个地方,我临时还没理解。究竟默认布局是哪个?

3、初始化数据

清空之前的内存数据

/** Clears all the sBg data structures */
        private void clearSBgDataStructures() {
            synchronized (sBgLock) {
                sBgWorkspaceItems.clear();
                sBgAppWidgets.clear();
                sBgFolders.clear();
                sBgItemsIdMap.clear();
                sBgWorkspaceScreens.clear();
            }
        }

4、查询ContentProvider,返回favorites表的结果集

final HashMap<String, Integer> installingPkgs = PackageInstallerCompat
                        .getInstance(mContext).updateAndGetActiveSessionCache();

                final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
                final ArrayList<Long> restoredRows = new ArrayList<Long>();
                final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
                if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
                final Cursor c = contentResolver.query(contentUri, null, null, null, null);

5、依据不同的类型,将数据保存到相应的arrayList中。

类型包括以下这几种:

ITEM_TYPE_APPLICATION
ITEM_TYPE_SHORTCUT

ITEM_TYPE_FOLDER

ITEM_TYPE_APPWIDGET
ITEM_TYPE_CUSTOM_APPWIDGET

依据以上类型,将数据保存到一下全局变量里面

sBgItemsIdMap,sBgWorkspaceItemsm,sBgAppWidgets,sBgFolders

另外。假设有空的目录、空的屏幕,也会delete掉,终于,把全部屏幕载入进全局变量sBgWorkspaceScreens中。

......
           // Remove any empty folder
                    for (long folderId : LauncherAppState.getLauncherProvider()
                            .deleteEmptyFolders()) {
                        sBgWorkspaceItems.remove(sBgFolders.get(folderId));
                        sBgFolders.remove(folderId);
                        sBgItemsIdMap.remove(folderId);
                    }

                    ......

 sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext));

                // Remove any empty screens
                ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgWorkspaceScreens);
                for (ItemInfo item: sBgItemsIdMap) {
                    long screenId = item.screenId;
                    if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
                            unusedScreens.contains(screenId)) {
                        unusedScreens.remove(screenId);
                    }
                }

                // If there are any empty screens remove them, and update.
                if (unusedScreens.size() != 0) {
                    sBgWorkspaceScreens.removeAll(unusedScreens);
                    updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
                }

b、bindWorkspace

bindWorkspace的功能是将上面获取到的数据由Launcher显示出来。

步骤:

1、首先复制数据:

            synchronized (sBgLock) {
                workspaceItems.addAll(sBgWorkspaceItems);
                appWidgets.addAll(sBgAppWidgets);
                orderedScreenIds.addAll(sBgWorkspaceScreens);

                folders = sBgFolders.clone();
                itemsIdMap = sBgItemsIdMap.clone();
            }

2、装载数据并排序

            //装载桌面项数据
            filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
                    otherWorkspaceItems);
            //装载widget
            filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
                    otherAppWidgets);
            //装载目录
            filterCurrentFolders(currentScreenId, itemsIdMap, folders, currentFolders,
                    otherFolders);
            //排序
            sortWorkspaceItemsSpatially(currentWorkspaceItems);
            sortWorkspaceItemsSpatially(otherWorkspaceItems);

3、開始绑定

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvUGljYXNzb19M/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">

就上个流程图吧。

3、应用程序apps的载入

载入apps的主要流程。最上面那张流程图已经给出了,假设全部app没有载入,则loadAllApps();,不然直接onlyBindAllApps();

1、loadAllApps()

依据代码,画了下流程图,可是我不明确userProfile是个什么鬼?

2、onlyBindAllApps()

这里的函数比绑定workspace简单多了,直接通知Launcher绑定

            Runnable r = new Runnable() {
                public void run() {
                    final long t = SystemClock.uptimeMillis();
                    final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                    if (callbacks != null) {
                        callbacks.bindAllApplications(list);
                        callbacks.bindAllPackages(widgetList);
                    }
                    if (DEBUG_LOADERS) {
                        Log.d(TAG, "bound all " + list.size() + " apps from cache in "
                                + (SystemClock.uptimeMillis()-t) + "ms");
                    }
                }
            };

看一下Launcher的bindAllApplications函数。当然这个函数在loadAllApps函数里面也有。就是怎样绑定数据来显示呢?

Launcher.java的bindAllApplications函数里面会给AllAppsContainerView设置数据

if (mAppsView != null) {
            mAppsView.setApps(apps);
        }

再跟代码到AllAppsContainerView。

public void setApps(List<AppInfo> apps) {
        mApps.setApps(apps);
    }

这个apps在哪里用到呢?比較明显的就是onFinishInflate()函数,

.....
        // Load the all apps recycler view
        mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
        mAppsRecyclerView.setApps(mApps);
        mAppsRecyclerView.setLayoutManager(mLayoutManager);
        mAppsRecyclerView.setAdapter(mAdapter);
        mAppsRecyclerView.setHasFixedSize(true);
.....

设置给了recyclerView。非常显然,Launcher的应用程序界面就是一个自己定义的RecyclerView,给这个recyclerview绑定的adpter是AllAppsGridAdapter。看一下这个adpter的onCreateViewHolder函数,非常明显。这里依据不同的类型载入了不同的布局(应用程序界面有app和目录。头上还有个搜索框。用recyclerview的这个功能是最easy实现的),关于Recyclerview怎样能够依据不同的类型载入不同的布局,能够參考我非常久之前写的博客 RecyclerView的不同position载入不同View实现



好了,Launcher3的workspace和应用程序apps的载入流程就说到这,后面还会对Launcher里面的内容做详细的分析。

时间: 2024-10-21 19:03:28

android7.x Launcher3源代码解析(3)---workspace和allapps载入流程的相关文章

Android View体系(八)从源代码解析View的layout和draw流程

相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源代码解析Scroller Android View体系(五)从源代码解析View的事件分发机制 Android View体系(六)从源代码解析Activity的构成 Android View体系(七)从源代码解析View的measure流程 前言 上一篇文章我们讲了View的measure的流程.接

jQuery源代码解析(3)—— ready载入、queue队列

ready.queue放在一块写,没有特殊的意思,仅仅是相对来说它俩可能源代码是最简单的了.ready是在dom载入完毕后.以最高速度触发,非常实用. queue是队列.比方动画的顺序触发就是通过默认队列'fx'处理的. (本文採用 1.12.0 版本号进行解说,用 #number 来标注行号) ready 非常多时候,我们须要尽快的载入一个函数,假设里面含有操作dom的逻辑,那么最好在dom刚刚载入完毕时调用.window的load事件会在页面中的一切都载入完毕时(图像.js文件.css文件.

android7.x Launcher3源码解析(3)---workspace和allapps加载流程

Launcher系列目录: 一.android7.x Launcher3源码解析(1)-启动流程 二.android7.x Launcher3源码解析(2)-框架结构 三.android7.x Launcher3源码解析(3)-workspace和allapps加载流程 前两篇博客分别对Lancher的启动和Launcher的框架结构进行了一些分析,这一篇,将着重开始分析界面的加载流程. 1.整体流程 先上一张整体的流程图吧.(图片看不清可以下载下来看或者右击新开个页面查看图片) 先从Launc

Spring源代码解析

Spring源代码解析(一):IOC容器:http://www.iteye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的启动:http://www.iteye.com/topic/86594 Spring源代码解析(三):Spring JDBC:http://www.iteye.com/topic/87034 Spring源代码解析(四):Spring MVC:http://www.iteye.com/topic/87692 Spring源代码解析(五

Spring源代码解析(收藏)

Spring源代码解析(收藏)Spring源代码解析(一):IOC容器:http://www.iteye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的启动:http://www.iteye.com/topic/86594 Spring源代码解析(三):Spring JDBC:http://www.iteye.com/topic/87034 Spring源代码解析(四):Spring MVC:http://www.iteye.com/topic/8769

NIO框架之MINA源代码解析(二):mina核心引擎

NIO框架之MINA源代码解析(一):背景 MINA的底层还是利用了jdk提供了nio功能,mina仅仅是对nio进行封装.包含MINA用的线程池都是jdk直接提供的. MINA的server端主要有accept.processor.session三部分组成的.当中accept主要负责在指定的port监听.若有新连接则建立一个新的session.processor则负责处理session相应的发送数据和接收数据并调用上层处理:session则缓存当前连接数据. MINA採用了线程懒启动的技术,即

Android源代码解析之(三)--&amp;gt;异步任务AsyncTask

转载请标明出处:一片枫叶的专栏 上一篇文章中我们解说了android中的异步消息机制. 主要解说了Handler对象的使用方式.消息的发送流程等.android的异步消息机制是android中多任务处理的基础,Handler是整个android应用层体系异步消息传递的基础组件,通过对Handler源代码的解析的解析相信大家对android中的异步消息机制有了一个大概的了解.很多其它关于android中的异步消息机制的知识可參考我的:android源代码解析之(二)–>异步消息机制 android

Spark MLlib LDA 源代码解析

1.Spark MLlib LDA源代码解析 http://blog.csdn.net/sunbow0 Spark MLlib LDA 应该算是比較难理解的,当中涉及到大量的概率与统计的相关知识,并且还涉及到了Spark GraphX图计算方面的知识.要想明确当中的原理得要下一番功夫. LDA源代码解析前的基础知识: 1)LDA主题模型的理论知识 參照:LDA数学八卦 2)SparkGraphX 基础知识 http://blog.csdn.net/sunbow0/article/details/

thttpd源代码解析 定时器模块

thttpd源代码解析 定时器模块 thttpd是很轻量级的httpserver,可运行文件仅50kB.名称中的第一个t表示tiny, turbo, 或throttling 与lighttpd.memcached.redis相比很小巧,仅有不到8k行,而后三者大小分别为:60k,13k,86k 支持HTTP/1.1和CGI:採用IO复用实现,单线程,可移植:实现了基于URL的文件流量限制功能 特别适用于大量静态数据訪问的场景,如图片存储 2004年已经停止维护,有一个关于X-Forwarded-