Launcher是什么
首先来说说Launcher是个什么东西,我想刚接触到这个东西的时候大家都是一头雾水,然后会自然而然的问一个问题,总是听到有人说Launcher,那Launcher是个什么东西呢?其实Launcher就是一个Activity,Launcher的源码中也是继承的Activity。体现在直观方面就是手机的桌面,当我们打开手机的时候,手机的桌面就是Launcher,一个Activity,只是这个Activity做的事情比较多,它在View方面可以左右滑动,可以响应长按等操作;在逻辑方面,它可以承载手机中所有应用的快捷方式,起着一个程序入口的作用。简单说,Launcher就是一个复杂的Activity。
怎么调用Launcher
既然Launcher是一个Activity,是一开机由系统调用的,那么系统是怎么识别这个特殊的Activity的呢?是由清单文件的配置来识别的,如下:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
你可以自己写一个带有这样过滤器的Activity,在模拟器上运行之后,点击home键之后,系统会自动提示你进入那个Launcher。第三方的ROM我测试了几款都不可以,这也可以想通。由于公司的Launcher是直接将Launcher源码拷贝过来,在这上面做的改动,所以这里只是说系统的Launcher的加载过程。
关于Launcher的相关类
我想只要是开发过的人,当第一次看一个Activity的时候首先要看的就是这个Activity的布局文件了,由于工作中研究的大都是Launcher的加载逻辑,对布局方面涉及不多,所以这里就只罗列出Launcher中相关的类和对应的作用。
布局方面:
Draglayer:用于处理拖拽事件,当拖动桌面的一个View的时候,这个View就放到了DragLayer
Workspace:手机屏幕上左右滑的屏幕,这个View表示可以滑动的单独一屏
PagedView:Workspace的父类,表示整个可以滑动的每一块屏幕
PageIndicator:表示桌面下方表示有几个页面的固定不动的小圆点
Hotseat:表示桌面下方不随这屏幕滑动而改变的一行
SearchDropTargetBar:表示屏幕上方的搜索框,在长按图标的时候会变成删除图标
常用类
LauncherModel :保存桌面运行时的状态信息,它中的内部类LoaderTask完成了在桌面启动时读取数据库数据,并把图标和小工具添加到桌面
BubblTextView:左面上的图标都是基于这个类
DragController:拖拽的控制类,具体的拖拽处理都放到这个类中
LauncherAppState:在项目启动的时候,这个类初始化一些必要的对象,如注册广播等
ItemInfo:存储每一个应用的信息类
Launcher的加载流程
找到onCreate()方法,开始一步一步的走流程。首先是这样的一段代码
if (DEBUG_STRICT_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
虽然在代码中你会看到标记DEBUG_STRICT_MODE已经置为了false,不会走下面的代码,但是作为一个有责任的程序员当然要一探究竟,经查阅,StricMode这个类是提供给开发这测试用的一个类,它包含可多种不同的策略,其中还有不同的规则,主要作用就是用来优化代码,对于提供的不同优化策略,有关于磁盘读和写的,有网络的,有内存的,开发者可以自己控制开关,结果会通过log的形式打印出来。值得注意的是,这个类只是在测试阶段来使用,当项目上线的时候就可以关闭这个功能。
接着往下走:
LauncherAppState.setApplicationContext(getApplicationContext());
LauncherAppState app = LauncherAppState.getInstance();
这里主要是为了LauncherAPPState这个对象,这个对象的作用,在上边也提到过,主要是在Launcher启动的时候初始化一些必要的对象,如注册广播啊,初始化一些内容提供者啊等等。
Point smallestSize = new Point();
Point largestSize = new Point();
Point realSize = new Point();
Display display = getWindowManager().getDefaultDisplay();
display.getCurrentSizeRange(smallestSize, largestSize);
display.getRealSize(realSize);
DisplayMetrics dm = new DisplayMetrics();
display.getMetrics(dm);
这段代码不用说也都看的懂,因为系统之后一套Launcher逻辑,而Android手机的尺寸是很多的,所以要提前获取到屏幕的尺寸,这段代码就是用来获取屏幕的尺寸的。
// Lazy-initialize the dynamic grid
//生成动态网格,保存到DeviceProfile中
DeviceProfile grid = app.initDynamicGrid(this,
Math.min(smallestSize.x, smallestSize.y),
Math.min(largestSize.x, largestSize.y),
realSize.x, realSize.y,
dm.widthPixels, dm.heightPixels);
当我们长按屏幕拖动屏幕上的图标的时候,每一个图片都有属于自己的一个方格的位置这段代码就是在Launcher上来生成那样的网格
//初始化LauncherModel:这个类和数据有关,保存了桌面运行时的状态信息。当桌面启动,读取应用图标和小工具,使用的就是它当中的内部类LoaderTask
LauncherModel mModel = app.setLauncher(this);
这段代码比较重要,首先我们要知道LauncherModel这个类的作用,他是用来保存桌面运行时的状态信息,它中的内部类LoaderTask完成了在桌面启动时读取数据库数据,并把图标和小工具添加到桌面。因为LauncherModel这个类大部分都是耗时的操作,所以要拿到它操作完之后的数据就要使用回调的方式,这里把this传递过去,就是为了回调使用。LauncherModel用来加载数据,Launcher用来显示数据。
//初始化IconCache,用于缓存应用程序的图标
IconCache mIconCache = app.getIconCache();
mIconCache.flushInvalidIcons(grid);//清空缓存
//创建拖拽控制对象
mDragController = new DragController(this);
mInflater = getLayoutInflater();
mStats = new Stats(this);
//初始化桌面插件管理对象,它是AppWidgetService的代理
mAppWidgetManager = AppWidgetManager.getInstance(this);
//桌面插件宿主。每一个桌面插件要显示到桌面上都必须有一个对应的桌面宿主
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
mAppWidgetHost.startListening();
为了有更好的体验,Launcher上加载的所有应用的图标都是先提前在Launcher中缓存起来的,使用的就是IconCache这个对象。然后创建拖拽对象,控制被用户拖拽的应用图标,同时创建桌面小部件的管理对象,开启桌面小部件的监听。
//检查本地配置信息是否发生变化,如果变化了要更新桌面对应的元素
checkForLocaleChange();
setContentView(R.layout.launcher);
//初始化桌面上所有的布局
setupViews();
//加载壁纸
setWallpaper();
grid.layout(this);
registerContentObservers();
//系统预留方法,空方法
lockAllApps();
//保存状态
mSavedState = savedInstanceState;
//恢复状态
restoreState(mSavedState);
检查本地配置信息,是检查如当时的时区,时间,经纬度等这些实时变化的东西,保持同步。之后就是加载布局文件,加载完毕之后就要初始化桌面上所有的布局,然后加载壁纸等操作,最后我们知道手机的桌面由于是应用频率最高的Activity,所以系统添加了状态保存和恢复机制,savedInstanceState这个变量就是onCreate()方法中的那个Bundle类型的参数
if (!mRestoring) {
if (sPausedFromUserAction) {
// If the user leaves launcher, then we should just load items asynchronously when
// they return.
mModel.startLoader(true, -1);
} else {
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
mModel.startLoader(true, mWorkspace.getCurrentPage());
}
}
以上的这几行代码就是Launcher加载桌面应用,包括加载应用菜单列表中的应用的逻辑,可以看到它是通过LauncherModel对象调用startLoader方法来开始加载数据的。
onCreate()方法在这之后还有几行代码,不过都不是太重要的在这里就不在贴出来说了,下一篇就来详细说说startLoader中的逻辑实现,同时系统的一些好的实现方法也值得我们在自己项目中来使用和学习。
欢迎关注我的微信公众号,我会把一些生活的感想和投资方面的总结写到公众号,希望你能来和我一起交流技术之外的东西。
<