网站建设 从入门到精通pdf,可视化自助建站,商丘做网站用什么程序好,仿珠宝首饰网站开发桌面应用图标流程前言本人工作上碰到这么一个需求#xff0c;开发一款滤镜引擎#xff0c;将桌面上所有的图标进行统一的滤镜化#xff0c;这就需要了解一下整个桌面去取图标的过程#xff0c;了解了整个过程#xff0c;找到真正拿图标的地方#xff0c;在真正取图标的地…桌面应用图标流程前言本人工作上碰到这么一个需求开发一款滤镜引擎将桌面上所有的图标进行统一的滤镜化这就需要了解一下整个桌面去取图标的过程了解了整个过程找到真正拿图标的地方在真正取图标的地方将图片进行替换或者滤镜化之前分析情况现在整理下与大家分享。本文所用的代码是基于Android 5.1桌面组件介绍一级菜单WorkSpace他是一个ViewGroup要想在桌面上显示东西就得往这个ViewGroup里添加自己的ViewBubbleTextView他是一个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();// Dont bother to start the thread if we know its not going to do anythingif (mCallbacks ! null mCallbacks.get() ! null) {// If there is already one running, tell it to stop.// also, dont downgrade isLaunching if were already runningisLaunching 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 stepif (DEBUG_LOADERS) Log.d(TAG, step 2: loading all apps);//加载二级菜单里面的方法loadAndBindAllApps();// Restore the default thread priority after we are done loading itemssynchronized (mLock) {android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);}}// Update the saved icons if necessaryif (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 workspaceif (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 workspacebindWorkspace(-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//如果workspaceResId0就会加载默认的配置(default_workspace_xxx.xml)并保存到数据库中if (workspaceResId 0) {workspaceResId sp.getInt(DEFAULT_WORKSPACE_RESOURCE_ID, R.xml.default_workspace);}// Populate favorites table with initial favoritesSharedPreferences.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();HashMap folders new HashMap();HashMap itemsIdMap new HashMap();ArrayList orderedScreenIds 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;// 当前screenfinal 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 itemsArrayList currentWorkspaceItems new ArrayList();// 存放当前workspace上的itemsArrayList otherWorkspaceItems new ArrayList();// 存放除当前workspace之外的itemsArrayList currentAppWidgets new ArrayList();// 存放当前workspace上的appwidgetsArrayList otherAppWidgets new ArrayList();// 存放除当前workspace之外的appwidgetsHashMap currentFolders new HashMap();// 存放当前workspace上的folderHashMap otherFolders new HashMap();// 存放除当前workspace之外的folderfilterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems, otherWorkspaceItems);// 过滤items区分当前screen和其他screen上的itemsfilterCurrentAppWidgets(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 were about to start binding itemsr 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 were starting binding all over again, clear any bind calls wed postponed in// the past (see waitUntilResume) -- we dont need them since were starting binding// from scratch againmBindOnResumeCallbacks.clear();// Clear the workspace because its going to be reboundmWorkspace.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 appsmBgAllAppsList.clear();// 清除所有app列表SharedPreferences prefs mContext.getSharedPreferences(LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);for (UserHandleCompat user : profiles) {// Query for the set of appsfinal 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 dont have any apps// TODO: Fix this. Only fail for the current user.if (apps null || apps.isEmpty()) {// 没有需要显示的直接返回return;}// Sort the applications by namefinal 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 ApplicationInfosfor (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? Shouldnt this be inside the Runnable below?final ArrayList added mBgAllAppsList.added;// 获取自上次更新(notify()广播)后新增加的应用清单如果是开机初次启动Launcher那么added就是mBgAllAppsListmBgAllAppsList.added new ArrayList();// 将AllAppsList的added清空不影响后续新增的app// Post callback on main threadmHandler.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()方法IconCachegetIcon() 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赋值