Android Launcher3分析——LauncherModel

Android Launcher3分析——LauncherModel

接上篇Android Launcher3分析——开篇,上篇提到LauncherModel是Launcher3处理数据的核心,这当然不是瞎掰的,我们用具体的代码分析来予以佐证。

LauncherModel本身继承自BroadcastReceiver,实现了OnAppsChangedCallbackCompat接口,该接口在LauncherAppsCompat.java中定义,由LauncherModel中实现,并在LauncherAppsCompat子类的内部类PackageMonitor(API16以上)(继承BroadcastReceiver类型)或WrapperCallback(API15以下)调用,源码中关于LauncherModel的说明,大致含义如下:

保持Launcher的内存状态。在内存静态区应该只能有一个LauncherModel的实例,同时它还为Launcher提供了一系列的更新数据库的Api

内部类或接口

Callbacks(接口)

该接口定义了一些绑定数据时的具体方法,由Launcher实现,在LauncherModel中调用。

ItemInfoFilter(接口)

ItemInfo过滤器,过滤掉不符合条件的ItemInfo

LoaderTask(实现了Runnable接口)

核心内部类,主要任务负责加载数据(workspace、all apps)和绑定数据(workspace、all apps)

核心方法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:
  {
    if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
    loadAndBindWorkspace();

    if (mStopped) {
      break keep_running;
    }

    waitForIdle();

    // second step
    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;
  }
}

先加载并绑定workspace,然后调用waitForIdle方法等待资源锁的释放,从而开始加载并绑定All Apps。在手动置空mContext(不然的话LoaderTask将持有mContext直至所有的runnable对象都被执行完毕,容易引起内存泄漏),接着指控LoaderTask本身,更新相关标识位。那么两个run里面的两个load函数到底有什么功能呢?

PackageUpdatedTask()

核心内部类, 实现了Runnable接口,主要负责更新Package信息(如package的增删改查等)

AppsAvailabilityCheck

BroadcastReceiver类型,根据包名检查对应app的有效性。

核心方法

loadAndBindWorkspace

加载并绑定workspace,先加载后绑定;

loadWorkspace

loadWorkspace的代码实现较复杂,这里就不贴代码了,只根据代码做个简单的梳理:

  1. 初始化必要的变量值,判断是否需要执行MigrateFromRestoreTask任务,根据mFlags判断是否需要对LauncherProvider中定义的数据库做初始化操作;
  2. 进入同步代码块:
    1. 清空sBg的数据,使其恢复到初始化状态,定义用于接受清洗过后的数据的数据结构(itemsToRemove、restoreRows和occupied,这里的occupied指的就是Launcher桌面已经被Item占据的单元列表);
    2. 利用ContentResolver查询Favorites表,获取所有列的索引,以便后面根据对应索引去的对应列的值;
    3. 遍历Favorites表,根据ItemType对数据进行清洗:
      • Application和Shortcut类型Item:
      • 将需要删除的item,需要移除的item,加入到对应的存储数据结构中,并将需要更新的item进行跟新操作
      • 数据清洗完后,根据提供的条件,得到ShortcutInfo(最终展示在Launcher桌面上的那些图标),设置其属性(其中主要属性有intent(点击图标会对应的跳转操作),坐标(cellX,cellY)、占地面积(spanX,spanY),容纳图标的容器,所属的是哪一个屏幕等),这里还会根据container决定将当前item放入到什么位置(container为Workspace和Hotseat时,放入workspace中,其他情况放入Folder中
      • 文件夹类型Item:
        1. 设置folderInfo的属性,对于FolderInfo类型的Item,其本身位于容器Workspace之中,但又可以作为容器来承载(Application和Shortcut类Item)
        2. 设置完属性后,将其加入到对应的数据存储结构中,以便后面处理
      • Widget类型(自定义的或App提供的)
        1. 根据对应的id机appWidgetId判断其有效性(将无效的加入到itemsToRemoved列表中,有效的则获取其appWidgetInfo信息);
        2. 移除不知道归属容器的Widget(就是不知道放哪儿的Widget)
        3. 更新providerName不一致的非自定义widget,然后将其加入到相应的数据结构;
  3. 处理清洗后的数据(itemsToRemoved、restoredRows等),这一步实质上是根据列表生成相应的sql语句,进行数据库操作;
  4. 移除空的屏幕,并更新。

以上就是loadWorkspace的全部过程,接下来看bindWorkspace时如何实现的。

bindWorkspace

由于bindWorkspace涉及到的都是UI操作,所以毫无疑问这些操作是在UI线程(即主线程)中完成的,具体实现方式是由LauncherModel调用Launcher实现的Callbacks接口里面的相应方法。bindWorkspace具体分析

  1. 绑定前的准备工作(待使用的数据结构初始化,变量的初始化);
  2. 在主线程中解除当前已存在的items的绑定;
  3. 过滤数据,将要绑定的内容分为当前的和非当前的;
  4. 通知Launcher开始绑定
  5. 先绑定screen,在绑定item
  6. 通知Launcher所有绑定已完成
  7. 善后处理

loadAndBindAllApps

加载并绑定all apps,先加载后绑定,具体实现的分析与loadAndBindWorkspace类似,不做赘述。

更新数据库的操作

主要包括对(App、App Shortcut、Widget、Folder的增删改查,具体体现在以下方法。

  • addItemToDatabase,向数据库添加数据;
  • deletePackageFromDatabase,从数据库删除数据Package
  • deleteItemFromDatabase,从数据库删除Item
  • deleteItemsFromDatabase,从数据库批量删除item
  • deleteFolderContentsFromDatabase,从数据库删除文件夹里面的内容信息
  • moveItemInDatabase,更新Item的位置信息
  • moveItemsIndatabase,批量更新Item的位置信息
  • modifyItemInDatabase,更新Item的信息
  • updateItemInDatabase,更新Item的信息

总结

以上就是本人根据源码结合自己对Launcher3中的数据处理核心LauncherModel的粗陋分析,个人觉得值得借鉴的地方在于Callbacks的设计以及开始绑定之前的数据区分处理,当然个人觉得这里还是有不足之处的,那就是LauncherModel的loadWorkspace方法似乎太庞大了,如果自定义Launcher需要在这方面上进行优化。接下来会对Launcher3中的一些展示核心(自定义控件进行分析)。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值