从事Android rom一年多,一直在负责 Launcher 相关的工作。最近打算写些文章记录下自己对这个模块的理解和源码实现的一些解析。
这些文章将会基于 Android 7.0 的 Launcher3 源码进行分析。
    模块源码路径:packages/apps/Launcher3 。

最开始先从架构设计入手大致介绍一下模块的基本构成。Launcher 模块基本上是按照改进版的MVC架构进行设计的。

Model层主要负责数据的加载和处理,然后通过回调接口,把数据传递给Controller层,最后由Controller层通知View的显示与更新。其中,View层与Model层只有轻度的耦合,View层有些操作会直接通过Model层来更新数据。用户的交互事件主要在Controller层完成。
    作为Controller层的 Launcher  就是程序的主入口,继承自Activity。本文主要关注各个层之间是如何建立联系的。其他内容后面再仔细分析。onCreate里将各个层之间的联系建立起来。

public class Launcher extends Activityimplements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {......@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//建立Model层与Controller层的关联LauncherAppState app = LauncherAppState.getInstance();mModel = app.setLauncher(this);......setContentView(R.layout.launcher);......//建立View层与Controller层的关联setupViews();mDeviceProfile.layout(this); //根据默认配置参数对控件布局作调整......//加载桌面用于显示的数据if (!mRestoring) {if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {// If the user leaves launcher, then we should just load items asynchronously when// they return.mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);} else {// We only load the page synchronously if the user rotates (or triggers a// configuration change) while launcher is in the foregroundmModel.startLoader(mWorkspace.getRestorePage());}}......}
}

一、Model层与Controller层建立关联

首先获取了LauncherAppState类的单例对象。该对象初始化时对桌面的默认配置数据进行了初始化和计算。如:桌面图标和文字大小等。这个后面的文章再仔细分析。
    除此之外,这个单例中持有一些重要类的实例对象,其中包括LauncherModel的实例对象。通过app.setLauncher(this)将Controller层和Model层进行关联,并把LauncherModel的实例对象传递给Launcher的成员变量mModel,方便Launcher对Model层进行一些状态的更新。
    前面说过,这两个层级之间通过回调接口进行数据交互。
    Launcher实现了两个接口:1、LauncherModel.Callbacks;2、LauncherProviderChangeListener
    其中,LauncherModel.Callbacks用于和LauncherModel进行数据交互;LauncherProviderChangeListener用于和LauncherProvider进行交互。

public class LauncherAppState {......LauncherModel setLauncher(Launcher launcher) {getLauncherProvider().setLauncherProviderChangeListener(launcher);mModel.initialize(launcher);mAccessibilityDelegate = ((launcher != null) && Utilities.ATLEAST_LOLLIPOP) ?new LauncherAccessibilityDelegate(launcher) : null;return mModel;}......
}

1.1、Launcher与LauncherProvider通过接口类LauncherProviderChangeListener建立关联

将Launcher作为接口实现类传递给LauncherProvider 的成员对象mListener,需要时就可以通过该成员对象向Launcher传递数据和状态更新。

public class LauncherProvider extends ContentProvider {......@Thunk LauncherProviderChangeListener mListener;public void setLauncherProviderChangeListener(LauncherProviderChangeListener listener) {mListener = listener;mOpenHelper.mListener = mListener;}...
}public interface LauncherProviderChangeListener {public void onLauncherProviderChange();public void onSettingsChanged(String settings, boolean value);public void onAppWidgetHostReset();
}

1.2、Launcher与LauncherModel通过接口Callbacks建立关联

将Launcher的实例对象作为接口实现类传递给LauncherModel,这里采用弱引用的形式持有Launcher的对象。这样在需要的时候,LauncherModel就可以通过mCallbacks调用相应接口方法,把数据传递给Launcher。
    至此,Model层与Controller层关联完毕。

public class LauncherModel extends BroadcastReceiverimplements LauncherAppsCompat.OnAppsChangedCallbackCompat {......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);}}......public interface Callbacks {public boolean setLoadOnResume();public int getCurrentWorkspaceScreen();public void startBinding();public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end,boolean forceAnimateIcons);public void bindScreens(ArrayList<Long> orderedScreenIds);public void bindAddScreens(ArrayList<Long> orderedScreenIds);public void bindFolders(LongArrayMap<FolderInfo> folders);public void finishBindingItems();public void bindAppWidget(LauncherAppWidgetInfo info);public void bindAllApplications(ArrayList<AppInfo> apps);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 bindWorkspaceComponentsRemoved(HashSet<String> packageNames, HashSet<ComponentName> components,UserHandleCompat user);public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos);public void notifyWidgetProvidersChanged();public void bindWidgetsModel(WidgetsModel model);public void bindSearchProviderChanged();public boolean isAllAppsButtonRank(int rank);public void onPageBoundSynchronously(int page);public void dumpLogsToLocalData();}......
}

二、View层与Controller层建立关联

MVC框架中建议View通过XML语言来描述,Launcher 的主布局通过 launcher.xml 来描述。setContentView(R.layout.launcher) ,将xml布局进行解析和加载。
    然后通过 setupViews() 进行一系列的 findViewById 操作,将控件们进行实例化,并给控件注册事件监听。Launcher 持有这些控件的实例对象的引用,用于通知View的显示与刷新。
    mDeviceProfile.layout(this) 根据参数对控件进行一些位置的调整。

public class Launcher extends Activityimplements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {......private void setupViews() {final DragController dragController = mDragController;mLauncherView = findViewById(R.id.launcher);mFocusHandler = (FocusIndicatorView) findViewById(R.id.focus_indicator);mDragLayer = (DragLayer) findViewById(R.id.drag_layer);mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);mWorkspace.setPageSwitchListener(this);mPageIndicators = mDragLayer.findViewById(R.id.page_indicator);mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);mWorkspaceBackgroundDrawable = getResources().getDrawable(R.drawable.workspace_bg);// Setup the drag layermDragLayer.setup(this, dragController);// Setup the hotseatmHotseat = (Hotseat) findViewById(R.id.hotseat);if (mHotseat != null) {mHotseat.setOnLongClickListener(this);}// Setup the overview panelsetupOverviewPanel();// Setup the workspacemWorkspace.setHapticFeedbackEnabled(false);mWorkspace.setOnLongClickListener(this);mWorkspace.setup(dragController);dragController.addDragListener(mWorkspace);// Get the search/delete barmSearchDropTargetBar = (SearchDropTargetBar)mDragLayer.findViewById(R.id.search_drop_target_bar);// Setup Apps and WidgetsmAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view);if (mLauncherCallbacks != null && mLauncherCallbacks.getAllAppsSearchBarController() != null) {mAppsView.setSearchBarController(mLauncherCallbacks.getAllAppsSearchBarController());} else {mAppsView.setSearchBarController(new DefaultAppSearchController());}// Setup the drag controller (drop targets have to be added in reverse order in priority)dragController.setDragScoller(mWorkspace);dragController.setScrollView(mDragLayer);dragController.setMoveTarget(mWorkspace);dragController.addDropTarget(mWorkspace);if (mSearchDropTargetBar != null) {mSearchDropTargetBar.setup(this, dragController);mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar());}if (TestingUtils.MEMORY_DUMP_ENABLED) {TestingUtils.addWeightWatcher(this);}}
}

三、Launcher通知LauncherModel 开始加载数据

首先判断一个变量mRestoring,该值用于确认Launcher启动时,是正常启动还是系统资源紧张导致Launcher被kill之后的重启。如果mRestoring为true,表示是被kill之后的重启,那么通过onSaveInstanceState保存下来的数据,恢复Launcher被kill之前的状态,不走这段重新加载数据的代码。如果mRestoring为false,则表示正常启动,那么进入数据加载的流程。
    这里的DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE是常量开关,默认是false,这里不用管它,数据加载走的代码是 mModel.startLoader(mWorkspace.getRestorePage())。
    数据加载完毕后,通过 LauncherModel.Callbacks 接口的回调方法把数据传递给Launcher,Launcher再把数据跟View绑定进行显示,这个过程比较复杂。后面会专门写一篇文章。

public class Launcher extends Activityimplements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {......@Overrideprotected void onCreate(Bundle savedInstanceState) {......//加载桌面用于显示的数据if (!mRestoring) {if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {// If the user leaves launcher, then we should just load items asynchronously when// they return.mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);} else {// We only load the page synchronously if the user rotates (or triggers a// configuration change) while launcher is in the foregroundmModel.startLoader(mWorkspace.getRestorePage());}}......}
}

至此,Launcher的框架及联系就over了。如有问题,欢迎讨论。
下一篇将会详细讲解 初始化过程中 图标和字体大小等显示相关参数是如何初始化和处理的

基于Android7.0的Launcher3源码分析(1)——框架设计分析相关推荐

  1. Android7.0 Phone应用源码分析(二) phone来电流程分析

    当有来电通知时,首先接收到消息的是Modem层,然后Medoem再上传给RIL层,RIL进程通过sokcet将消息发送给RILJ(framework层的RIL),同样进入RILJ的processRes ...

  2. Android7.0 Phone应用源码分析(四) phone挂断流程分析

    电话挂断分为本地挂断和远程挂断,针对这两种情况各做分析 先来看下本地挂断电话的时序图:

  3. Android7.0 Phone应用源码分析(三) phone拒接流程分析

  4. mosquitto客户端对象“struct mosquitto *mosq”管理下篇(mosquitto2.0.15客户端源码分析之四)

    文章目录 前言 5 设置网络参数 5.1 客户端连接服务器使用的端口号 `mosq->port` 5.2 指定绑定的网络地址 `mosq->bind_address` 5.3 客户端连接服 ...

  5. 《MySQL 8.0.22执行器源码分析(3.2)关于HashJoinIterator》

    在本文章之前,应该了解的概念: 连接的一些概念.NLJ.BNL.HashJoin算法. 目录 关于join连接 probe行保存概念 Hashjoin执行流程(十分重要) HashJoinIterat ...

  6. Android 8.1/9.0 MTK Camera源码分析之录像快门声音控制流程

    前面已经针对拍照快门声音控制流程进行了分析,接下来分析一下录像快门声音的控制流程. Android 8.1/9.0 MTK Camera源码分析之快门声音控制流程 这两篇文章其实都是相对于手机系统RO ...

  7. Android 8.1/9.0 MTK Camera源码分析之快门声音控制流程

    Android 8.1/9.0 MTK Camera源码分析之快门声音控制 在Android 8.1上mtk camera有控制快门声音的接口,但是并没有了控制录像快门声音的接口.之所以会有这个现象, ...

  8. 【OkHttp】OkHttp 源码分析 ( 网络框架封装 | OkHttp 4 迁移 | OkHttp 建造者模式 )

    OkHttp 系列文章目录 [OkHttp]OkHttp 简介 ( OkHttp 框架特性 | Http 版本简介 ) [OkHttp]Android 项目导入 OkHttp ( 配置依赖 | 配置 ...

  9. Activiti源码分析(框架、核心类。。。)

    Activiti源码分析(框架.核心类...) 目录 概 述 activiti源码分析(一)设计模式 总结: 相关工具如下: 分析: 小结: 参考资料和推荐阅读 LD is tigger foreve ...

最新文章

  1. ASFNU SC Day6
  2. iptables 流量速率控制
  3. JavaScript中的工厂函数vs构造函数vs class
  4. 反思各种型格人做事方法
  5. html课表插件,课程表插件Timetables(原创)
  6. 漫画:五分钟看懂车联网
  7. 登录drupal管理员_天气公司依靠Drupal来管理内容
  8. 五、扩展Orchard(五) Writing a Content Part
  9. 《zw版·Halcon-delphi系列原创教程》halconxlib控件列表
  10. 基于内容的视频标注——关键帧图象层标注
  11. x264 编码数配置
  12. Informatica的IDP理念:让业务部门成为真正数据受益者
  13. 《你只是看起来很努力》--读书笔记
  14. 关于微信H5自动播放视频-canvas实现逐帧动画效果加音频
  15. 格雷码和二进制的转换及典型例题(4bits格雷码计数器)
  16. OpenCV 视频捕捉
  17. 阿里员工爆出最好用的python库推荐!!--random随机数生成【原文附代码】
  18. 修改idea的头部文档注释信息
  19. 普通最小二乘法平面直线回归问题的三种实现(Python)
  20. ASR项目实战-从源码开始构建Kaldi

热门文章

  1. 【JS篇】通过点击或其他操作进行复制(链接,文字等)
  2. 《RETHINKING THE VALUE OF NETWORK PRUNING》论文笔记
  3. 阿里 easyExcel 动态写入数据,多级表头,单元格合并示例
  4. 宁波阿里云代理商:阿里巴巴CEO张勇:阿里云是一家云计算产品公司
  5. 【图像修复】基于matlab深度信息图像修复【含Matlab源码 2299期】
  6. OlivedPro 直播录制录屏软件可以实现24小时无人值守直播,支持录制 Youtube、Twitch、Tiktok、抖音、斗鱼、虎牙、Nimo 等平台 。这可以提高直播质量和管理效率。
  7. Docker-Dockerfile方式镜像的构建
  8. 关于如何在html网页中插入可以自动播放的背景音乐
  9. 10.8 - 每日一题 - 408
  10. sql判断整除_关系代数中除法的SQL实现