Launcher3原始碼分析(LauncherModel載入資料)
LauncherModel繼承BroadcastReceiver,顯然是一個廣播接收者。在上一篇Launcher的啟動中講到桌面資料的載入工作是在LauncherModel中執行的,那麼它是如何載入資料的呢?下面將分析Launcher和LauncherModel的通訊方式以及LauncherModel載入桌面資料的過程。
首先分析的是Launcher和LauncherModel的通訊方式:
(1)LauncherModel.Callback回撥介面:LauncherModel通過介面輸送已經載入完成的資料給Launcher。
//在Launcher.java中持有LauncherModel物件
mModel = app.setLauncher(this);
繼續分析setLauncher(Launcher launcher)方法
//返回LauncherModel
LauncherModel setLauncher(Launcher launcher) {
getLauncherProvider().setLauncherProviderChangeListener(launcher);
mModel.initialize(launcher);
mAccessibilityDelegate = ((launcher != null ) && Utilities.isLmpOrAbove()) ?
new LauncherAccessibilityDelegate(launcher) : null;
return mModel;
}
以上方法引用了此LauncherModel中的initialize(Callbacks callbacks);方法。且到LauncherModel中分析
public void initialize(Callbacks callbacks) {
synchronized (mLock) {
// Disconnect any of the callbacks and drawables associated with ItemInfos on the
// workspace to prevent leaking Launcher activities on orientation change.
unbindItemInfosAndClearQueuedBindRunnables();
mCallbacks = new WeakReference<Callbacks>(callbacks);
}
}
發現傳入的引數是Callbacks物件,而Launcher實現了LauncherModel.Callbacks介面,顯然LauncherModel和Launcher就建立了聯絡。
從而完成了Launcher在LauncherModel中註冊介面,將介面實現的引用註冊到LauncherModel中,並且返回已經完成初始化的LauncherModel的引用。LauncherModel的所有操作結果都會通過callbacks中定義的各個回撥介面中的方法通知給Launcher,並由Launcher分發給不同的桌面元件或者Launcher自身。
LauncherModel提供的Callbacks介面:
public interface Callbacks {
//如果Launcher在載入完成之前被強制暫停,那麼需要通過這個回撥方法通知Launcher
//在它再次顯示的時候重新執行載入過程
public boolean setLoadOnResume();
//獲取當前螢幕序號
public int getCurrentWorkspaceScreen();
//啟動桌面資料繫結
public void startBinding();
//批量繫結桌面元件:快捷方式列表,列表的開始位置,列表結束的位置,是否使用動畫
public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end,
boolean forceAnimateIcons);
//批量繫結桌面頁,orderedScreenIds 序列化後的桌面頁列表
public void bindScreens(ArrayList<Long> orderedScreenIds);
public void bindAddScreens(ArrayList<Long> orderedScreenIds);
//批量繫結資料夾,folders 資料夾對映列表
public void bindFolders(LongArrayMap<FolderInfo> folders);
//繫結任務完成
public void finishBindingItems();
//批量繫結小部件,info 需要繫結到桌面上的小部件資訊
public void bindAppWidget(LauncherAppWidgetInfo info);
//繫結應用程式列表介面的應用程式資訊,apps 需要繫結到應用程式列表中的應用程式列表
public void bindAllApplications(ArrayList<AppInfo> apps);
// Add folders in all app list.
public void bindAllApplications2Folder(ArrayList<AppInfo> apps, ArrayList<ItemInfo> items);
//批量新增元件
public void bindAppsAdded(ArrayList<Long> newScreens,
ArrayList<ItemInfo> addNotAnimated,
ArrayList<ItemInfo> addAnimated,
ArrayList<AppInfo> addedApps);
//批量更新應用程式相關的快捷方式或者入口
public void bindAppsUpdated(ArrayList<AppInfo> apps);
public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated,
ArrayList<ShortcutInfo> removed, UserHandleCompat user);
public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
// 從桌面移除一些元件,當應用程式被移除或者禁用的時候呼叫
public void bindComponentsRemoved(ArrayList<String> packageNames,
ArrayList<AppInfo> appInfos, UserHandleCompat user, int reason);
public void bindAllPackages(WidgetsModel model);
//全域性搜尋或者搜尋屬性更新
public void bindSearchablesChanged();
public boolean isAllAppsButtonRank(int rank);
/**
* 指示正在繫結的頁面
* @param page 桌面頁序號
*/
public void onPageBoundSynchronously(int page);
//輸出當前Launcher資訊到本地檔案中
public void dumpLogsToLocalData();
}
(2)執行緒處理
@Thunk DeferredHandler mHandler = new DeferredHandler();
@Thunk LoaderTask mLoaderTask;
@Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
@Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper());
Launcher和LauncherModel執行在Launcher這個應用程式的主執行緒中, sWorkerThread只是Launcher應用程式主執行緒下的一個子執行緒,對於執行緒和執行緒之間的訊息互動,一個比較好的方案是將任務拋到目標執行緒的處理器中,為此,LauncherModel為sWorkerThread在主執行緒中建立了一個處理器,以實現sWorkerThread和Launcher所在程序之間的資訊互動。
(3)廣播介面:
LauncherModel繼承了BroadcastReceiver,因此可以接收來自Launcher或其他地方的廣播,在onReceive(Context context, Intent intent)方法中做出相應的處理。
來個分割線,分析上面載入繫結資料過程的幾個方法:
LauncherModel負責桌面資料的載入,即呼叫startLoader方法啟動執行緒,載入資料。
public void startLoader(int synchronousBindPage) {
startLoader(synchronousBindPage, LOADER_FLAG_NONE);
}
//啟動LoaderTask執行緒裡面的run方法,sWorker是一個Handler物件,用於啟動執行緒的run方法
public void startLoader(int synchronousBindPage, int loadFlags) {
// Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
InstallShortcutReceiver.enableInstallQueue();
synchronized (mLock) {
// Clear any deferred bind-runnables from the synchronized load process
// We must do this before any loading/binding is scheduled below.
//
//如果當前只是對桌面的第一個頁面進行資料重新整理,那麼這個介面的呼叫會被封裝成為一個任務載入到一個訊息佇列中
//等待後續所有任務完成後才統一執行,如果這個任務還沒有得到執行,而新的重新整理頁面的請求已經來到,那麼LauncherModel
//在啟動載入之前會將訊息佇列清空,以確保所在任務都執行完成後,Launcher才會得到通知。
synchronized (mDeferredBindRunnables) {
//mDeferredBindRunnables是一個Runnable的列表(ArrayList),當LauncherModel的載入任務完成後,這裡將會儲存發往Launcher的通知,封裝在一個Runnable中,加入該表
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.
stopLoaderLocked();
mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags);
if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
&& mAllAppsLoaded && mWorkspaceLoaded && !mIsLoaderTaskRunning) {
//同步載入
mLoaderTask.runBindSynchronousPage(synchronousBindPage);
} else {
sWorkerThread.setPriority(Thread.NORM_PRIORITY);
//非同步載入
sWorker.post(mLoaderTask);
}
}
}
}
接下來看LoaderTask的run方法。
public void run() {
synchronized (mLock) {
if (mStopped) {
return;
}
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: {
//載入和繫結workspace資料
if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
loadAndBindWorkspace();
if (mStopped) {
break keep_running;
}
//等待執行緒空閒的時候再繼續載入,避免ANR
waitForIdle();
// second step
//載入和繫結all apps資料
if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
loadAndBindAllApps();
}
// 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;
mHasLoaderCompletedOnce = true;
}
}
分析該方法,可見載入Launcher資料主要執行兩個步驟:
(1)loadAndBindWorkspace();
(2)loadAndBindAllApps();
那就按著這兩個步驟,繼續往下看:
loadAndBindWorkspace();
private void loadAndBindWorkspace() {
//載入繫結桌面資料任務開始true
mIsLoadingAndBindingWorkspace = true;
// Load the workspace
if (DEBUG_LOADERS) {
Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
}
//接下來先判斷桌面資料是否完全載入
if (!mWorkspaceLoaded) {
//false,載入桌面資料
loadWorkspace();
synchronized (LoaderTask.this) {
if (mStopped) {
//任務已經被停止,返回
return;
}
//任務未停止
mWorkspaceLoaded = true;
}
}
//桌面資料載入完成,將資料繫結到桌面
// Bind the workspace
bindWorkspace(-1);
}
根據mWorkspaceLoader這個標識判斷桌面資料是否載入完成,如果為true則執行bindWorkspace(-1)繫結桌面資料,如果為false,則先執行桌面資料的載入loadWorkspace(),再執行資料的繫結。
//Launcher桌面的資料主要包括來自Launcher資料庫的各種表,
//loadWorkspace方法負責從這些資料庫表中讀取資料並轉譯為Launcher桌面項的資料結構
private void loadWorkspace() {
final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
final Context context = mContext;
final ContentResolver contentResolver = context.getContentResolver();
//獲取包管理服務
final PackageManager manager = context.getPackageManager();
final boolean isSafeMode = manager.isSafeMode();
//獲取Launcher定製的應用程式管理介面
final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
final boolean isSdCardReady = context.registerReceiver(null,
new IntentFilter(StartupReceiver.SYSTEM_READY)) != null;
//獲取桌面的行列數
LauncherAppState app = LauncherAppState.getInstance();
InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
int countX = (int) profile.numColumns;
int countY = (int) profile.numRows;
//根據輸入的標識對資料進行預處理
//判斷標識是否為清空桌面
if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true);
//刪除資料
LauncherAppState.getLauncherProvider().deleteDatabase();
}
//判斷是否為遷移Launcher2的快捷方式
if ((mFlags & LOADER_FLAG_MIGRATE_SHORTCUTS) != 0) {
// append the user's Launcher2 shortcuts
Launcher.addDumpLog(TAG, "loadWorkspace: migrating from launcher2", true);
//遷移Launcher2的快捷方式
LauncherAppState.getLauncherProvider().migrateLauncher2Shortcuts();
} else {
// Make sure the default workspace is loaded
//確保預設資料得以載入
Launcher.addDumpLog(TAG, "loadWorkspace: loading default favorites", false);
//根據需要載入預設快捷方式
LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary();
}
synchronized (sBgLock) {
clearSBgDataStructures();
//更新並獲取快取
final HashMap<String, Integer> installingPkgs = PackageInstallerCompat
.getInstance(mContext).updateAndGetActiveSessionCache();
final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
final ArrayList<Long> restoredRows = new ArrayList<Long>();
//獲取快捷方式的uri
final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
//根據獲取的uri查詢Favorites表資料
final Cursor c = contentResolver.query(contentUri, 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)
//準備桌面佔用情況標識對映
final LongArrayMap<ItemInfo[][]> occupied = new LongArrayMap<>();
……
// Break early if we've stopped loading
//載入任務已經被停止
if (mStopped) {
//從資料庫中清理需要刪除的資料項
clearSBgDataStructures();
return;
}
//從資料庫中清理需要刪除的資料項
if (itemsToRemove.size() > 0) {
// Remove dead items
contentResolver.delete(LauncherSettings.Favorites.CONTENT_URI,
Utilities.createDbSelectionQuery(
LauncherSettings.Favorites._ID, itemsToRemove), null);
if (DEBUG_LOADERS) {
Log.d(TAG, "Removed = " + Utilities.createDbSelectionQuery(
LauncherSettings.Favorites._ID, itemsToRemove));
}
// Remove any empty folder
for (long folderId : LauncherAppState.getLauncherProvider()
.deleteEmptyFolders()) {
sBgWorkspaceItems.remove(sBgFolders.get(folderId));
sBgFolders.remove(folderId);
sBgItemsIdMap.remove(folderId);
}
}
//設定恢復狀態為已恢復
if (restoredRows.size() > 0) {
// Update restored items that no longer require special handling
ContentValues values = new ContentValues();
values.put(LauncherSettings.Favorites.RESTORED, 0);
contentResolver.update(LauncherSettings.Favorites.CONTENT_URI, values,
Utilities.createDbSelectionQuery(
LauncherSettings.Favorites._ID, restoredRows), null);
}
//處理在裝在sdcard上的應用程式
if (!isSdCardReady && !sPendingPackages.isEmpty()) {
context.registerReceiver(new AppsAvailabilityCheck(),
new IntentFilter(StartupReceiver.SYSTEM_READY),
null, sWorker);
}
//處理桌面資料
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);
}
if (DEBUG_LOADERS) {
Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
Log.d(TAG, "workspace layout: ");
int nScreens = occupied.size();
for (int y = 0; y < countY; y++) {
String line = "";
for (int i = 0; i < nScreens; i++) {
long screenId = occupied.keyAt(i);
if (screenId > 0) {
line += " | ";
}
ItemInfo[][] screen = occupied.valueAt(i);
for (int x = 0; x < countX; x++) {
if (x < screen.length && y < screen[x].length) {
line += (screen[x][y] != null) ? "#" : ".";
} else {
line += "!";
}
}
}
Log.d(TAG, "[ " + line + " ]");
}
}
}
}
載入資料完成就可以繫結資料到桌面了。
private void bindWorkspace(int synchronizeBindPage) {
final long t = SystemClock.uptimeMillis();
Runnable r;
// Don't use these two variables in any of the callback runnables.
// Otherwise we hold a reference to them.
//獲取Launcher的介面
final Callbacks oldCallbacks = mCallbacks.get();
//判斷Launcher的介面是否存在
if (oldCallbacks == null) {
// This launcher has exited and nobody bothered to tell us. Just bail.
Log.w(TAG, "LoaderTask running with no launcher");
return;
}
// Save a copy of all the bg-thread collections
//桌面資料項列表
ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>();
//桌面小部件項資料列表
ArrayList<LauncherAppWidgetInfo> appWidgets =
new ArrayList<LauncherAppWidgetInfo>();
//經過排序的桌面索引列表
ArrayList<Long> orderedScreenIds = new ArrayList<Long>();
final LongArrayMap<FolderInfo> folders;
final LongArrayMap<ItemInfo> itemsIdMap;
//或衝區資料複製
synchronized (sBgLock) {
workspaceItems.addAll(sBgWorkspaceItems);
appWidgets.addAll(sBgAppWidgets);
orderedScreenIds.addAll(sBgWorkspaceScreens);
folders = sBgFolders.clone();
itemsIdMap = sBgItemsIdMap.clone();
}
//獲取launcher當前所處的頁面索引
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;
final long currentScreenId = currentScreen < 0
? INVALID_SCREEN_ID : orderedScreenIds.get(currentScreen);
// 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();
//分離當前頁面與其他頁面的資料並通知Launcher開始載入
// Separate the items that are on the current screen, and all the other remaining items
//分類資料區定義
ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>();
ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>();
ArrayList<LauncherAppWidgetInfo> currentAppWidgets =
new ArrayList<LauncherAppWidgetInfo>();
ArrayList<LauncherAppWidgetInfo> otherAppWidgets =
new ArrayList<LauncherAppWidgetInfo>();
LongArrayMap<FolderInfo> currentFolders = new LongArrayMap<>();
LongArrayMap<FolderInfo> otherFolders = new LongArrayMap<>();
//分類桌面項、小部件、資料夾資料
filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
otherWorkspaceItems);
filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
otherAppWidgets);
filterCurrentFolders(currentScreenId, itemsIdMap, folders, currentFolders,
otherFolders);
//對資料進行排序
sortWorkspaceItemsSpatially(currentWorkspaceItems);
sortWorkspaceItemsSpatially(otherWorkspaceItems);
// Tell the workspace that we're about to start binding items
//在主執行緒上執行通知Launcher繫結開始任務
r = new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
//繫結資料到launcher,launcher回撥
callbacks.startBinding();
}
}
};
runOnMainThread(r);
bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
// Load items on the current page
bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
currentFolders, null);
if (isLoadingSynchronously) {
r = new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null && currentScreen != PagedView.INVALID_RESTORE_PAGE) {
callbacks.onPageBoundSynchronously(currentScreen);
}
}
};
runOnMainThread(r);
}
// Load all the remaining pages (if we are loading synchronously, we want to defer this
// work until after the first render)
synchronized (mDeferredBindRunnables) {
mDeferredBindRunnables.clear();
}
bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
(isLoadingSynchronously ? mDeferredBindRunnables : null));
// Tell the workspace that we're done binding items
//實現包含了結束繫結通知的任務
r = new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.finishBindingItems();
}
// If we're profiling, ensure this is the last thing in the queue.
if (DEBUG_LOADERS) {
Log.d(TAG, "bound workspace in "
+ (SystemClock.uptimeMillis()-t) + "ms");
}
//設定載入以及繫結結束標識
mIsLoadingAndBindingWorkspace = false;
}
};
if (isLoadingSynchronously) {
synchronized (mDeferredBindRunnables) {
mDeferredBindRunnables.add(r);
}
} else {
//在主執行緒中執行該任務
runOnMainThread(r);
}
}
載入和繫結好桌面資料就可以載入和繫結AllApps資料了
//通過呼叫兩個方法來完成應用程式的資料載入即繫結
private void loadAndBindAllApps() {
if (DEBUG_LOADERS) {
Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
}
//判斷應用程式選單是否已載入
if (!mAllAppsLoaded) {
//未載入,執行載入繫結過程
loadAllApps();
synchronized (LoaderTask.this) {
if (mStopped) {
return;
}
}
updateIconCache();
synchronized (LoaderTask.this) {
if (mStopped) {
return;
}
//設定已經載入標識
mAllAppsLoaded = true;
}
} else {
//已載入資料,只執行繫結過程
onlyBindAllApps();
}
}
跟桌面的資料載入繫結過程類似,也是根據mAllAppsLoaded這個標識來判斷執行的是載入還是繫結。
private void 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;
}
//Android系統提供了多賬戶的概念,不同的賬戶下可以使用的應用程式是不同的,因此Launcher需要注意這個細節,
//在不同賬戶下處理不同的應用程式列表資訊,所有在載入應用程式列表的時候需要獲取當前裝置上的所有賬戶。
final List<UserHandleCompat> profiles = mUserManager.getUserProfiles();
// Clear the list of apps
//清除列表
mBgAllAppsList.clear();
for (UserHandleCompat user : profiles) {
// Query for the set of apps
final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
//獲取應用程式入口資訊
final List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
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;
}
//將應用程式資訊加入快取區
// 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));
}
//按賬戶儲存查詢到的應用列表,ManagedProfileHeuristic工具將查詢得到的資料分類儲存到共享檔案
final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user);
if (heuristic != null) {
runAfterBindCompletes(new Runnable() {
@Override
public void run() {
heuristic.processUserApps(apps);
}
});
}
}
……
//將需要載入的應用程式選單中的資料完成分類後,接著將資料傳送到Launcher中處理
// 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) {
if (!FolderPlugins.getInstance().isEnable()) {//(arrayForBind == null || arrayForBind.isEmpty()) && ){
callbacks.bindAllApplications(added);
} else {
callbacks.bindAllApplications2Folder(added, arrayForBind);
}
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");
}
}
});
// Cleanup any data stored for a deleted user.
ManagedProfileHeuristic.processAllUsers(profiles, mContext);
loadAndBindWidgetsAndShortcuts(mApp.getContext(), tryGetCallbacks(oldCallbacks),
true /* refresh */);
if (DEBUG_LOADERS) {
Log.d(TAG, "Icons processed in "
+ (SystemClock.uptimeMillis() - loadTime) + "ms");
}
}
private void onlyBindAllApps() {
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 (onlyBindAllApps)");
return;
}
// shallow copy
@SuppressWarnings("unchecked")
final ArrayList<AppInfo> list
= (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
final WidgetsModel widgetList = mBgWidgetsModel.clone();
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");
}
}
};
boolean isRunningOnMainThread = !(sWorkerThread.getThreadId() == Process.myTid());
if (isRunningOnMainThread) {
r.run();
} else {
mHandler.post(r);
}
}
相關推薦
Launcher3原始碼分析(LauncherModel載入資料)
LauncherModel繼承BroadcastReceiver,顯然是一個廣播接收者。在上一篇Launcher的啟動中講到桌面資料的載入工作是在LauncherModel中執行的,那麼它是如何載入資料的呢?下面將分析Launcher和LauncherModel
Launcher3原始碼分析 — 載入Workspace的資料 .
Launcher3的資料載入過程涉及到了兩個主要的類LauncherProvider和LauncherModel。 LauncherProvider LauncherProvider繼承自ContentProvider類,內部基於資料庫儲存實現了ContentProvid
從Android原始碼分析Activity載入流程
從Android原始碼分析Activity載入流程 概述:startActivity->startActivityForResult->Instrumentation::execStartActivity->scheduleLaunchActivi
Launcher3原始碼分析(Workspace)
Workspace主要功能:完成多個螢幕的以及桌布的顯示,多個螢幕之間的切換和桌布的新增。 /** * Used to inflate the Workspace from XML. */ public Workspac
Redis原始碼分析(一)——Redis資料結構-字串SDS
1. SDS簡介 Redis中使用的字串均為『簡單動態字串』(Simple Dynamic String),簡稱SDS。 SDS是在C字串的基礎上進行了一些包裝,使得它更符合Redis的使用場景。 在Redis中,C字串只用在一些無需修改的地方,如日誌列
【Netty原始碼分析】傳送資料過程
future.channel().writeAndFlush("Hello Netty Server ,I am a common client"); 呼叫AbstractChannel的writeAndFlush函式@Override public ChannelFutu
BeeGFS原始碼分析1-元資料服務概要分析
元資料服務是BeeGFS中用來維護檔案和目錄關係及其屬性配置的服務,其多執行緒epoll設計實現非常高效,主要流程如下: Con
1.Sentinel原始碼分析—FlowRuleManager載入規則做了什麼?
最近我很好奇在RPC中限流熔斷降級要怎麼做,hystrix已經1年多沒有更新了,感覺要被遺棄的感覺,那麼我就把眼光聚焦到了阿里的Sentinel,順便學習一下阿里的原始碼。 這一章我主要講的是FlowRuleManager在載入FlowRule的時候做了什麼,下一篇正式講Sentinel如何控制併發數的。 下
BeeGFS原始碼分析4-元資料同步
同步任務初始化 // fhgfs_meta\source\app\App.cpp void App::initComponent
Spring原始碼分析之-載入IOC容器
本文接上一篇文章 SpringIOC 原始碼,控制反轉前的處理(https://mp.weixin.qq.com/s/9RbVP2ZQVx9-vKngqndW1w) 繼續進行下面的分析 首先貼出 Spring bean容器的重新整理的核心 11個步驟進行祭拜(一定要讓我學會了...阿門) // 完成IoC容
Giraph 原始碼分析(五)—— 載入資料+同步總結
作者|白松 關於Giraph 共有九個章節,本文第五個章節。 環境:在單機上(機器名:giraphx)啟動了2個workers。
Flume NG原始碼分析(三)使用Event介面表示資料流
Flume NG有4個主要的元件: Event表示在Flume各個Agent之間傳遞的資料流 Source表示從外部源接收Event資料流,然後傳遞給Channel Channel表示對從Source傳遞的Event資料流的臨時儲存 Sink表示從Channel中接收儲存的Event
兄弟連區塊鏈教程Fabric1.0原始碼分析ledgerID資料
1、idStore概述 Fabric支援建立多個Ledger,不同Ledger以ledgerID區分。 多個ledgerID及其創世區塊儲存在idStore資料庫中,idStore資料庫基於leveldb實現。 idStore預設使用路徑:/var/hyperledger/production
zigbee 之ZStack-2.5.1a原始碼分析(三)無線資料傳送和接收
前面說過SampleApp_Init和SampleApp_ProcessEvent是我們重點關注的函式,接下來分析無線傳送和接收相關的程式碼: 在SampleApp_ProcessEvent函式中: if ( events & SYS_EVENT_MSG ) { &nbs
從SpringBoot原始碼分析 配置檔案的載入原理和優先順序
從SpringBoot原始碼分析 配置檔案的載入原理和優先順序 本文從SpringBoot原始碼分析 配置檔案的載入原理和配置檔案的優先順序 跟入原始碼之前,先提一個問題: SpringBoot 既可以載入指定目錄下的配置檔案獲取配置項,也可
List去重(資料為物件的情況)及String中的equals()方法和hashCode()方法原始碼分析
面試中經常被問到的list如何去重,用來考察你對list資料結構,以及相關方法的掌握,體現你的java基礎學的是否牢固。 我們大家都知道,set集合的特點就是沒有重複的元素。如果集合中的資料型別是基本資料型別,可以直接將list集合轉換成set,就會自動去除重複的元素,這個就相對比較簡單。上一篇
大資料之Spark(三)--- Spark核心API,Spark術語,Spark三級排程流程原始碼分析
一、Spark核心API ----------------------------------------------- [SparkContext] 連線到spark叢集,入口點. [HadoopRDD] extends RDD 讀取hadoop
自定義spring boot starter三部曲之三:原始碼分析spring.factories載入過程
本文是《自定義spring boot starter三部曲》系列的終篇,前文中我們開發了一個starter並做了驗證,發現關鍵點在於spring.factories的自動載入能力,讓應用只要依賴starter的jar包即可,今天我們來分析Spring和Spring boot原始碼,瞭解s
區塊鏈入門教程以太坊原始碼分析交易資料分析eth
交易的資料結構 交易的資料結構定義在core.types.transaction.go中,結構如下: type Transaction struct { data txdata // caches hash atomic.Value size atomic.Value from atomic.Value
Java原始碼分析——Class類、ClassLoader類解析(三) 類載入器、實現自定義類載入器
在這個系列的第一篇章就講解了Class類的獲取以及載入過程,但是並沒有提及具體的載入過程,在java中,載入一個類是通過ClassLoader類來執行的,也就是類載入器完成。java中所有的類,都必須載入進jvm中才能執行,這個載入的意思是