Android Launcher源码结构

Launcher 是 Android手机开启后第一个运行的 应用程序,也叫Home,或者叫做手机桌面。

本文介绍的是4.1源码的launcher2 app. Android41\packages\apps\Launcher2

首先找到主Activity,打开AndroidManifest.xml  入口是  com.android.launcher2.Launcher 这个类

Launcher 主界面包含 wallpaper墙纸,work_screen屏幕, 最底部的hotseat, 以及all apps.

onCreate方法里面 主要初始化一些对象,包括拖拽对象,hotSeat,  墙纸大小设置,

打开主布局文件launcher.xml

外层是一个Framelayout叠层 com.android.launcher2.DragLayer

com.android.launcher2.Workspace.java 为主要Home Screen

手机里面包含了多个屏的滑动,一共有5个

[html] view plaincopy

  1. <com.android.launcher2.Workspace
  2. android:id="@+id/workspace"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:paddingLeft="@dimen/workspace_left_padding"
  6. android:paddingRight="@dimen/workspace_right_padding"
  7. android:paddingTop="@dimen/workspace_top_padding"
  8. android:paddingBottom="@dimen/workspace_bottom_padding"
  9. launcher:defaultScreen="2"
  10. launcher:cellCountX="@integer/cell_count_x"
  11. launcher:cellCountY="@integer/cell_count_y"
  12. launcher:pageSpacing="@dimen/workspace_page_spacing"
  13. launcher:scrollIndicatorPaddingLeft="@dimen/qsb_bar_height"
  14. launcher:scrollIndicatorPaddingRight="@dimen/button_bar_height">
  15. <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
  16. <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
  17. <include android:id="@+id/cell3" layout="@layout/workspace_screen" />
  18. <include android:id="@+id/cell4" layout="@layout/workspace_screen" />
  19. <include android:id="@+id/cell5" layout="@layout/workspace_screen" />
  20. </com.android.launcher2.Workspace>

每一个屏叫做 com.android.launcher2.CellLayout.java

[html] view plaincopy

  1. <com.android.launcher2.CellLayout
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
  4. android:layout_width="wrap_content"
  5. android:layout_height="wrap_content"
  6. android:paddingLeft="@dimen/cell_layout_left_padding"
  7. android:paddingRight="@dimen/cell_layout_right_padding"
  8. android:paddingTop="@dimen/cell_layout_top_padding"
  9. android:paddingBottom="@dimen/cell_layout_bottom_padding"
  10. android:hapticFeedbackEnabled="false"
  11. launcher:cellWidth="@dimen/workspace_cell_width"
  12. launcher:cellHeight="@dimen/workspace_cell_height"
  13. launcher:widthGap="@dimen/workspace_width_gap"
  14. launcher:heightGap="@dimen/workspace_height_gap"
  15. launcher:maxGap="@dimen/workspace_max_gap" />

Workspace.java 是继承ViewGroup

CellLayout 也是继承ViewGroup

Workspace.java 对象在Launcher里面做了一些初始化。

首先在setupViews方法里面 获取了对象引用。还包含了shortcut添加与删除操作。

在Workspace.java中onTouchEvent方法中,监听了屏幕的滑动操作,比如长按,拖动app图标。

拖动用DragController.java类,处理拖动计算坐标。

Android Launcher源码加载APP流程

分析Android Launcher源码中的一些重要类之间的关系,基本的加载流程。先来看一个类图

Launcher.java 是主Activity 在onCreate方法里面初始化了LauncherMode实例.

[java] view plaincopy

  1. LauncherApplication app = ((LauncherApplication)getApplication());

[java] view plaincopy

  1. mModel = app.setLauncher(this);

直接进入LauncherApplication.java的方法

[java] view plaincopy

  1. LauncherModel setLauncher(Launcher launcher) {
  2. mModel.initialize(launcher);
  3. return mModel;
  4. }

这里的mModel就是LauncherModel类了,LauncherModel扮演者重要的角色,实际上是个广播,监控app的安装,改变,和卸载另外就是加载所有app信息

这里Launcher实现了Callbacks接口,直接加入到callbacks列表中,后面的很多功能都要靠它回调处理

[java] view plaincopy

  1. public void initialize(Callbacks callbacks) {
  2. synchronized (mLock) {
  3. mCallbacks = new WeakReference<Callbacks>(callbacks);
  4. }
  5. }

在Launcher.java  onCreate的代码中看下面的一段代码.

[java] view plaincopy

  1. if (!mRestoring) {
  2. if (sPausedFromUserAction) {
  3. // If the user leaves launcher, then we should just load items asynchronously when
  4. // they return.
  5. mModel.startLoader(true, -1);
  6. } else {
  7. // We only load the page synchronously if the user rotates (or triggers a
  8. // configuration change) while launcher is in the foreground
  9. mModel.startLoader(true, mWorkspace.getCurrentPage());
  10. }
  11. }

源码中注释也显示了2中情况,一种是当前用户的界面不是桌面可能是某个应用程序界面 这样我们通过后台异步加载。

另外一种情况是 刷新当前页面的app信息数据.

现在定位到LauncherMode.java源码中startLoader方法 部分代码如下.

[java] view plaincopy

  1. if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) {
  2. mLoaderTask.runBindSynchronousPage(synchronousBindPage);//同步加载当前页
  3. } else {
  4. sWorkerThread.setPriority(Thread.NORM_PRIORITY);
  5. sWorker.post(mLoaderTask);//异步加载
  6. }

和上面讲的一样,加载当前页面信息还是更新所有信息. synchronousBindPage就是当前用户滑动到第几个屏幕,一共是5个屏幕.

我们用到了LoaderTask这个类是一个Runnable实现.  这个地方用的是HandlerThread (这个类我以前没用过,一直是自己获取Looper再处理的 )

这个类就是具体载入appinfo,appwidget,  folder的信息了。

我们看看LoaderTask run方法实现. 注意 LoaderTask 是LauncherMode的内部类.

[java] view plaincopy

  1. keep_running: {
  2. // Elevate priority when Home launches for the first time to avoid
  3. // starving at boot time. Staring at a blank home is not cool.
  4. synchronized (mLock) {
  5. if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
  6. (mIsLaunching ? "DEFAULT" : "BACKGROUND"));
  7. android.os.Process.setThreadPriority(mIsLaunching
  8. ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
  9. }
  10. if (loadWorkspaceFirst) {
  11. if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
  12. loadAndBindWorkspace();//第一次载入
  13. } else {
  14. if (DEBUG_LOADERS) Log.d(TAG, "step 1: special: loading all apps");
  15. loadAndBindAllApps(); //更新数据
  16. }
  17. if (mStopped) {
  18. break keep_running;
  19. }
  20. // Whew! Hard work done.  Slow us down, and wait until the UI thread has
  21. // settled down.
  22. synchronized (mLock) {
  23. if (mIsLaunching) {
  24. if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");
  25. android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
  26. }
  27. }
  28. waitForIdle();
  29. // second step
  30. if (loadWorkspaceFirst) {
  31. if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
  32. loadAndBindAllApps();
  33. } else {
  34. if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace");
  35. loadAndBindWorkspace();
  36. }
  37. // Restore the default thread priority after we are done loading items
  38. synchronized (mLock) {
  39. android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
  40. }
  41. }

按照源码的注释 是第一次加载workspace , 第二次加载所有的app 信息,你会发现 loadAndBindAllApps方法和loadAndBindWorkspace 2次调用是倒着的。

我的理解就是  先执行加载workspace 再执行加载all apps.

我们定位到 loadAndBindWorkspace方法中,

[java] view plaincopy

  1. if (!mWorkspaceLoaded) {
  2. loadWorkspace();
  3. synchronized (LoaderTask.this) {
  4. if (mStopped) {
  5. return;
  6. }
  7. mWorkspaceLoaded = true;
  8. }
  9. }

如果workspace还没加载就调用loadWorkspace方法. 定位到这个方法里面,一目了然发现 都是直接获取应用程序信息了包括widget.

直接调用了 PackageManager,AppWidgetManager,ContentResolver 获取信息.

读取信息保存后 我们需要与workspace汇合,这个地方是调用bindWorkspace

在这个方面里面我们会发现使用前面说的callbacks进行回调完整数据与View的绑定显示.

[java] view plaincopy

  1. /**
  2. * Binds all loaded data to actual views on the main thread.
  3. */
  4. private void bindWorkspace(int synchronizeBindPage) {
  5. final long t = SystemClock.uptimeMillis();
  6. Runnable r;
  7. // Don‘t use these two variables in any of the callback runnables.
  8. // Otherwise we hold a reference to them.
  9. final Callbacks oldCallbacks = mCallbacks.get();
  10. if (oldCallbacks == null) {
  11. // This launcher has exited and nobody bothered to tell us.  Just bail.
  12. Log.w(TAG, "LoaderTask running with no launcher");
  13. return;
  14. }
  15. final boolean isLoadingSynchronously = (synchronizeBindPage > -1);
  16. final int currentScreen = isLoadingSynchronously ? synchronizeBindPage :
  17. oldCallbacks.getCurrentWorkspaceScreen();
  18. // Load all the items that are on the current page first (and in the process, unbind
  19. // all the existing workspace items before we call startBinding() below.
  20. unbindWorkspaceItemsOnMainThread();
  21. ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>();
  22. ArrayList<LauncherAppWidgetInfo> appWidgets =
  23. new ArrayList<LauncherAppWidgetInfo>();
  24. HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>();
  25. HashMap<Long, ItemInfo> itemsIdMap = new HashMap<Long, ItemInfo>();
  26. synchronized (sBgLock) {
  27. workspaceItems.addAll(sBgWorkspaceItems);
  28. appWidgets.addAll(sBgAppWidgets);
  29. folders.putAll(sBgFolders);
  30. itemsIdMap.putAll(sBgItemsIdMap);
  31. }
  32. ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>();
  33. ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>();
  34. ArrayList<LauncherAppWidgetInfo> currentAppWidgets =
  35. new ArrayList<LauncherAppWidgetInfo>();
  36. ArrayList<LauncherAppWidgetInfo> otherAppWidgets =
  37. new ArrayList<LauncherAppWidgetInfo>();
  38. HashMap<Long, FolderInfo> currentFolders = new HashMap<Long, FolderInfo>();
  39. HashMap<Long, FolderInfo> otherFolders = new HashMap<Long, FolderInfo>();
  40. // Separate the items that are on the current screen, and all the other remaining items
  41. filterCurrentWorkspaceItems(currentScreen, workspaceItems, currentWorkspaceItems,
  42. otherWorkspaceItems);
  43. filterCurrentAppWidgets(currentScreen, appWidgets, currentAppWidgets,
  44. otherAppWidgets);
  45. filterCurrentFolders(currentScreen, itemsIdMap, folders, currentFolders,
  46. otherFolders);
  47. sortWorkspaceItemsSpatially(currentWorkspaceItems);
  48. sortWorkspaceItemsSpatially(otherWorkspaceItems);
  49. // Tell the workspace that we‘re about to start binding items
  50. r = new Runnable() {
  51. public void run() {
  52. Callbacks callbacks = tryGetCallbacks(oldCallbacks);
  53. if (callbacks != null) {
  54. callbacks.startBinding();
  55. }
  56. }
  57. };
  58. runOnMainThread(r);
  59. // Load items on the current page
  60. bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
  61. currentFolders, null);
  62. if (isLoadingSynchronously) {
  63. r = new Runnable() {
  64. public void run() {
  65. Callbacks callbacks = tryGetCallbacks(oldCallbacks);
  66. if (callbacks != null) {
  67. callbacks.onPageBoundSynchronously(currentScreen);
  68. }
  69. }
  70. };
  71. runOnMainThread(r);
  72. }
  73. // Load all the remaining pages (if we are loading synchronously, we want to defer this
  74. // work until after the first render)
  75. mDeferredBindRunnables.clear();
  76. bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
  77. (isLoadingSynchronously ? mDeferredBindRunnables : null));
  78. // Tell the workspace that we‘re done binding items
  79. r = new Runnable() {
  80. public void run() {
  81. Callbacks callbacks = tryGetCallbacks(oldCallbacks);
  82. if (callbacks != null) {
  83. callbacks.finishBindingItems();
  84. }
  85. // If we‘re profiling, ensure this is the last thing in the queue.
  86. if (DEBUG_LOADERS) {
  87. Log.d(TAG, "bound workspace in "
  88. + (SystemClock.uptimeMillis()-t) + "ms");
  89. }
  90. mIsLoadingAndBindingWorkspace = false;
  91. }
  92. };
  93. if (isLoadingSynchronously) {
  94. mDeferredBindRunnables.add(r);
  95. } else {
  96. runOnMainThread(r);
  97. }
  98. }

[java] view plaincopy

*************************************************************************************************

时间: 2024-08-24 08:33:19

Android Launcher源码结构的相关文章

Android Launcher源码研究(二) 加载app流程1

今天主要分析Android Launcher源码中的一些重要类之间的关系,基本的加载流程.先来看一个类图 Launcher.java 是主Activity 在onCreate方法里面初始化了LauncherMode实例. LauncherApplication app = ((LauncherApplication)getApplication()); mModel = app.setLauncher(this); 直接进入LauncherApplication.java的方法 Launcher

Android Launcher源码研究(三) 加载app流程2

接上次的. 首先Launcher实现了LauncherModel.Callbacks接口,APP信息数据加载成功后 ,回调接口把app信息显示到Launcher的 workspace界面上,这个过程代码里面称为bind. 下面是个类调用过程的时序图,不是很标准,不过能表达基本调用顺序帮助我们理解. 首先就是Launcher OnCreate中调用LauncherMode startLoader方法,这里只看异步的方式 就是当前的页面下标为-1,加载所有app信息 mWorkspace.getCu

android Launcher源码解析07:Workspace 02——设置壁纸

http://blog.csdn.net/xianming01/article/details/8280232

2016年最牛逼的分类Android项目源码免费一次性打包下载!

之前发过一个帖子,但是那个帖子有点问题我就重新发一个吧,下面的源码是我从今年开始不断整理源码区和其他网站上的安卓例子源码,目前总共有810套左右,根据实现的功能被我分成了100多个类,总共接近2.5G,还在不断更新.初学者可以快速方便的找到自己想要的例子,大神也可以看一下别人的方法实现.虽然的例子都是我一个人辛辛苦苦花了很多时间和精力整理的,但是既然这些例子是来自于社区那就让他们免费回归社区吧,(是的!特么的不要一分钱!最看不起那些挂羊头卖狗的)你可以在本帖里面按Ctrl+F查找你需要的关键字,

Android应用程序启动过程——Launcher源码分析

当我们在Launcher界面单击一个应用程序图标时就会启动一个程序,那这一个过程究竟发生了些哪样呢?让我们跟踪Launcher源码来分析一下吧. 先上流程图: step1.追踪Launcher  从源码中我们可以发现Launcher其实也是一个程序,它继承于Activity.找到该文件中的onCreate()方法,代码片段如下: protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceSta

Android从源码框架思路开始

做开发以来,总有那么一个习惯喜欢阅读源码,深入了解源码的设计用意所在.源码对于开发人员有多么重要,想必喜欢查看 开发源码的开发人员都明白,如:Android开发中,Framework及底层开发对Android源码的依赖就蛮高了,有很多的需求是需要自己 再源码的基础上进行修改,如:现在的Android智能手机,我要修改一下顶的样式排列,那就需要下载源码,自己修改,然后自己刷 机重装系统.从Java到Android,再到IOS都有为开发人员提供源码这一个选项,但是问题就来了,如何获取公司提供的源码呢

【流媒体开发】VLC Media Player - Android 平台源码编译 与 二次开发详解 (提供详细800M下载好的编译源码及eclipse可调试播放器源码下载)

作者 : 韩曙亮  博客地址 : http://blog.csdn.net/shulianghan/article/details/42707293 转载请注明出处 : http://blog.csdn.net/shulianghan VLC 二次开发 视频教程 : http://edu.csdn.net/course/detail/355 博客总结 : -- 本博客目的 : 让 Android 开发者通过看本博客能够掌握独立移植 VLC Media Player 核心框架到自己的 app 中,

完美高仿精仿京东商城手机客户端android版源码

完美高仿精仿京东商城手机客户端android版源码,喜欢的朋友可以下载吧. 源码下载: http://code.662p.com/view/4876.html AndroidManifest.xml <?xml version="1.0" encoding="utf-8" ?> - <manifest android:versionCode="6952" android:versionName="2.7.0"

Android debuggerd 源码分析

debuggerd 简介 Android系统自带一个实用的程序异常退出的诊断daemon debuggerd.此进程可以侦测到程序崩溃,并将崩溃时的进程状态信息输出到文件和串口中,以供开发人员分析调试使用.Debuggerd的数据被保存在/data/tombstone/目录下,共可保存10个文件,当超过10个时,会覆盖重写最早生产的文件.串口中,则直接用DEBUG的tag,输出logcat信息. Linux kernel有自己的一套signal机制,在应用程序崩溃时,通常系统内核都会发送sign