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);
}
}