Launcher3源码浅析(5.1)--LauncherModel

目录

  • 前言
  • 数据加载
  • 监听数据变化
  • 更新数据

前言

  LauncherModel是一个数据模型类,从数据模型上去完整维护Launcher的数据。其主要处理的是加载数据到模型,监听数据的变化,更新数据模型和数据库。另外它有一套丰富的回调函数,当数据变更时,这些回调会被调用,主要用于更新视图。

数据加载

  首先在Launcher.java的onCreate里调用LauncherModel的startLoader方法:
mModel.startLoader
  startLoader在线程里处理数据的加载:

 ......
 mLoaderTask = new LoaderTask(mApp.getContext(), isLaunching, loadFlags);
 if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
     && mAllAppsLoaded && mWorkspaceLoaded) {
        mLoaderTask.runBindSynchronousPage(synchronousBindPage);
} else {
         sWorkerThread.setPriority(Thread.NORM_PRIORITY);
         sWorker.post(mLoaderTask);
}
......

  sWorkerThread是一个HandlerThread,在一开始就初始化并启动了;sWorker是一个Handler对象,调用post方法,则实际处理加载是在LoaderTask这个Runnable里。

......
private static final HandlerThread sWorkerThread = new  HandlerThread("launcher-loader");
static {
    sWorkerThread.start();
}
private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
......

  LoaderTask的run里调用loadAndBindWorkspace()来加载Workspace的数据,调用loadAndBindAllApps()来加载抽屉里的数据,即Android设备上显示的所有应用的图标信息。接下来分别看看loadAndBindWorkspace和loadAndBindAllApps是如何加载数据的。

1.loadAndBindWorkspace
  loadAndBindWorkspace里调用loadWorkspace,最终就是在loadWorkspace()里加载数据库的数据。那么下面再来看看loadWorkspace()是怎么加载数据的。

.....
final ContentResolver contentResolver = context.getContentResolver();
.....
final Cursor c = contentResolver.query(contentUri, null, null, null, null);
.....
while (!mStopped && c.moveToNext()) {
  try {
        int itemType = c.getInt(itemTypeIndex);
        boolean restored = 0 != c.getInt(restoredIndex);
        boolean allowMissingTarget = false;

        switch (itemType) {
            case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
            case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                id = c.getLong(idIndex);
            .....
            case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                id = c.getLong(idIndex);
                FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
            .....
            case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
                // Read all Launcher-specific widget details
                int appWidgetId = c.getInt(appWidgetIdIndex);
            .....
        .....
            break;
       }
   } catch (Exception e) {
    Launcher.addDumpLog(TAG, "Desktop items loading interrupted", e, true);
   }
 }

  上面截取的loadWorkspace中的部分代码,可以看到是通过ContentResolver根据URI来查询ContentProvider中提供的数据的,while循环获取Cursor里的数据,根据itemType,是快捷方式图标,还是文件夹,还是桌面插件,这三种不同的类型数据来分别处理,放到各自的全局变量里去。

2.loadAndBindAllApps
  类似loadAndBindWorkspace的处理,loadAndBindAllApps是调用loadAllApps()来最终实现数据加载。接着再来看看loadAllApps的实现。

.....
// Clear the list of apps
mBgAllAppsList.clear();
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<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
    .....
    // 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));
    }
    .....
}
.....
// 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");
        }
    }
});
.....

  从上面截取的loadAllApps的部分代码可以看到,应用图标信息的加载是通过LauncherAppsCompat的getActivityList方法实现的,跟进去可以看到最终是由LauncherApps(5.1还是5.0以前的版本没有这个对象)这个系统服务调用getActivityList来获取的。

  获取到数据之后,把数据存到全局列表mBgAllAppsList里,然后通过回调bindAllApplications把这些数据放到AppsCustomizePagedView容器里,bindAllApplications接口在Launcher.java里实现。

数据变化

  首先在LauncherApplication的onCreate方法里,初始化了LauncherAppState对象,如下:

LauncherAppState.setApplicationContext(this);
LauncherAppState.getInstance();

在LauncherAppState的构造函数里则注册了一些广播,并设置数据改变的监听,

 ......
 // Register intent receivers
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
sContext.registerReceiver(mModel, filter);
filter = new IntentFilter();
filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
sContext.registerReceiver(mModel, filter);
filter = new IntentFilter();
filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
sContext.registerReceiver(mModel, filter);
// Register for changes to the favorites
ContentResolver resolver = sContext.getContentResolver();
resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true, mFavoritesObserver);
......

  LaucherModel继承自BroadcastReceiver,所以当收到上面注册的广播时,就会进行响应处理。
   再看一下监听到数据库改变时的处理:

......
/**
 * Receives notifications whenever the user favorites have changed.
 */
private final ContentObserver mFavoritesObserver = new ContentObserver(new Handler()) {
    @Override
    public void onChange(boolean selfChange) {
        // If the database has ever changed, then we really need to force a reload of the
        // workspace on the next load
         mModel.resetLoadedState(false, true);
         mModel.startLoaderFromBackground();
    }
};
......

  上述代码可以看到,监听到数据改变时,会强制重新加载数据。

更新数据

  LaucherModel实现了LauncherAppsCompat.OnAppsChangedCallbackCompat接口,

public interface OnAppsChangedCallbackCompat {
   void onPackageRemoved(String packageName, UserHandleCompat user);
   void onPackageAdded(String packageName, UserHandleCompat user);
   void onPackageChanged(String packageName, UserHandleCompat user);
   void onPackagesAvailable(String[] packageNames, UserHandleCompat user, boolean replacing);
   void onPackagesUnavailable(String[] packageNames, UserHandleCompat user, boolean replacing);
}

  当应用改变的时候,通过回调会调用到LaucherModel里实现的上面的接口,这些实现都调用了enqueuePackageUpdated方法:

void enqueuePackageUpdated(PackageUpdatedTask task) {
    sWorker.post(task);
}

  可以看到实际的实现在PackageUpdatedTask里,PackageUpdatedTask里会相应的去更新数据库及相关数据模型。因为我也没改过这里边的东西,没有详细的了解,所以具体就不深入分析了,有兴趣的可以看看。

你可能感兴趣的:(Launcher)