Android系统启动(六)-Launcher篇
经过之前Android系统一系列启动流程,终于到了用户可视化操作界面了,即:Home桌面了,它是一个应用程序,叫Launcher。它主要展示一个个应用的快捷图标,并且通过点击图标来启动相应的应用程序。那么这篇文章就来解读下它。
一、Launcher的启动流程
接上篇AMS最后讲到的startHomeActivityLocked
public void systemReady(final Runnable goingCallback){
...
startHomeActivityLocked(currentUserId, "systemReady");
...
}
从这个方法开始开启Launcher的启动流程,那么就进去看看:
boolean startHomeActivityLocked(int userId, String reason) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// We are running in factory test mode, but unable to find
// the factory test app, so just sit around displaying the
// error message and don't try to start anything.
return false;
}
Intent intent = getHomeIntent();
ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, [aInfo.name](http://aInfo.name)));
// Don't do this if the home app is currently being
// instrumented.
aInfo = new ActivityInfo(aInfo);
aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid, true);
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
mActivityStarter.startHomeActivityLocked(intent, aInfo, reason);
}
} else {
Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
}
return true;
}
首先是调用getHomeIntent()方法,看一下getHomeIntent是如何实现构造Intent对象的:
Intent getHomeIntent() {
Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
intent.setComponent(mTopComponent);
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
intent.addCategory(Intent.CATEGORY_HOME);
}
return intent;
}
启动Launcher的Intent对象中添加了Intent.CATEGORY_HOME常量,这个其实是一个launcher的标志,一般系统的启动页面Activity都会在androidmanifest.xml中配置这个标志。
看下Launcher的AndroidManifest.xml:
//packages/apps/Launcher3/AndroidManifest.xml
<activity
android:name="com.android.launcher3.Launcher"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:theme="@style/Theme"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="nosensor"
android:configChanges="keyboard|keyboardHidden|navigation"
android:resumeWhilePausing="true"
android:taskAffinity=""
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY"/>
</intent-filter>
最后通过如下代码启动home的Activity:
mActivityStarter.startHomeActivityLocked(intent, aInfo, reason);
具体流程牵扯到Activity的启动流程,之后会有专门章节来总结,这里就简单列举下之后的执行路径:
ActivityStarter.startHomeActivityLocked
|
ActivityStarter.startActivityLocked 验证intent、Class、Permission等 ,保存将要启动的Activity的Record
|
ActivityStarter.doPendingActivityLaunchesLocked
|
ActivityStarter.startActivityUnchecked 检查将要启动的Activity的launchMode和启动Flag ,根据launcheMode和Flag配置task
|
ActivityStack.startActivityLocked
|
ActvityStack.startActivityLocked 任务栈历史栈配置
|
ActivityStack.resumeTopActivityInnerLocked() 查找要进入暂停的Activity
|
ActivityStack.startPausingLocked() 通过ipc告诉要暂停的Activity进入暂停
|
ActivityThread.handlePauseActivity() 1正式让之前的Activity暂停 2 告诉AMS已经暂停完成
|
ActivityManagerService.activityPaused()
|
ActivityStack.activityPausedLocked()
|
ActivityStackSuperVisor.resumeTopActivitiesLocked()
|
ActivityStack.resumeTopActivityLocked() 验证是否该启动的Activity所在进程和app是否存在,若存在,直接启动,否则,准备创建该进程
|
ActivityStackSuperVisor.startSpecificActivityLocked() 该进程不存在,创建进程
|
ActivityManagerService.startProcessLocked()
|
ActivityManagerService.startProcessLocked() 通过Process.start(“android.app.ActivityThread”)启动进程
|
ActivityThread.main()
|
ActivityThread.attach() 创建了 Instrumentation()
|
IActivityManager.attachApplication()
|
ActivityStackSuperVisor.attachApplicationLocked() 准备启动应用,先查找MainActivity
|
ActivityStackSuperVisor.realStartActivityLocked() IPC通知ActivityThread
|
ActivityThread.scheduleLaunchActivity() H 发消息 sendMessage(H.LAUNCH_ACTIVITY, r);
|
ActivityThread.handleLaunchActivity()
|
ActivityThread.performLaunchActivity()
|
Instrumentation.newActivity() 创建activity
|
activity.attach( )
|
Instrumentation.callActivityOnCreate 最终执行Activity onCreate
(太懒了,就不画时序图了 - - ||| )
二、launcher中应用图标显示流程
经过前面的启动分析,我们终于进入到Launcher的主Activity了
public class Launcher extends Activity{
private LauncherModel mModel;
onCreate(Bundle savedInstanceState){
...
LauncherAppState app = LauncherAppState.getInstance(); //LauncherAppState创建单例对象
…
mModel = app.setLauncher(this);
setContentView(R.layout.launcher); //设置布局
…
//load显示数据
if (!mRestoring) {
if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
// If the user leaves launcher, then we should just load items asynchronously when
// they return.
mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
} else {
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
mModel.startLoader(mWorkspace.getRestorePage());
}
}
...
}
}
看样子核心功能都在LauncherModel里:
先看看 mModel = app.setLauncher(this);
LauncherModel setLauncher(Launcher launcher) {
getLauncherProvider().setLauncherProviderChangeListener(launcher);
mModel.initialize(launcher);
mAccessibilityDelegate = ((launcher != null) && Utilities.ATLEAST_LOLLIPOP) ?
new LauncherAccessibilityDelegate(launcher) : null;
return mModel;
}
主要做了个初始化操作:
//LauncherModel
public void initialize(Callbacks callbacks) {
synchronized (mLock) {
unbindItemInfosAndClearQueuedBindRunnables();
mCallbacks = new WeakReference<Callbacks>(callbacks);
}
}
在initialize函数中会将Callbacks,也就是传入的Launcher 封装成一个弱引用对象。因此我们得知mCallbacks变量指的就是封装成弱引用对象的Launcher,这个mCallbacks后文会用到它。
回到onCreate 看看后面LauncherModel.startLoader方法:
...
@Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");//创建了具有消息循环的线程HandlerThread对象
static {
sWorkerThread.start();
}
@Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper());//创建了Handler,并且传入HandlerThread的Looper。
...
public void startLoader(int synchronousBindPage, int loadFlags) {s
InstallShortcutReceiver.enableInstallQueue();
synchronized (mLock) {
synchronized (mDeferredBindRunnables) {
mDeferredBindRunnables.clear();
}
if (mCallbacks != null && mCallbacks.get() != null) {
stopLoaderLocked();
mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags);//创建LoaderTask
if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
&& mAllAppsLoaded && mWorkspaceLoaded && !mIsLoaderTaskRunning) {
mLoaderTask.runBindSynchronousPage(synchronousBindPage);
} else {
sWorkerThread.setPriority(Thread.NORM_PRIORITY);
sWorker.post(mLoaderTask);//将LoaderTask作为消息发送给HandlerThread
}
}
}
}
LoaderTask类实现了Runnable接口,当LoaderTask所描述的消息被处理时则会调用它的run函数,代码如下所示:
//LauncherModel
private class LoaderTask implements Runnable {
...
final List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
...
mHandler.post(new Runnable() {
public void run() {
synchronized (mLock) {
if (mStopped) {
return;
}
mIsLoaderTaskRunning = true;
}
keep_running: {
if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
loadAndBindWorkspace();//调用loadAndBindWorkspace函数用来加载工作区信息
if (mStopped) {
break keep_running;
}
waitForIdle();
if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
loadAndBindAllApps();//用来加载系统已经安装的应用程序信息
}
mContext = null;
synchronized (mLock) {
if (mLoaderTask == this) {
mLoaderTask = null;
}
mIsLoaderTaskRunning = false;
mHasLoaderCompletedOnce = true;
}
}
}
...
}
mLauncherApps.getActivityList执行的是
//LauncherApps
mService.getLauncherActivities(packageName, user);
而mService 是:
ILauncherApps.Stub.asInterface(
ServiceManager.getService(Context.LAUNCHER_APPS_SERVICE)));
也就是最终由LauncherAppService执行getLauncherActivities:
@Override
public ParceledListSlice<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)
throws RemoteException {
ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);
if (!isUserEnabled(user)) {
return null;
}
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
mainIntent.setPackage(packageName);
long ident = Binder.clearCallingIdentity();
try {
List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
user.getIdentifier());
return new ParceledListSlice<>(apps);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
最终由PMS 执行queryIntentActivitiesAsUser 去获取要展示的APP信息。(这部分之后总结PMS再细说)
Launcher是用工作区的形式来显示系统安装的应用程序的快捷图标,每一个工作区都是来描述一个抽象桌面的,它由n个屏幕组成,每个屏幕又分n个单元格,每个单元格用来显示一个应用程序的快捷图标。这里loadAndBindWorkspace不分析,直接看加载数据相关的loadAndBindAllApps, loadAndBindAllApps又会调用loadAllApps:
//LauncherModel
private void loadAllApps() {
...
mHandler.post(new Runnable() {
public void run() {
final long bindTime = SystemClock.uptimeMillis();
final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindAllApplications(added);
if (DEBUG_LOADERS) {
Log.d(TAG, "bound " + added.size() + " apps in "
+ (SystemClock.uptimeMillis() - bindTime) + "ms");
}
} else {
Log.i(TAG, "not binding apps: no Launcher activity");
}
}
});
...
}
调用callbacks的bindAllApplications函数,在前面我们得知这个callbacks实际是指向Launcher的,因此我们来查看Launcher的bindAllApplications函数:
//Launcher
public void bindAllApplications(final ArrayList<AppInfo> apps) {
if (waitUntilResume(mBindAllApplicationsRunnable, true)) {
mTmpAppsList = apps;
return;
}
if (mAppsView != null) {
mAppsView.setApps(apps);//
}
if (mLauncherCallbacks != null) {
mLauncherCallbacks.bindAllApplications(apps);
}
}
那mAppsView是什么?
mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
apps_view对应的是一个自定义控件:
com.android.launcher3.allapps.AllAppsContainerView
//AllAppsContainerView
public void setApps(List<AppInfo> apps) {
mApps.setApps(apps);
}
再看看mApps初始化的地方:
public AllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
Resources res = context.getResources();
mLauncher = (Launcher) context;
mSectionNamesMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
mApps = new AlphabeticalAppsList(context);
mAdapter = new AllAppsGridAdapter(mLauncher, mApps, this, mLauncher, this);
mApps.setAdapter(mAdapter);
mLayoutManager = mAdapter.getLayoutManager();
mItemDecoration = mAdapter.getItemDecoration();
mRecyclerViewTopBottomPadding =
res.getDimensionPixelSize(R.dimen.all_apps_list_top_bottom_padding);
mSearchQueryBuilder = new SpannableStringBuilder();
Selection.setSelection(mSearchQueryBuilder, 0);
}
最后再看看AllAppsContainerView的onFinishInflate方法:
//AllAppsContainerView
@Override
protected void onFinishInflate() {
super.onFinishInflate();
...
// Load the all apps recycler view
mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
mAppsRecyclerView.setApps(mApps);//2
mAppsRecyclerView.setLayoutManager(mLayoutManager);
mAppsRecyclerView.setAdapter(mAdapter);//3
mAppsRecyclerView.setHasFixedSize(true);
mAppsRecyclerView.addOnScrollListener(mElevationController);
mAppsRecyclerView.setElevationController(mElevationController);
...
}
onFinishInflate函数在加载完xml文件时就会调用。AllAppsRecyclerView设置数据,并setAdapter。
一张图总结Launcher整个启动过程以及数据显示过程:
from fu_kevin参考:
https://blog.csdn.net/fu_kevin0606/article/details/54931704
http://liuwangshu.cn/framework/booting/4-launcher.html