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()

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

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里面的内容做详细的分析。

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

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

    相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源 ...

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

    ready.queue放在一块写,没有特殊的意思,仅仅是相对来说它俩可能源代码是最简单的了.ready是在dom载入完毕后.以最高速度触发,非常实用. queue是队列.比方动画的顺序触发就是通过默认 ...

  3. Tomcat源代码解析系列

    学web也有一段时间了.为了从底层了解web应用在Tomcat中的执行,决定看一下Tomcat的源代码參见<How Tomcat works>    和大牛博客.对大体架构有了一定的了解, ...

  4. Spring源代码解析

    Spring源代码解析(一):IOC容器:http://www.iteye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的启动:http://www.itey ...

  5. Arrays.sort源代码解析

    Java Arrays.sort源代码解析 Java Arrays中提供了对所有类型的排序.其中主要分为Primitive(8种基本类型)和Object两大类. 基本类型:采用调优的快速排序: 对象类 ...

  6. Spring源代码解析(收藏)

    Spring源代码解析(收藏)   Spring源代码解析(一):IOC容器:http://www.iteye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的 ...

  7. volley源代码解析(七)--终于目的之Response&lt;T&gt;

    在上篇文章中,我们终于通过网络,获取到了HttpResponse对象 HttpResponse是android包里面的一个类.然后为了更高的扩展性,我们在BasicNetwork类里面看到.Volle ...

  8. Cocos2d-x源代码解析(1)——地图模块(3)

    接上一章<Cocos2d-x源代码解析(1)--地图模块(2)> 通过前面两章的分析,我们能够知道cocos将tmx的信息结构化到 CCTMXMapInfo.CCTMXTilesetInf ...

  9. Android EventBus源代码解析 带你深入理解EventBus

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40920453,本文出自:[张鸿洋的博客] 上一篇带大家初步了解了EventBus ...

随机推荐

  1. CODEVS【3556】科技庄园

    题目描述 Description Life是codevs的用户,他是一个道德极高的用户,他积极贯彻党的十八大精神,积极走可持续发展道路,在他的不屑努力下STN终于决定让他在一片闲杂地里种桃,以亲身实践 ...

  2. using要写多少

    原文发布时间为:2009-10-25 -- 来源于本人的百度文章 [由搬家工具导入] 有时候未必要像默认的这样要using这么多。。。 using System;using System.Data;u ...

  3. android中与Adapter相关的控件----ListView

    ListView讲解: 一.ListView这个控件是一个使用非常广泛的控件,值得深入的学习和研究.基本使用已经在Adapter中使用过了 二.常用的属性和方法 footerDividersEnabl ...

  4. maven更换阿里云仓库

    本来不想写,网上到处都是,不过好多到我这不行,自己记录下,省的到处找 D:\apache-maven-3.6.1\conf目录下setting.xml文件(这是我的解压的位置) <mirrors ...

  5. poj 2299(离散化+树状数组)

    Ultra-QuickSort Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 53777   Accepted: 19766 ...

  6. hdu 5055(模拟)

    Bob and math problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Oth ...

  7. Python Challenge 第十三关

    第13关.一张电话的图片,一句话:phone that evil.看到电话,加上之前关卡有些图片有链接,我就在电话按键上都点点试试,果然 5 是个链接,就点了进去.出来一个XML文件,第一句写着:Th ...

  8. Careercup | Chapter 1

    1.1 Implement an algorithm to determine if a string has all unique characters. What if you cannot us ...

  9. EOJ Monthly 2018.7

    准备继续大学acm啦 又要开始愉快的码码码啦 第一次在华东师大OJ上面做题 看来EOJ上的积分体质是假的,我怎么一把上红??? A.数三角形 神tm的防AK题放在A,出题人很不友好啊... 先写了个暴 ...

  10. 北冥有 Data,其名为鲲,鲲之大,一个 MySQL 放不下!

    千万量级的数据,用 MySQL 要怎么存? 初学者在看到这个问题的时候,可能首先想到的是 MySQL 一张表到底能存放多少条数据? 根据 MySQL 官方文档的介绍,MySQL 理论上限是 (232) ...