https://rainmonth.github.io/posts/A180702.html
Android Launcher3分析——开篇
简介
Launcher就是一个Activity,Launcher的源码中也是继承的Activity。直观体现就是手机的桌面,当我们打开手机的时候,手机的桌面就是Launcher,一个Activity,只是这个Activity做的事情比较多:
- View方面,可以左右滑动,可以响应长按操作;
- 逻辑方面,可以承载手机中所有应用的快捷方式,是其他程序的入口;
总的来说,Launcher就是一个包含了许多自定义控件的复杂Activity。
整体上看Launcher3
Android四大组件一应俱全,可见Launcher3是一个综合性较强的项目
Activity(6个)
- com.android.launcher3.Launcher(主要的Activity)
- com.android.launcher3.ToggleWeightWatcher
- com.android.launcher3.WallpaperPickerActivity
- com.android.launcher3.WallpaperCropActivity
- com.android.launcher3.SettingsActivity
- com.android.launcher3.MemoryDumpActivity
Service(1个)
- com.android.launcher3.MemoryTracker
BroadcastReceiver(4个)
- com.android.launcher3.WallpaperChangedReceiver
- com.android.launcher3.InstallShortcutReceiver
- com.android.launcher3.AppWidgetsRestoredReceiver
- com.android.launcher3.StartupReceiver
ContentProvider(1个)
- com.android.launcher3.LauncherProvider
如何定义一个Launcher
Android定义一个Launcher很简单,要定义成Launcher的Activity代码如下:
1 | <activity |
注意,上面intent-filter的具体含义:
- android.intent.action.MAIN决定应用程序最先启动的Activity;
- android.intent.category.HOME决定设备启动后第一个启动的Activity(通常要更改framework层的设置才能使之生效,因为国内系统定制化严重,各家手机厂商的ROM都提供了自己的Launcher)
- android.intent.category.LAUNCHER决定应用程序是否显示在程序列表里;
- android.intent.category.DEFAULT,决定可以接受隐式intent的Activity在没有传递intent-filter时是否能匹配成功,添加了该项的能匹配成功,反之匹配失败(当然,如果Activity时应用最先启动的Activity就不需要这个了)
- android.intent.category.MONKEY,决定Activity是否能被monkey或其他自动化测试工具进行访问测试
先看Launcher对应的布局文件launcher.xml,如下:
launcher.xml
1 |
|
根布局层次结构:
1 | LauncherRootView extends InsettableFrameLayout |
Launcher中核心类的简单说明
具体说明请点击相应连接
- Launcher:主界面Activity,最核心且唯一的Activity;
- LauncherAppState:单例对象,主要有如下作用:
- 初始化InvariantDeviceProfile、IconCache、WidgetPreviewLoader、LauncherModel等;
- 注册广播,用来处理本地配置变化、搜索数据库(global search provider)变化、应用的安装与卸载等;
- InvariantDeviceProfile:一些不变的设备相关参数管理类,包含横竖屏的两种DeviceProfile;
- IconCache:应用程序图标缓存类,里面用数据库存储了应用的icon及title等的缓存信息;
- WidgetPreviewLoader:加载Widget信息数据库,里面用数据库存储了Widget的信息
- LauncherModel:在内存中保存Launcher的状态,提供读写数据库的API,其内部类LoaderTask用来加载Launcher的内容(包括workspace icons、widgets和all apps icons)
- LauncherAppsCompat:兼容抽象基类,用来获取已安装的App列表;
- UserManagerCompat:兼容抽象基类,用来处理不通版本下的用户管理;
- DragController:拖拽事件控制类,拖拽事件的处理逻辑在这里实现
- LauncherStateTransitionAnimation:Launcher的动画导演,负责安排不同状态切换之间的动画处理
- AppWidgetManagerCompat:兼容抽象基类,负责处理不通版本下应用和Widget管理
- LauncherAppWidgetHost:继承子AppWidgetHost,顾名思义,AppWidgetHost是桌面app、widget等的宿主,之所以继承是为了LauncherAppWidgetHostView能更好的处理长按事件;
- FocusIndicatorView:一个实现了View.OnFocusChangeListener的View(具体作用上不清楚)
- DragLayer:一个用来协调子View拖拽事件的ViewGroup,实际上事件的分发拦截等是在DragController,因为DragLayer持有DragController的实例,并调用了setup方法初始化了它;
- Workspace:一个包含了壁纸和有限数量的页面的较大空间
Launcher主流程
先分析生命周期函数
onCreate()
主要流程都在onCreate中,如下(采用代码中添加注释的方法说明,省略次要代码):
1 |
|
可以看到,在设置布局之后,进行了View的初始化、View的事件绑定等,然后根据DeviceProfile(设备描述类,定义了Launcher在不同设备、不公状态下的以下常量等)的layout方法为初始化的View添加上布局参数。参数设置完成后,就进入了数据加载阶段,数据加载是通过LauncherModel的内部类LoaderTask来完成的(根据当前的配置,来选择时同步加载数据还是异步加载数据)。接下来就是控制Launcher Intro Screen的显示与否了,显示的话,就显示Intro Screen,不显示就进入else部分,显示Launcher Clings(其实就是首次运行Launcher时的一些关于Launcher用途的说明)
Launcher 代码中关于mLauncherCallbacks部分,由于mLauncherCallbacks的赋值操作必须调用setLauncherCallbacks来完成,但该函数只在LauncherExtension中才调用,所以如果要对Launcher做扩展,需要了解这部分代码,否则,可以忽略。
onResume()
onResume()中主要进行的是视图显示状态的恢复、依次执行Runnable任务(包括BindAllApplicationRunnable、BindPackagesUpdatesRunnable以及UpdateOrientationRunnable)、恢复App或App Shortcut的状态,必要的时候还会重新生成workspace上的Widget和QSB等。
Launcher中有一个waitUntilResume函数,字面意思“直到onResume执行才。。。。”,先看看其具体代码:
1 |
|
还有一个重载方法:
1 | private boolean waitUntilResume(Runnable run) { |
在如下几个地方调用了:
- Launcher(直接调用的地方)
- bindAllApplications(final ArrayList
apps) - bindAllPackages(final WidgetsModel model)
- onSettingsChanged(String settings, boolean value)
- bindAllApplications(final ArrayList
- Launcher(间接调用的地方,调用的是重载的方法)
- bindAppsAdded
- bindAppsUpdated
- bindAppWidget
- bindComponentsRemoved
- bindFolders
- bindItems
- bindRestoreItemsChange
- bindShortcutsChanged
- bindWidgetsRestored
- finishBindingItems
可见,与绑定有关的runnable都是在onResume的时候执行的,那么在这些runnable到底都做了什么,有什么功能,如何实现这些功能的呢?这里先提如下几个问题,我们带着问题读代码,马上就能得到答案:
- 在这些runnable执行之前,又做了什么了?
- 我们都要绑定写什么对象?
- 我们要绑定的这些对象怎么得到的?
- 我们要把这些对象绑定到哪去?
现在依次解答上面的个问题,其实在onResume之前就已经被LauncherModel(数据处理的核心类)安排好了,现在来答上面的问题:
在这些runnable执行之前,做了些什么?
onCreate里面调用了Launcher的startLoader方法,开方法会开启其内部类LoaderTask的run方法来进行如下操作:
- loadAndBindWorkspace,即加载并绑定workspace;
- loadWrokspace
- bindWorkspace
- loadAndBindAllApps,即加载并绑定All apps;
- loadAndBindWorkspace,即加载并绑定workspace;
我们都要绑定写什么对象?
想想Launcher上都有什么,显然App、AppShortcut、widget、Folder等都可能有,所以要绑定的当然是这些对象。
我们要绑定的这些对象怎么得到的?
通过LauncherModel的内部类LoaderTask来得到这些对象
我们要把这些对象绑定到哪去?
该放哪儿去放哪儿去。由于Launcher中的对象(App、AppShortcut、Widget、Folder等)无论是在Desktop(即桌面)或Hotseat(即底部的图标栏)都有相应的坐标点(cellX,cellY)及占地面积(spanX、spanY),我们只要将其按对应坐标对号入座即可。当然特需情况(如图标遮挡什么的)需要特殊处理。
其实,2、3、4里面要做的任务都是在1里面完成的,可见数据处理核心还是LauncherModel里面。接下来的文章会对LauncherModel做详细分析。