从综述将启动流程拆分成7步,其中第一步,重点是通过手机的硬件来获取信息。
关于第一步在oncreate中的源码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
if (mLauncherCallbacks != null) {
mLauncherCallbacks.preOnCreate();
}
super.onCreate(savedInstanceState);
LauncherAppState app = LauncherAppState.getInstance(this);
从源码看,重点就做了一件事情,创建LauncherAppState 对象。
这是因为在Launcher的启动流程中,手机硬件参数获取不是在Launcher.java这个类中完成。
LauncherAppState.getInstance(this) 这段代码是初始化一个LauncherAppState的对象app,其初始化方法是通过一个静态方法getInstance来创建,创建的时候需要传入Launcher对象作为参数,那么说明在LauncherAppState中会用到Launcher的对象,下面详细看getInstance方法。
源码如下:
public static LauncherAppState getInstance(final Context context) {
if (INSTANCE == null) {
if (Looper.myLooper() == Looper.getMainLooper()) {
INSTANCE = new LauncherAppState(context.getApplicationContext());
} else {
try {
return new MainThreadExecutor().submit(new Callable
@Override
public LauncherAppState call() throws Exception {
return LauncherAppState.getInstance(context);
}
}).get();
} catch (InterruptedException|ExecutionException e) {
throw new RuntimeException(e);
}
}
}
return INSTANCE;
}
这是一个设计模式中的单例模式。其实一开始从方法名getInstance就可以看出这是单例模式。单例模式的核心是创建且只创建一次对象,于是整个代码的核心是创建对象
“new LauncherAppState(context.getApplicationContext())”
单例模式是只创建一次对象的,如果是第一次调用,则会创建这个对象,并赋值给INSTANCE ,而如果不是第一次调用,这个时候INSTANCE 已经被赋值对象了,就将这个对象交给调用者。
这里有个细节,本方法使用了context.getApplicationContext(),这说明之前后面需要使用的不是activity的对象,而只需要获取其context。在Android使用过程中,如果我们需要获取的是context而不是activity本身,都建议使用activity的getApplicationContext()方法,防止内存泄露,增加代码健壮性。
我们所关注LauncherAppState的源码如下:
private LauncherAppState(Context context) {
mContext = context;
mInvariantDeviceProfile = new InvariantDeviceProfile(mContext);
mIconCache = new IconCache(mContext, mInvariantDeviceProfile);
mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
mModel = new LauncherModel(this, mIconCache,
Utilities.getOverrideObject(AppFilter.class, mContext, R.string.app_filter_class));
LauncherAppsCompat.getInstance(mContext).addOnAppsChangedCallback(mModel);
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
if (Utilities.ATLEAST_NOUGAT) {
filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);
}
mContext.registerReceiver(mModel, filter);
UserManagerCompat.getInstance(mContext).enableAndResetCache();
new ConfigMonitor(mContext).register();
}
在这构造器中,最关键的是InvariantDeviceProfile类,因为这个方便是获取硬件参数的类。也是第一步当中最重要的行为。
而后还创建了mIconCache 、mWidgetCache 、mModel 三个对象,创建这些对象是因为在本类中会用到这写对象所以创建。这三个类对于Launcher启动流程的第一步不是强相关的。
比如LauncherModer的创建,主要是为了注册广播,这个属于Launcher的后台部分。之前提到过Launcher三部分:启动,触摸,后台。本文只讲启动部分,启动部分包含了所有的UI。
以上就是第一步的整体流程,除开在java中的代码,还有一个res中的xml文件作为配合,也是第一步,通过手机硬件获取对应参数的关键。
是名为device_profiles的xml文件,此文件是由很多profile文件组成。
其中一个profile如下。
launcher:minWidthDps="296"
launcher:minHeightDps="491.33"
launcher:numRows="4"
launcher:numColumns="4"
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="48"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="5"
launcher:hotseatIconSize="48"
launcher:defaultLayoutId="@xml/default_workspace_4x4"
/>
device_profiles.xml中一共有11个profile,也只包含11个profile没有其他内容。
示例中的profile就是google官方给出的一个模型。
这个模型有物理的最小长度,最小宽度,布局的行列数,文件夹的行列数,allapp的行列数,图标大小,图标名字代销,快捷栏图标大小,快捷栏图标数,以及默认布局文件。
在这个参数中,其中涉及手机尺寸的是物理的最小长度,最小宽度即minWidthDps和minHeightDps 。
在11个模型中
长宽从launcher:minWidthDps="255" launcher:minHeightDps="300"
到launcher:minWidthDps="1527"launcher:minHeightDps="2527"
这从小屏按键机,到20寸的大型显示器都包含了。
整个Launcher能够实现的Launcher布局模式只能够按照这11中模型来做,实际的手机和模型做比较,选取最接近的模型来作为本手机的实际模型。
整个代码在mInvariantDeviceProfile 的构造器中完成,将在后面继续讲解