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

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. [Oracle] 临时将Physical Standby激活

    Oracle 10g/11g下如何将物理Standby库临时激活用于测试 在实际运营环境中, 我们经常碰到类似这样的需求: 譬如想不影响现网业务评估DB补丁在现网环境中运行的时间, 或者是想在做DB切 ...

  2. JdbcTemplate模板使用

    1.添加模板的配置文件 在spring的配置文件中加入如下代码 <bean class="org.springframework.jdbc.core.JdbcTemplate" ...

  3. centos 7 安装git并配置ssh

    一.安装 1.查看是否安装git rpm -qa|grep git 有git加版本号就说明已经安装过了 2.安装git yum install git 3.查看git版本 git version 二. ...

  4. IOS view的圆角和阴影并存

    记录一下这个简单但又难搞的问题,如何设置 view的圆角和阴影并存 UIView *v=[[UIView alloc]initWithFrame:CGRectMake(10, 10, 100, 100 ...

  5. Codeforces 777D Cloud of Hashtags(贪心)

    题目链接 Cloud of Hashtags 题目还是比较简单的,直接贪心,但是因为我有两个细节没注意,所以FST了: 1.用了cin读入,但是没有加 std::ios::sync_with_stdi ...

  6. codevs——1576 最长严格上升子序列(序列DP)

     时间限制: 1 s  空间限制: 256000 KB  题目等级 : 黄金 Gold 题解       题目描述 Description 给一个数组a1, a2 ... an,找到最长的上升降子序列 ...

  7. poj 1185 炮兵阵地 [经典状态压缩DP]

    题意:略. 思路:由于每个大炮射程为2,所以如果对每一行状态压缩的话,能对它造成影响的就是上面的两行. 这里用dp[row][state1][state2]表示第row行状态为state2,第row- ...

  8. 【转】java:多网卡环境下获取MAC地址

    http://blog.csdn.net/10km/article/details/78569962 JDK6以后 java.net.NetworkInterface提供了完整的方法用于获取网络设备信 ...

  9. ios内存管理笔记(一)

  10. linux 学习解决归档管理器打开rar和zip中文文件名乱码问题

    在ubunut下打开windows下压缩的rar文件和zip压缩文件出现中文文件名乱码的问题真的很头疼.文件名乱码其实也没有什么关系是不?至少重命名再改回来或者是使用英文命名都可以克服.不巧的是,如此 ...