Android之Launcher分析和修改4——初始化加载数据

上面一篇文章说了Launcher是如何被启动的,Launcher启动的过程主要是加载界面数据然后显示出来,

界面数据都是系统APP有关的数据,都是从Launcher的数据库读取,下面我们详细分析Launcher如何加载数据。

在Launcher.java的onCreate()方法里面,调用了开始加载数据接口:

//Edited by mythou
//http://www.cnblogs.com/mythou/
//加载启动数据
if (!mRestoring) 
{mModel.startLoader(this, true);
}

mModel是LauncherModel的对象,由此可见,数据加载主要是在LauncherModel类里面实现的。

 

1、Callbacks接口

LauncherModel里面,需要先分析一个Callbacks接口。

//Edited by mythou
//http://www.cnblogs.com/mythou/public interface Callbacks {public boolean setLoadOnResume();public int getCurrentWorkspaceScreen();public void startBinding();public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);public void bindFolders(HashMap<Long,FolderInfo> folders);public void finishBindingItems();public void bindAppWidget(LauncherAppWidgetInfo info);public void bindAllApplications(ArrayList<ApplicationInfo> apps);public void bindAppsAdded(ArrayList<ApplicationInfo> apps);public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent);public void bindPackagesUpdated();public boolean isAllAppsVisible();public void bindSearchablesChanged();}
复制代码

Callbacks接口提供了很多接口,用于返回相关的数据给Launcher模块,下面我们对每个接口作用做个阐释。

setLoadOnResume() :当Launcher.java类的Activity处于onPause的时候,如果重新恢复,需要调用onResume,此时需要在onResume调用这个接口,恢复Launcher数据。

getCurrentWorkspace():获取屏幕序号(0~4)

startBinding():通知Launcher开始加载数据。清空容器数据,重新加载

bindItems(ArrayList<ItemInfo> shortcuts, int start, int end):加载App shortcut、Live Folder、widget到Launcher相关容器。

bindFolders(HashMap<Long, FolderInfo> folders):加载folder的内容

finishBindingItems():数据加载完成。

bindAppWidget(LauncherAppWidgetInfo item):workspace加载APP 快捷方式

bindAllApplications(final ArrayList<ApplicationInfo> apps):所有应用列表接着APP图标数据

bindAppsAdded(ArrayList<ApplicationInfo> apps):通知Launcher新安装了一个APP,更新数据。

bindAppsUpdated(ArrayList<ApplicationInfo> apps):通知Launcher一个APP更新了。(覆盖安装)

bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent):通知Launcher,应用被删除

bindPackagesUpdated():多个应用更新。

isAllAppsVisible():返回所有应用列表是否可见状态。

bindSearchablesChanged():Google搜索栏或者删除区域发生变化时通知Launcher

 

2、数据加载流程

Launcher.java类继承了Callbacks接口,并实现了该接口。LauncherModel里面会调用这些接口,反馈数据和状态给Launcher。数据加载总体分为两部分,一部分是加载workspace的数据,另一部分是加载All APP界面的数据。

下面是一个加载数据流程图:


3、startLoader()

下面我们先分析startLoader()接口,startLoader主要是启动了一个线程,用于加载数据。

//Edited by mythou
//http://www.cnblogs.com/mythou/   public void startLoader(Context context, boolean isLaunching) {synchronized (mLock) {//...............if (mCallbacks != null && mCallbacks.get() != null) {isLaunching = isLaunching || stopLoaderLocked();mLoaderTask = new LoaderTask(context, isLaunching);sWorkerThread.setPriority(Thread.NORM_PRIORITY);sWorker.post(mLoaderTask); }}}

startLoader主要是启动LoaderTask线程里面的run方法。sWorker是一个Handle对象,用于启动线程的run方法。

 

 4、LoaderTask的run()方法

//Edited by mythou
//http://www.cnblogs.com/mythou/public void run() {//............keep_running: {//...............//加载当前页面的数据,先把一页的数据加载完成,//主要是为了增加程序流畅性,提高用户体验if (loadWorkspaceFirst) {if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");loadAndBindWorkspace();} else {if (DEBUG_LOADERS) Log.d(TAG, "step 1: special: loading all apps");loadAndBindAllApps();}if (mStopped) {break keep_running;}// THREAD_PRIORITY_BACKGROUND设置线程优先级为后台,//这样当多个线程并发后很多无关紧要的线程分配的CPU时间将会减少,有利于主线程的处理synchronized (mLock) {if (mIsLaunching) {if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);}}//等待线程空闲的时候,继续加载其他页面数据waitForIdle();//加载剩余页面的数据,包含workspace和all app页面if (loadWorkspaceFirst) {if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");loadAndBindAllApps();} else {if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace");loadAndBindWorkspace();}// Restore the default thread priority after we are done loading itemssynchronized (mLock) {android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);}}}
复制代码


上面是经过简化的LoaderTask的run方法代码,其实主要就两部分操作,第一部分操作,加载当前页面的数据

(当前workspace页面或者当前All APP页面的数据)然后等待线程空闲的时候,再加载剩余的页面数据。

代码上面加了关键注释,可以结合代码分析。这样做主要目的是增加Launcher启动的速度,让用户觉得系统初始化速度

较快,有较好的用户体验。先把用户看见的界面初始化完毕,然后再开一个后台线程慢慢加载其他的数据。

下面我们分别分析workspace和All APP加载和绑定。

 

5、workspace加载数据

loadAndBindWorkspace()方法主要就是执行loadWorkspace()和 bindWorkspace()方法。
下面分别对这两个方法进行分析。
//Edited by mythou
//http://www.cnblogs.com/mythou/     private void loadWorkspace() {//..........//清空容器,存放界面不同的元素,App快捷方式、widget、foldersynchronized (sBgLock) {sBgWorkspaceItems.clear();sBgAppWidgets.clear();sBgFolders.clear();sBgItemsIdMap.clear();sBgDbIconCache.clear();final ArrayList<Long> itemsToRemove = new ArrayList<Long>();final Cursor c = contentResolver.query(LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);// +1 for the hotseat (it can be larger than the workspace)// Load workspace in reverse order to ensure that latest items are loaded first (and// before any earlier duplicates)//表示屏幕上的位置,//第一维表示分屏的序号,其中最后一个代表Hotseat             //第二维表示x方向方格的序号         //第三维表示y方向方格的序号final ItemInfo occupied[][][] =new ItemInfo[Launcher.SCREEN_COUNT + 1][mCellCountX + 1][mCellCountY + 1];//读取数据库响应键值列序号try {final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);//...........while (!mStopped && c.moveToNext()) {try {int itemType = c.getInt(itemTypeIndex);switch (itemType) {//item类型为ITEM_TYPE_APPLICATION或者ITEM_TYPE_SHORTCUT  //container为CONTAINER_DESKTOP或者CONTAINER_HOTSEAT//把当前的item添加到sWorkspaceItems中case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:emType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {info = getShortcutInfo(manager, intent, context, c, iconIndex,titleIndex, mLabelCache);} else {info = getShortcutInfo(c, context, iconTypeIndex,iconPackageIndex, iconResourceIndex, iconIndex,titleIndex);switch (container) {case LauncherSettings.Favorites.CONTAINER_DESKTOP:case LauncherSettings.Favorites.CONTAINER_HOTSEAT://添加数据sBgWorkspaceItems.add(info);break;default://如果item的属性是folder,添加到folder,创建forderFolderInfo folderInfo =findOrMakeFolder(sBgFolders, container);folderInfo.add(info);break;}sBgItemsIdMap.put(info.id, info);} else {}break;//item类型为文件夹,添加case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:id = c.getLong(idIndex);FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);//.........sBgItemsIdMap.put(folderInfo.id, folderInfo);sBgFolders.put(folderInfo.id, folderInfo);break;//Widget添加case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:// Read all Launcher-specific widget detailsint appWidgetId = c.getInt(appWidgetIdIndex);id = c.getLong(idIndex);//...........sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);sBgAppWidgets.add(appWidgetInfo);break;}} catch (Exception e) {Log.w(TAG, "Desktop items loading interrupted:", e);}}} finally {c.close();}}}

workspace的数据加载总的来说也是按照元素属性来区分加载,分为App快捷方式、Widget、Folder元素。

这几个元素分别加载到不同的容器里面。其中sItemsIdMap保存所有元素的id和ItemInfo组成的映射。其他

元素分别加载到3个不同的容器里面,用于后面绑定数据用。这里只给出了loadWorkspace的流程代码,详细代码,

需要看源码,还有很多细节。不过刚开始分析Launcher,我的原则是先把握整体流程和知道改动代码,需要在哪里查找。

 

6、workspace绑定数据

Launcher的内容绑定分为五步:分别对应着startBinding()、bindItems()、bindFolders()、 bindAppWidgets()、

finishBindingItems()的调用。下面针对bindWorkspace做个简单的流程分析。

//Edited by mythou
//http://www.cnblogs.com/mythou/ 
private void bindWorkspace() {//通知Launcher开始绑定数据mHandler.post(new Runnable() {public void run() {Callbacks callbacks = tryGetCallbacks(oldCallbacks);if (callbacks != null) {//绑定数据到launcher,Launcher回调,清空相关容器 OWLcallbacks.startBinding();}}});//添加元素到workspace,主要是添加APP快捷方式N = workspaceItems.size();for (int i=0; i<N; i+=ITEMS_CHUNK) {final int start = i;final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);mHandler.post(new Runnable() {public void run() {Callbacks callbacks = tryGetCallbacks(oldCallbacks);if (callbacks != null) {callbacks.bindItems(workspaceItems, start, start+chunkSize);}}});}//文件夹绑定final HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>(sFolders);mHandler.post(new Runnable() {public void run() {Callbacks callbacks = tryGetCallbacks(oldCallbacks);if (callbacks != null) {callbacks.bindFolders(folders);}}});//分两次加载widget ,当前界面和其他界面,增强用户体验OWL//其他页面widget会在后台线程再次加载final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen();N = sAppWidgets.size();// once for the current screenfor (int i=0; i<N; i++) {final LauncherAppWidgetInfo widget = sAppWidgets.get(i);if (widget.screen == currentScreen) {mHandler.post(new Runnable() {public void run() {Callbacks callbacks = tryGetCallbacks(oldCallbacks);if (callbacks != null) {callbacks.bindAppWidget(widget);}}});}}//加载其他看不见的屏幕widgetfor (int i=0; i<N; i++) {final LauncherAppWidgetInfo widget = sAppWidgets.get(i);if (widget.screen != currentScreen) {mHandler.post(new Runnable() {public void run() {Callbacks callbacks = tryGetCallbacks(oldCallbacks);if (callbacks != null) {callbacks.bindAppWidget(widget);}}});}}//加载完成,通知Launcher,已经完成数据加载mHandler.post(new Runnable() {public void run() {Callbacks callbacks = tryGetCallbacks(oldCallbacks);if (callbacks != null) {callbacks.finishBindingItems();}}});}

 上面就是Launcher的workspace绑定数据的过程,跟加载数据过程很相似,也是区分3中类型的元素进行加载。

下面我们总结一下,workspace的加载和绑定数据的过程。我们现在回头看,可以发现,其实workspace里面就是

存放了3中数据ItemInfo、FolderInfo、LauncherAppWidgetInfo。分别对应我们的APP快捷方式、文件夹、Widget

数据。其中FolderInfo、LauncherAppWidgetInfo都是继承了ItemInfo。数据加载过程,就是从Launcher的数据库

读取数据然后按元素属性分别放到3个ArrayList里面。绑定数据过程就是把3个ArrayList的队列关联到Launcher界面里面。

 7、ALL APP数据加载绑定

//Edited by mythou
//http://www.cnblogs.com/mythou/    private void loadAllAppsByBatch() {//只有这两个标记才需要显示在所有程序列表 OWLfinal Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);final PackageManager packageManager = mContext.getPackageManager();List<ResolveInfo> apps = null;int N = Integer.MAX_VALUE;int startIndex;int i=0;int batchSize = -1;while (i < N && !mStopped) {if (i == 0) {mAllAppsList.clear();final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;//过滤需要显示的appapps = packageManager.queryIntentActivities(mainIntent, 0);if (DEBUG_LOADERS) {Log.d(TAG, "queryIntentActivities took "+ (SystemClock.uptimeMillis()-qiaTime) + "ms");}if (apps == null) {return;}N = apps.size();if (DEBUG_LOADERS) {Log.d(TAG, "queryIntentActivities got " + N + " apps");}if (N == 0) {// There are no apps?!?return;}//mBatchSize==0表示一次性加载所有的应用  if (mBatchSize == 0) {batchSize = N;} else {batchSize = mBatchSize;}}final boolean first = i <= batchSize;final Callbacks callbacks = tryGetCallbacks(oldCallbacks);final ArrayList<ApplicationInfo> added = mAllAppsList.added;mAllAppsList.added = new ArrayList<ApplicationInfo>();//绑定加载所有的APP数据mHandler.post(new Runnable() {public void run() {final long t = SystemClock.uptimeMillis();if (callbacks != null) {if (first) {//一次性加载所以app,返回数据到launchercallbacks.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");}}});
}

AllAPP的数据加载和绑定跟workspace的差不多,也是先加载数据然后绑定数据,通知Launcher。加载数据的时候

PackageManager获取所有已经安装的APK包信息,然后过滤只包含需要显示在所有应用列表的应用,需要包含

ACTION_MAIN和CATEGORY_LAUNCHER两个属性。这个我们在编写应用程序的时候都应该知道。

AllAPP加载跟workspace不同的地方是加载的同时,完成数据绑定的操作,也就是说第一次加载AllAPP页面的数据,

会同时绑定数据到Launcher。第二次需要加载的时候,只会把数据直接绑定到Launcher,而不会重新搜索加载数据。

Launcher启动加载和绑定数据就是这样完成。绑定完数据,Launcher就可以运行。





本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/292920.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图像处理技术之分辨率与压缩

一 图像分辨率数码图像有两大类&#xff0c;一类是矢量图&#xff0c;也叫向量图&#xff1b;另一类是点阵图&#xff0c;也叫位图。矢量图比较简单&#xff0c;它是由大量数学方程式创建的&#xff0c;其图形是由线条和填充颜色的块面构成的&#xff0c;而不是由像素组成的&am…

当代年轻人,都有些不成文的规定?

全世界只有3.14 % 的人关注了爆炸吧知识面对海量信息&#xff0c;我们不可能有时间去一一筛选&#xff0c;导致我们看到的总是局部。包括公众号&#xff0c;看似可以随便关注&#xff0c;但是你的选择其实是有限空间的。你可以关注更多资讯、干货十足的公号主动和别人分享有趣的…

未检测到正确安装的网络适配器_电脑网络适配器有感叹号怎么解决?

今天有位女性乎友问&#xff1a;我家的电脑之前一直没装宽带&#xff0c;主要是家人怕影响学习&#xff0c;不过上周就涨了&#xff0c;电脑终于连上网线&#xff0c;但没办法上网&#xff0c;还发现电脑设备管理器中网络适配器出现黄色感叹号。对于电脑的网络适配器感叹号问题…

使用Spectre.Console创建漂亮的控制台应用程序

前言你是否厌倦了控制台应用程序默认的简陋界面&#xff1f;这时&#xff0c;你可以试试引用Nuget包Spectre.Console。打印使用帮助首先&#xff0c;我们可以设置控制台应用程序可以执行的命令&#xff1a;static async Task<int> Main(string[] args) {var app new Com…

Android之ComponentName的用法

ComponentName(组件名称)是用来打开其他应用程序中的Activity或服务的、 ComponentName,顾名思义,就是组件名称,通过调用Intent中的setComponent方法,我们可以打开另外一个应用中的Activity或者服务。 实例化一个ComponentName需要两个参数,第一个参数是要启动应用的包…

Linux入门之进程管理(4)之进程与文件

Linux入门之进程管理(4)之进程与文件前面使用进程相关命令管理工具都是根据进程编号或者进程名称及其其它属性信息来查看和处理相关进程的&#xff0c;但是在某些情况下&#xff0c;想要查看某个文件或者某个设备被哪些进程所使用&#xff0c;使用ps、pgrep等命令查询的是不够准…

初识MASA Blazor

MASA Blazor是一个Blazor的UI组件库。就像大家写前端熟知的Bootstrap, Ant Design一样。MASA Blazor官网地址&#xff1a;https://blazor.masastack.comMASA Blazor Github地址&#xff1a;https://github.com/BlazorComponent/MASA.BlazorMASA Blazor Pro演示地址&#xff1a;…

win7技巧

win7鼠标右键菜单清理 win7右键菜单设置 |浏览&#xff1a;7659|更新&#xff1a;2014-03-04 14:23|标签&#xff1a; windows7 鼠标 1234567分步阅读虽然我们现在打开某个软件都是双击解决完事&#xff0c;但是偶尔我们需要右键属性的时候&#xff0c;可以看到右键菜单拖拖拉拉…

基于Python的电商平台淘宝商品评论数据采集与分析

引言 在电商竞争日益激烈的情况下&#xff0c;商家既要提高产品质量&#xff0c;又要洞悉客户的想法和需求&#xff0c;关注客户购买商品后的评论&#xff0c;而第三方API接口商家获取商品评价主要依赖于人工收集&#xff0c;不但效率低&#xff0c;而且准确度得不到保障。通过…

《Pyflink》Flink集群安装,Python+Flink调研

Flink集群安装&#xff0c;PythonFlink调研 Flink集群部署 下载对应版本安装包&#xff1a;https://flink.apache.org/downloads.html 实验环境为hadoop2.7&#xff0c; scala2.11 所以下载flink-1.7.1-bin-hadoop27-scala_2.11.tgz 配置conf/flink-conf.yaml jobmanager.rpc…

hdu 4539(状压dp)

题目链接&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid4539 思路&#xff1a;跟poj1185简直就是如出一辙&#xff01; 1 #include<iostream>2 #include<cstdio>3 #include<cstring>4 #include<algorithm>5 using namespace std;6 7 int row…

万字长文入门 Redis 命令、事务、锁、订阅、性能测试

Redis 基本数据类型Redis 中&#xff0c;常用的数据类型有以下几种&#xff1a;String&#xff1a;字符串类型&#xff0c;二进制安全字符串&#xff1b;Hash&#xff1a;哈希表&#xff1b;List 列表&#xff1a;链表结构&#xff0c;按照插入顺序排序的字符串元素的集合&…

基于应用日志的扫描器检测实践

基于应用日志的扫描器检测实践 在网络上搜索web扫描器时&#xff0c;各类扫描器工具、扫描攻略玲琅满目&#xff0c;但对扫描器检测方法的内容却少之又少。因此&#xff0c;本文对各类web扫描特特征进行了梳理和总结&#xff0c;并结合苏宁应用防火墙&#xff08;SNWAF&#x…

将一个压缩文件分成多个压缩文件;RAR文件分卷

有时候需要上传压缩文件&#xff0c;但是限制了单个文件的大小&#xff0c;那我们怎么才能将一个比较大的压缩文件分割成多个压缩文件&#xff0c;从而符合要求的进行文件的上传呢&#xff1f;这里小编告诉你一个技巧。 工具/原料 电脑 winrar&#xff08;一般电脑都自带了&…

python画相关性可视化图_Python可视化很简单,一文教你绘制饼图、极线图和气泡图...

matplotlib库作为Python数据化可视化的最经典和最常用库&#xff0c;掌握了它就相当于学会了Python的数据化可视化&#xff0c;上次呢&#xff0c;已经和大家聊了关于柱状图、条形图和直方图相关的东东&#xff0c;相信大家已经掌握了哈&#xff0c;那今天呢&#xff0c;咱们再…

图像处理技术(二)滤波去噪

在图像处理领域中&#xff0c;在真正的应用过程前&#xff0c;通常需要对图像进行预先处理&#xff0c;达到去除干扰项的目的。滤波去噪就是其中的一项图像预处理工作。在.NET下常用OpenCV进行图像处理工作,常用的.NET下的OpenCV库有Emgu CV和OpenCVSharp。EmguCV是.NET平台下对…