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个
- <com.android.launcher2.Workspace
- android:id="@+id/workspace"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingLeft="@dimen/workspace_left_padding"
- android:paddingRight="@dimen/workspace_right_padding"
- android:paddingTop="@dimen/workspace_top_padding"
- android:paddingBottom="@dimen/workspace_bottom_padding"
- launcher:defaultScreen="2"
- launcher:cellCountX="@integer/cell_count_x"
- launcher:cellCountY="@integer/cell_count_y"
- launcher:pageSpacing="@dimen/workspace_page_spacing"
- launcher:scrollIndicatorPaddingLeft="@dimen/qsb_bar_height"
- launcher:scrollIndicatorPaddingRight="@dimen/button_bar_height">
-
- <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
- <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
- <include android:id="@+id/cell3" layout="@layout/workspace_screen" />
- <include android:id="@+id/cell4" layout="@layout/workspace_screen" />
- <include android:id="@+id/cell5" layout="@layout/workspace_screen" />
- </com.android.launcher2.Workspace>
每一个屏叫做 com.android.launcher2.CellLayout.java
- <com.android.launcher2.CellLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
-
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="@dimen/cell_layout_left_padding"
- android:paddingRight="@dimen/cell_layout_right_padding"
- android:paddingTop="@dimen/cell_layout_top_padding"
- android:paddingBottom="@dimen/cell_layout_bottom_padding"
- android:hapticFeedbackEnabled="false"
-
- launcher:cellWidth="@dimen/workspace_cell_width"
- launcher:cellHeight="@dimen/workspace_cell_height"
- launcher:widthGap="@dimen/workspace_width_gap"
- launcher:heightGap="@dimen/workspace_height_gap"
- 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实例.
- LauncherApplication app = ((LauncherApplication)getApplication());
- mModel = app.setLauncher(this);
直接进入LauncherApplication.java的方法
- LauncherModel setLauncher(Launcher launcher) {
- mModel.initialize(launcher);
- return mModel;
- }
这里的mModel就是LauncherModel类了,LauncherModel扮演者重要的角色,实际上是个广播,监控app的安装,改变,和卸载另外就是加载所有app信息
这里Launcher实现了Callbacks接口,直接加入到callbacks列表中,后面的很多功能都要靠它回调处理
- public void initialize(Callbacks callbacks) {
- synchronized (mLock) {
- mCallbacks = new WeakReference<Callbacks>(callbacks);
- }
- }
在Launcher.java onCreate的代码中看下面的一段代码.
- if (!mRestoring) {
- if (sPausedFromUserAction) {
-
-
- mModel.startLoader(true, -1);
- } else {
-
-
- mModel.startLoader(true, mWorkspace.getCurrentPage());
- }
- }
源码中注释也显示了2中情况,一种是当前用户的界面不是桌面可能是某个应用程序界面 这样我们通过后台异步加载。
另外一种情况是 刷新当前页面的app信息数据.
现在定位到LauncherMode.java源码中startLoader方法 部分代码如下.
- if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) {
- mLoaderTask.runBindSynchronousPage(synchronousBindPage);
- } else {
- sWorkerThread.setPriority(Thread.NORM_PRIORITY);
- sWorker.post(mLoaderTask);
- }
和上面讲的一样,加载当前页面信息还是更新所有信息. synchronousBindPage就是当前用户滑动到第几个屏幕,一共是5个屏幕.
我们用到了LoaderTask这个类是一个Runnable实现. 这个地方用的是HandlerThread (这个类我以前没用过,一直是自己获取Looper再处理的 )
这个类就是具体载入appinfo,appwidget, folder的信息了。
我们看看LoaderTask run方法实现. 注意 LoaderTask 是LauncherMode的内部类.
- keep_running: {
-
-
- synchronized (mLock) {
- if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
- (mIsLaunching ? "DEFAULT" : "BACKGROUND"));
- android.os.Process.setThreadPriority(mIsLaunching
- ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
- }
- if (loadWorkspaceFirst) {
- if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
- loadAndBindWorkspace();
- } else {
- if (DEBUG_LOADERS) Log.d(TAG, "step 1: special: loading all apps");
- loadAndBindAllApps();
- }
-
- if (mStopped) {
- break keep_running;
- }
-
-
-
- synchronized (mLock) {
- if (mIsLaunching) {
- if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");
- android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- }
- }
- waitForIdle();
-
-
- if (loadWorkspaceFirst) {
- if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
- loadAndBindAllApps();
- } else {
- if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace");
- loadAndBindWorkspace();
- }
-
-
- synchronized (mLock) {
- android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
- }
- }
按照源码的注释 是第一次加载workspace , 第二次加载所有的app 信息,你会发现 loadAndBindAllApps方法和loadAndBindWorkspace 2次调用是倒着的。
我的理解就是 先执行加载workspace 再执行加载all apps.
我们定位到 loadAndBindWorkspace方法中,
- if (!mWorkspaceLoaded) {
- loadWorkspace();
- synchronized (LoaderTask.this) {
- if (mStopped) {
- return;
- }
- mWorkspaceLoaded = true;
- }
- }
如果workspace还没加载就调用loadWorkspace方法. 定位到这个方法里面,一目了然发现 都是直接获取应用程序信息了包括widget.
直接调用了 PackageManager,AppWidgetManager,ContentResolver 获取信息.
读取信息保存后 我们需要与workspace汇合,这个地方是调用bindWorkspace
在这个方面里面我们会发现使用前面说的callbacks进行回调完整数据与View的绑定显示.
- private void bindWorkspace(int synchronizeBindPage) {
- final long t = SystemClock.uptimeMillis();
- Runnable r;
-
-
-
- final Callbacks oldCallbacks = mCallbacks.get();
- if (oldCallbacks == null) {
-
- Log.w(TAG, "LoaderTask running with no launcher");
- return;
- }
-
- final boolean isLoadingSynchronously = (synchronizeBindPage > -1);
- final int currentScreen = isLoadingSynchronously ? synchronizeBindPage :
- oldCallbacks.getCurrentWorkspaceScreen();
-
-
-
- unbindWorkspaceItemsOnMainThread();
- ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>();
- ArrayList<LauncherAppWidgetInfo> appWidgets =
- new ArrayList<LauncherAppWidgetInfo>();
- HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>();
- HashMap<Long, ItemInfo> itemsIdMap = new HashMap<Long, ItemInfo>();
- synchronized (sBgLock) {
- workspaceItems.addAll(sBgWorkspaceItems);
- appWidgets.addAll(sBgAppWidgets);
- folders.putAll(sBgFolders);
- itemsIdMap.putAll(sBgItemsIdMap);
- }
-
- ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>();
- ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>();
- ArrayList<LauncherAppWidgetInfo> currentAppWidgets =
- new ArrayList<LauncherAppWidgetInfo>();
- ArrayList<LauncherAppWidgetInfo> otherAppWidgets =
- new ArrayList<LauncherAppWidgetInfo>();
- HashMap<Long, FolderInfo> currentFolders = new HashMap<Long, FolderInfo>();
- HashMap<Long, FolderInfo> otherFolders = new HashMap<Long, FolderInfo>();
-
-
- filterCurrentWorkspaceItems(currentScreen, workspaceItems, currentWorkspaceItems,
- otherWorkspaceItems);
- filterCurrentAppWidgets(currentScreen, appWidgets, currentAppWidgets,
- otherAppWidgets);
- filterCurrentFolders(currentScreen, itemsIdMap, folders, currentFolders,
- otherFolders);
- sortWorkspaceItemsSpatially(currentWorkspaceItems);
- sortWorkspaceItemsSpatially(otherWorkspaceItems);
-
-
- r = new Runnable() {
- public void run() {
- Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.startBinding();
- }
- }
- };
- runOnMainThread(r);
-
-
- bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
- currentFolders, null);
- if (isLoadingSynchronously) {
- r = new Runnable() {
- public void run() {
- Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.onPageBoundSynchronously(currentScreen);
- }
- }
- };
- runOnMainThread(r);
- }
-
-
-
- mDeferredBindRunnables.clear();
- bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
- (isLoadingSynchronously ? mDeferredBindRunnables : null));
-
-
- r = new Runnable() {
- public void run() {
- Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.finishBindingItems();
- }
-
-
- if (DEBUG_LOADERS) {
- Log.d(TAG, "bound workspace in "
- + (SystemClock.uptimeMillis()-t) + "ms");
- }
-
- mIsLoadingAndBindingWorkspace = false;
- }
- };
- if (isLoadingSynchronously) {
- mDeferredBindRunnables.add(r);
- } else {
- runOnMainThread(r);
- }
- }
*************************************************************************************************