桌面应用图标流程

前言

本人工作上碰到这么一个需求,开发一款滤镜引擎,将桌面上所有的图标进行统一的滤镜化,这就需要了解一下整个桌面去取图标的过程,了解了整个过程,找到真正拿图标的地方,在真正取图标的地方将图片进行替换,或者滤镜化,之前分析情况,现在整理下,与大家分享。本文所用的代码,是基于Android 5.1

桌面组件介绍

一级菜单

WorkSpace:他是一个ViewGroup,要想在桌面上显示东西,就得往这个ViewGroup里添加自己的View

BubbleTextView:他是一个TextView,上方是图标,下方是名称,在桌面上的图标都是由这个类表示

FolderIcon:他也是一个ViewGroup,用来表示桌面上的文件夹图标,里面添加了缩略处理过的bitmap,他的背景图片就是文件夹的形状

HotSeat: 他是个FrameLayout,是桌面下方的固定快捷区,包含了几个常用的图标,中间的AllApp按钮是固定位置,也是一个TextView

抽屉页面 组件

PagedView:他是一个viewgroup,代表进入抽屉页后的界面,应用图标需要添加到这个viewgoup里面才能显示,一个或几个PagedView 承载了手机上所有的应用图标

PagedViewIcon:他是一个TextView,和BubblTextView一样,只是在抽屉容器里换了个名字

桌面加载图标流程

先来看一张流程图

桌面Activity 也就是Launcher.java 类,该类里面维护了一个 LauncherModel,该对象会在onCreate 方法中去调用startLoader() 方法,

下面看一下startLoader() 方法的源码,

public void startLoader(boolean isLaunching, int synchronousBindPage) { synchronized (mLock) { if (DEBUG_LOADERS) { Log.d(TAG, "startLoader isLaunching=" + isLaunching); } // Clear any deferred bind-runnables from the synchronized load process // We must do this before any loading/binding is scheduled below. mDeferredBindRunnables.clear(); // Don't bother to start the thread if we know it's not going to do anything if (mCallbacks != null && mCallbacks.get() != null) { // If there is already one running, tell it to stop. // also, don't downgrade isLaunching if we're already running isLaunching = isLaunching || stopLoaderLocked(); // 这搞了一个异步任务去加载 mLoaderTask = new LoaderTask(mApp.getContext(), isLaunching); if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) { mLoaderTask.runBindSynchronousPage(synchronousBindPage); } else { sWorkerThread.setPriority(Thread.NORM_PRIORITY); sWorker.post(mLoaderTask); } } } }

我们看到,这里面有个关键的类,loaderTask,见名只义,可以猜到这里面会起一个线程,去加载一些资源。看看里面去加载什么

LoaderTask.java

可以看到LoaderTask实现了Runnable接口,直接去看该类的run() 方法

public void run() { boolean isUpgrade = false; synchronized (mLock) { mIsLoaderTaskRunning = true; } // OpTImize for end-user experience: if the Launcher is up and // running with the // All Apps interface in the foreground, load All Apps first. Otherwise, load the // workspace first (default). keep_running: { // Elevate priority when Home launches for the first TIme to avoid // starving at boot TIme. Staring at a blank home is not cool. synchronized (mLock) { if (DEBUG_LOADERS) Log.d(TAG, "SetTIng thread priority to " + (mIsLaunching ? "DEFAULT" : "BACKGROUND")); android.os.Process.setThreadPriority(mIsLaunching ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND); } if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace"); //加载一级菜单的方法 isUpgrade = loadAndBindWorkspace(); if (mStopped) { break keep_running; } // Whew! Hard work done. Slow us down, and wait until the UI thread has // settled down. synchronized (mLock) { if (mIsLaunching) { if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND"); android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } } waitForIdle(); // second step if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps"); //加载二级菜单里面的方法 loadAndBindAllApps(); // Restore the default thread priority after we are done loading items synchronized (mLock) { android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); } } // Update the saved icons if necessary if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons"); synchronized (sBgLock) { for (Object key : sBgDbIconCache.keySet()) { updateSavedIcon(mContext, (ShortcutInfo) key, sBgDbIconCache.get(key)); } sBgDbIconCache.clear(); } if (AppsCustomizePagedView.DISABLE_ALL_APPS) { // Ensure that all the applications that are in the system are // represented on the home screen. if (!UPGRADE_USE_MORE_APPS_FOLDER || !isUpgrade) { verifyApplications(); } } // Clear out this reference, otherwise we end up holding it until all of the // callback runnables are done. mContext = null; synchronized (mLock) { // If we are still the last one to be scheduled, remove ourselves. if (mLoaderTask == this) { mLoaderTask = null; } mIsLoaderTaskRunning = false; } }

可以看到在该类中主要有两个方法,

loadAndBindWorkSpace(), WorkSpace是一级菜单里面的容器类,该方法是加载一及菜单的方法

loadAndBindAllapp() ,这是抽屉内二级菜单的加载方法

下面着重分析下这两个方法的加载流程

loadAndBindWorkSpace()

这里加载主要分为两个流程一个是 loadWorkSpace 另一个是 bindWorkSpace,可以看下源代码

private boolean loadAndBindWorkspace() { mIsLoadingAndBindingWorkspace = true; // Load the workspace if (DEBUG_LOADERS) { Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded); } boolean isUpgradePath = false; if (!mWorkspaceLoaded) { isUpgradePath = loadWorkspace(); synchronized (LoaderTask.this) { if (mStopped) { return isUpgradePath; } mWorkspaceLoaded = true; } } // Bind the workspace bindWorkspace(-1, isUpgradePath); return isUpgradePath; }

可以看到并没有直接去加载,而是先判断了一些条件,然后去加载

loadWorkSpace() 方法比较长大概分为三步,

初始化后面要用到的对象实例

final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; final Context context = mContext; final ContentResolver contentResolver = context.getContentResolver(); final PackageManager manager = context.getPackageManager(); final AppWidgetManager widgets = AppWidgetManager.getInstance(context); final boolean isSafeMode = manager.isSafeMode(); LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); int countX = (int) grid.numColumns; int countY = (int) grid.numRows;

加载默认配置,并保存数据库中

synchronized public void loadDefaultFavoritesIfNecessary(int origWorkspaceResId) { String spKey = LauncherAppState.getSharedPreferencesKey(); SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE); if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) { int workspaceResId = origWorkspaceResId; // Use default workspace resource if none provided //如果workspaceResId=0,就会加载默认的配置(default_workspace_xxx.xml),并保存到数据库中 if (workspaceResId == 0) { workspaceResId = sp.getInt(DEFAULT_WORKSPACE_RESOURCE_ID, R.xml.default_workspace); } // Populate favorites table with initial favorites SharedPreferences.Editor editor = sp.edit(); editor.remove(EMPTY_DATABASE_CREATED); if (origWorkspaceResId != 0) { editor.putInt(DEFAULT_WORKSPACE_RESOURCE_ID, origWorkspaceResId); } mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), workspaceResId); mOpenHelper.setFlagJustLoadedOldDb(); editor.commit(); } }

读取数据库,获取需要加载的应用快捷方式

此段代码较多,就是去读取数据库的一些操作,具体过程是根据一些不同的type 存到不同的list中。

bindWorkSpace()

应用信息读取完之后,刚才的几个变量中就存储了该信息,然后将其绑定到workspace中去,这个过程也是很复杂的

创建局部变量,将全局变量的信息复制过来,单独进行操作

ArrayList workspaceItems = new ArrayList(); ArrayList appWidgets = new ArrayList(); HashMapfolders = new HashMap(); HashMapitemsIdMap = new HashMap(); ArrayListorderedScreenIds = new ArrayList(); synchronized (sBgLock) { workspaceItems.addAll(sBgWorkspaceItems); appWidgets.addAll(sBgAppWidgets); folders.putAll(sBgFolders); itemsIdMap.putAll(sBgItemsIdMap); orderedScreenIds.addAll(sBgWorkspaceScreens); } final boolean isLoadingSynchronously = synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE; int currScreen = isLoadingSynchronously ? synchronizeBindPage : oldCallbacks.getCurrentWorkspaceScreen(); if (currScreen >= orderedScreenIds.size()) { // There may be no workspace screens (just hotseat items and an empty page). currScreen = PagedView.INVALID_RESTORE_PAGE; } final int currentScreen = currScreen;// 当前screen final long currentScreenId = currentScreen < 0 ? INVALID_SCREEN_ID : orderedScreenIds.get(currentScreen);// 当前screen id // Load all the items that are on the current page first (and in the process, unbind // all the existing workspace items before we call startBinding() below. unbindWorkspaceItemsOnMainThread();// 先解除绑定

根据item中的screenID将items分成当前screen和其他screen,并进行排序

// Separate the items that are on the current screen, and all the other remaining items ArrayList currentWorkspaceItems = new ArrayList();// 存放当前workspace上的items ArrayList otherWorkspaceItems = new ArrayList();// 存放除当前workspace之外的items ArrayList currentAppWidgets = new ArrayList();// 存放当前workspace上的appwidgets ArrayList otherAppWidgets = new ArrayList();// 存放除当前workspace之外的appwidgets HashMapcurrentFolders = new HashMap();// 存放当前workspace上的folder HashMapotherFolders = new HashMap();// 存放除当前workspace之外的folder filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems, otherWorkspaceItems);// 过滤items,区分当前screen和其他screen上的items filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets, otherAppWidgets);// 同上 filterCurrentFolders(currentScreenId, itemsIdMap, folders, currentFolders, otherFolders);// 同上 sortWorkspaceItemsSpatially(currentWorkspaceItems);// 对workspace上的items进行排序,按照从上到下和从左到右的顺序 sortWorkspaceItemsSpatially(otherWorkspaceItems);// 同上

runnable执行块,告诉workspace要开始绑定items了,startBinding方法在Launcher中实现,做一些清除工作

// Tell the workspace that we're about to start binding items r = new Runnable() { public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.startBinding(); } } }; runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);

可以看一下实现类launcher中startBinding()方法

public void startBinding() { // If we're starting binding all over again, clear any bind calls we'd postponed in // the past (see waitUntilResume) -- we don't need them since we're starting binding // from scratch again mBindOnResumeCallbacks.clear(); // Clear the workspace because it's going to be rebound mWorkspace.clearDropTargets(); mWorkspace.removeAllWorkspaceScreens(); mWidgetsToAdvance.clear(); if (mHotseat != null) { mHotseat.resetLayout(); } }

可以看到主要做的是清除和重置工作

绑定workspace screen

bindWorkspaceScreens(oldCallbacks, orderedScreenIds); 具体实现在launcher中

Workspace绑定完成之后,就是将items、widgets和folders放到上面去

loadAndBindAllapp()

可以看到加载过程也是分为两步:如果所有app已经加载过了,就只需要绑定就行了,否则的话,加载所有app,第一次启动肯定是加载所有的,我们按照这种情况来分析

1.获取需要显示到Launcher中的app列表,创建app图标

private void loadAndBindAllApps() { if (DEBUG_LOADERS) { Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded); } if (!mAllAppsLoaded) { loadAllApps(); synchronized (LoaderTask.this) { if (mStopped) { return; } mAllAppsLoaded = true; } } else { onlyBindAllApps(); } }

loadAllApps()

final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; 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 (loadAllApps)"); return; } final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); final List profiles = mUserManager.getUserProfiles(); // Clear the list of apps mBgAllAppsList.clear();// 清除所有app列表 SharedPreferences prefs = mContext.getSharedPreferences( LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE); for (UserHandleCompat user : profiles) { // Query for the set of apps final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; List apps = mLauncherApps.getActivityList(null, user);// 获取需要显示在Launcher上的activity列表 if (DEBUG_LOADERS) { Log.d(TAG, "getActivityList took " + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user); Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user); } // Fail if we don't have any apps // TODO: Fix this. Only fail for the current user. if (apps == null || apps.isEmpty()) {// 没有需要显示的,直接返回 return; } // Sort the applications by name final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; Collections.sort(apps, new LauncherModel.ShortcutNameComparator(mLabelCache));// 排序 if (DEBUG_LOADERS) { Log.d(TAG, "sort took " + (SystemClock.uptimeMillis()-sortTime) + "ms"); } // Create the ApplicationInfos for (int i = 0; i < apps.size(); i++) { LauncherActivityInfoCompat app = apps.get(i); // This builds the icon bitmaps. mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, mLabelCache));// 创建应用图标对象,并添加到所有APP列表中 } if (ADD_MANAGED_PROFILE_SHORTCUTS && !user.equals(UserHandleCompat.myUserHandle())) { // Add shortcuts for packages which were installed while launcher was dead. String shortcutsSetKey = INSTALLED_SHORTCUTS_SET_PREFIX + mUserManager.getSerialNumberForUser(user); Set packagesAdded = prefs.getStringSet(shortcutsSetKey, Collections.EMPTY_SET); HashSet newPackageSet = new HashSet(); for (LauncherActivityInfoCompat info : apps) { String packageName = info.getComponentName().getPackageName(); if (!packagesAdded.contains(packageName) && !newPackageSet.contains(packageName)) { InstallShortcutReceiver.queueInstallShortcut(info, mContext); } newPackageSet.add(packageName); } prefs.edit().putStringSet(shortcutsSetKey, newPackageSet).commit(); } } // Huh? Shouldn't this be inside the Runnable below? final ArrayList added = mBgAllAppsList.added;// 获取自上次更新(notify()广播)后新增加的应用清单,如果是开机初次启动Launcher,那么added就是mBgAllAppsList mBgAllAppsList.added = new ArrayList();// 将AllAppsList的added清空,不影响后续新增的app // Post callback on main thread mHandler.post(new Runnable() { public void run() { final long bindTime = SystemClock.uptimeMillis(); final Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.bindAllApplications(added); if (DEBUG_LOADERS) { Log.d(TAG, "bound " + added.size() + " apps in " + (SystemClock.uptimeMillis() - bindTime) + "ms"); } } else { Log.i(TAG, "not binding apps: no Launcher activity"); } } }); if (DEBUG_LOADERS) { Log.d(TAG, "Icons processed in " + (SystemClock.uptimeMillis() - loadTime) + "ms"); }

绑定app--bindAllApplications

public void bindAllApplications(final ArrayList apps) { if (LauncherAppState.isDisableAllApps()) {// 判断是否禁用所有app,就是所有应用都显示在一级目录 if (mIntentsOnWorkspaceFromUpgradePath != null) { if (LauncherModel.UPGRADE_USE_MORE_APPS_FOLDER) { getHotseat().addAllAppsFolder(mIconCache, apps, mIntentsOnWorkspaceFromUpgradePath, Launcher.this, mWorkspace); } mIntentsOnWorkspaceFromUpgradePath = null; } if (mAppsCustomizeContent != null) { mAppsCustomizeContent.onPackagesUpdated( LauncherModel.getSortedWidgetsAndShortcuts(this)); } } else { if (mAppsCustomizeContent != null) { mAppsCustomizeContent.setApps(apps); mAppsCustomizeContent.onPackagesUpdated(LauncherModel.getSortedWidgetsAndShortcuts(this)); } } if (mLauncherCallbacks != null) { mLauncherCallbacks.bindAllApplications(apps); } }

可以看到无论是一级桌面拿图标,还是抽屉页面拿图标,都是去走,IconCache的getIcon()方法,

IconCache

getIcon()

public Bitmap getIcon(ComponentName component, ResolveInfo resolveInfo, HashMap labelCache) { synchronized (mCache) { if (resolveInfo == null || component == null) { return null; } CacheEntry entry = cacheLocked(component, resolveInfo, labelCache); return entry.icon; } }

这里判断一下条件,会去走cacheLocked() 方法

cacheLocked()

private CacheEntry cacheLocked(ComponentName componentName, ResolveInfo info, HashMap labelCache) { CacheEntry entry = mCache.get(componentName); if (entry == null) { entry = new CacheEntry(); mCache.put(componentName, entry); ComponentName key = LauncherModel.getComponentNameFromResolveInfo(info); if (labelCache != null && labelCache.containsKey(key)) { entry.title = labelCache.get(key).toString(); } else { entry.title = info.loadLabel(mPackageManager).toString(); if (labelCache != null) { labelCache.put(key, entry.title); } } if (entry.title == null) { entry.title = info.activityInfo.name; } entry.icon = Utilities.createIconBitmap( getFullResIcon(info), mContext); } return entry; }

这个方法里面。就是最终去拿图标的方法,里面去拿一些必要信息,去给entry赋值

android 壁纸加载流程,Android 桌面加载图标过程分析相关推荐

  1. 酷炫android壁纸,安卓极致酷炫桌面ADW 附带12款个性主题

    ADW桌面EX-其它给力功能 除了上述的几大亮点功能之外,ADW桌面EX还拥有文件夹.桌面滑动特效选择.自定义图标群组等功能.其中,最亮的当然是文件夹功能了.自从iPhone推出以来,其文件夹收藏图标 ...

  2. Android APK安装后不在桌面生成快捷方式图标

    大家知道在Android系统的手机安装APK应用后会在桌面上生成此APK的快捷方式,用户通过直接点击可以进入此应用程序.但作为开发人员,我们有时候并不希望在安装APK完成后生成快捷方式,而是通过先进入 ...

  3. android 控件回执流程,Android telephony MMS 学习笔记

    本文主要从以下几个方面来学习MMS在android系统中的处理: (1)MMS初始化. (2)MMS发送. (3)MMS接收(包括push MMS接收和从MMSC中提取MMS内容). (4)MMS存储 ...

  4. android 壁纸软件 宝宝,宝宝巴士桌面软件下载

    宝宝巴士桌面手机版寓教于乐,这里汇集了丰富的儿歌.绘本.游戏等资源,有形象生动的小卡片,支持在玩中学,极大的激发了孩子的学习兴趣,起到很好的早教启蒙作用,欢迎有需要的朋友下载使用! 主要功能 单词编成 ...

  5. android遥控器按键传输流程,android 海思平台遥控器按键映射流程

    1.红外遥控器映射 key.xml文件是遥控器键值与linux标准键值之前的映射, /device/hisilicon/bigfish/system/ir_user/key_pars/key.xml ...

  6. android蓝牙设置名称流程,Android 8 设置蓝牙名称 流程

    记录android 8设置蓝牙名称的流程.java packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothDeviceR ...

  7. android ble 蓝牙绑定流程,android BLE蓝牙开发

    蓝牙BLE设备是目前比较热门的设备.由于BLE有低功耗等特点,被广泛应用到身边的电子产品上.如智能手表.手环.防丢器等各种产品上.最近研究一下android上的ble应用开发.跟大家分享一下相关的内容 ...

  8. android 壁纸设置成功,在Android上设置壁纸刷新

    我正在尝试更改当前的用户壁纸. 我已经设置了set_wallpaper权限,但它确实有效. 但是当我改变壁纸时,我必须等待大约15秒才能看到壁纸的变化. 这是很奇怪的,因为如果我检查锁定屏幕它已经改变 ...

  9. android短信发送流程,Android 发送短信程序

    [android]代码库mButton1.setOnClickListener(new Button.OnClickListener() { @Override public void onClick ...

最新文章

  1. xgboost gbdt特征点分烈点
  2. pytest框架_Python最火的第三方开源测试框架——pytest
  3. 英特尔表示:元宇宙的路还很长
  4. wpf开发仿真3d软件_3D体验平台品牌应用——SOLIDWORKS?
  5. android通知背景色,android – 更改通知RemoteViews背景颜色
  6. IIC挂死问题解决过程
  7. Java Thread等待,通知和notifyAll示例
  8. join丢失数据_15、Hive数据倾斜与解决方案
  9. 4~20mA变送器量程与输入电流、输出电流的关系
  10. java中io创建文件和读取文件
  11. debug工具_Hackintool for【黑】mac V3.4.4中文版 黑苹果必装工具箱
  12. fp5139应用电路图_LM358各种应用电路
  13. 视力测试软件正确吗,体检视力测试
  14. 【物联网取证篇】5G消息取证浅谈
  15. 【转载】《三体》:给时光以生命,给岁月以文明
  16. Ablation study消融实验
  17. stata最大值最小值命令_用Stata实现数据标准化
  18. Bentley MicroStation CE版的颜色变换(CONNECT Edition)
  19. Codeforces 819 C. Mister B and Beacons on Field 容斥 数学
  20. CSS | 如何达到监听页面滚动的效果?

热门文章

  1. windows 下同步时间的几种方法
  2. 【docker】——报错:file not found in build context or excluded by .dockerignore
  3. 这个联想的程序员,让盲人朋友也能用智能手机!
  4. 【蓝桥杯】第十三届蓝桥杯单片机第二次省赛 代码程序
  5. android一键换手机,不同品牌手机一键换机:换新手机怎么转移数据?
  6. Android平台GB28181接入端如何对接UVC摄像头?
  7. 2021高考成绩估分查询时间,重磅!2021全国各地高考预测分数线出炉,这样估分可以估算全省排名...
  8. echarts-gauge
  9. DApp投票合约简单开发步骤(完整)
  10. 中国美术家协会吴冬梅的工笔人生