Hi!请登陆

Launcher启动流程及初始化

2020-11-15 31 11/15

前言

前面我们学习了SystemServer的启动流程,也了解了AMS是如何启动起来并通过Binder注册到ServiceManger内的,OK,本文基于这俩篇基础继续来学习Launcher。

Launcher是如何启动起来的Launcher启动起来之后自身的流程是怎样初始化的

PS:本文的流程分析基于android_2.3.7,高版本的源码和本篇文章流程分析略有出入,请注意自己当前源码的版本。

流程代码分析

1.Launcher是如何启动起来的

流程图

Launcher启动流程及初始化

frameworks\base\services\java\com\android\server\SystemServer.java$ServerThread#run()

class ServerThread extends Thread {
private static final String TAG = "SystemServer";
......
@Override
public void run() {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN,
SystemClock.uptimeMillis());
......
((ActivityManagerService)ActivityManagerNative.getDefault())
.systemReady(new Runnable() {
public void run() {
Slog.i(TAG, "Making services ready");
if (statusBarF != null) statusBarF.systemReady2();
if (batteryF != null) batteryF.systemReady();
if (connectivityF != null) connectivityF.systemReady();
if (dockF != null) dockF.systemReady();
if (usbF != null) usbF.systemReady();
if (uiModeF != null) uiModeF.systemReady();
if (recognitionF != null) recognitionF.systemReady();
Watchdog.getInstance().start();
// It is now okay to let the various system services start their
// third party code...
if (appWidgetF != null) appWidgetF.systemReady(safeMode);
if (wallpaperF != null) wallpaperF.systemReady();
if (immF != null) immF.systemReady();
if (locationF != null) locationF.systemReady();
if (throttleF != null) throttleF.systemReady();
}
});
......
}
}

这个getDefault实际上返回的就是Ams。强转之后去调用systemReady()。

frameworks\base\services\java\com\android\server\am\ActivityManagerService.java#systemReady()

public void systemReady(final Runnable goingCallback) {
// In the simulator, startRunning will never have been called, which
// normally sets a few crucial variables. Do it here instead.
......
synchronized(this) {
......
mMainStack.resumeTopActivityLocked(null);
}
}

systemReady内调用了mMainStack的resumeTopActivityLocked方法,点进去。
延伸思考:ActivityStack mMainStack是什么?

frameworks\base\services\java\com\android\server\am\ActivityStack.java#resumeTopActivityLocked()

/**
* Ensure that the top activity in the stack is resumed.
*
* @param prev The previously resumed activity, for when in the process
* of pausing; can be null to call from elsewhere.
*
* @return Returns true if something is being resumed, or false if
* nothing happened.
*/
final boolean resumeTopActivityLocked(ActivityRecord prev) {
// Find the first activity that is not finishing.
ActivityRecord next = topRunningActivityLocked(null);
// Remember how we'll process this pause/resume situation, and ensure
// that the state is reset however we wind up proceeding.
final boolean userLeaving = mUserLeaving;
mUserLeaving = false;
if (next == null) {
// There are no more activities! Let's just start up the
// Launcher...
if (mMainStack) {
return mService.startHomeActivityLocked();
}
}
next.delayedResume = false;
......
return true;
}

在ActivityStack.java的resumeTopActivityLocked()方法内又去调用了mService.startHomeActivityLocked(),这个mService就是ActivityManagerService。
那么再回到ActivityManagerService去看startHomeActivityLocked()。

frameworks\base\core\java\android\app\ActivityManagerService.java#startHomeActivityLocked()

boolean startHomeActivityLocked() {
if (mFactoryTest == SystemServer.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 = new Intent(
mTopAction,
mTopData != null ? Uri.parse(mTopData) : null);
intent.setComponent(mTopComponent);
if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
intent.addCategory(Intent.CATEGORY_HOME);
}
//向PKMS查询满足条件的ActivityInfo
ActivityInfo aInfo =
intent.resolveActivityInfo(mContext.getPackageManager(),
STOCK_PM_FLAGS);
if (aInfo != null) {
intent.setComponent(new ComponentName(
aInfo.applicationInfo.packageName, aInfo.name));
// Don't do this if the home app is currently being
// instrumented.
//在正常情况下,app应该为null,因为刚开机,Home进程肯定还没启动
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid);
if (app == null || app.instrumentationClass == null) {
//启动Home
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
mMainStack.startActivityLocked(null, intent, null, null, 0, aInfo,
null, null, 0, 0, 0, false, false);
}
}
return true;
}

这儿就是启动Launcher的事发现场了。代码内注释了//启动Home处。
0.先是为启动Intent设置了启动flag
1.然后设置了一个intent.addCategory(Intent.CATEGORY_HOME);
2.再调用mMainStack.startActivityLocked方法,这里Launcher就启动起来了。

启动之后,我们再去看Launcher的UI,可以想象到的是,至少有一个列表来展示各个App的图标,然后我们再重点深入去看它的点击事件,用于后续分析App的启动流程。

PS:
另外我试图追了下,mMainStack.startActivityLocked()这个方法,感觉涉及的东西挺多,一时半会也无法理解后续流程,这里我先放一放,等我系统的了解了ActivityRecord,TaskRecord,ActivityStack,WMS这部分知识的时候,再回过头来往下深究。

2.启动起来之后自身的流程是怎样初始化点击事件的

流程图

Launcher启动流程及初始化

packages\apps\Launcher2\src\com\android\launcher2\Launcher.java#onCreate

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//获取launcher的application
LauncherApplication app = ((LauncherApplication)getApplication());
//LauncherMode
mModel = app.setLauncher(this);
//iconCatche
mIconCache = app.getIconCache();
mDragController = new DragController(this);
//inflater
mInflater = getLayoutInflater();
//AppWidgetManager android桌面控件相关
mAppWidgetManager = AppWidgetManager.getInstance(this);
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
mAppWidgetHost.startListening();
if (PROFILE_STARTUP) {
android.os.Debug.startMethodTracing("/sdcard/launcher");
}
loadHotseats();
checkForLocaleChange();
setWallpaperDimension();
setContentView(R.layout.launcher);
//UI初始化
setupViews();
//注册观察者
registerContentObservers();
lockAllApps();
mSavedState = savedInstanceState;
restoreState(mSavedState);
if (PROFILE_STARTUP) {
android.os.Debug.stopMethodTracing();
}
//重要 这里开启桌面及cellLayout的初始化
if (!mRestoring) {
mModel.startLoader(this, true);
}
// For handling default keys
mDefaultKeySsb = new SpannableStringBuilder();
Selection.setSelection(mDefaultKeySsb, 0);
IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
registerReceiver(mCloseSystemDialogsReceiver, filter);
}

1.setupViews(),初始化UI

2.注意一下这mModel.startLoader(this, true);。它是后续对屏幕WorkSpace初始化的一个关键节点。它比较重要.

packages\apps\Launcher2\src\com\android\launcher2\Launcher.java#setupViews

/**
* Finds all the views we need and configure them properly.
*/
private void setupViews() {
DragController dragController = mDragController;
DragLayer dragLayer = (DragLayer) findViewById(R.id.drag_layer);
dragLayer.setDragController(dragController);
mAllAppsGrid = (AllAppsView)dragLayer.findViewById(R.id.all_apps_view);
mAllAppsGrid.setLauncher(this);
mAllAppsGrid.setDragController(dragController);
((View) mAllAppsGrid).setWillNotDraw(false); // We don't want a hole punched in our window.
// Manage focusability manually since this thing is always visible
((View) mAllAppsGrid).setFocusable(false);
//Workspace 是一个ViewGroup
mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace);
final Workspace workspace = mWorkspace;
workspace.setHapticFeedbackEnabled(false);
//删除区域 就是我们拖个图标往哪一放就删掉了
DeleteZone deleteZone = (DeleteZone) dragLayer.findViewById(R.id.delete_zone);
mDeleteZone = deleteZone;
mHandleView = (HandleView) findViewById(R.id.all_apps_button);
mHandleView.setLauncher(this);
mHandleView.setOnClickListener(this);
mHandleView.setOnLongClickListener(this);
ImageView hotseatLeft = (ImageView) findViewById(R.id.hotseat_left);
hotseatLeft.setContentDescription(mHotseatLabels[0]);
hotseatLeft.setImageDrawable(mHotseatIcons[0]);
ImageView hotseatRight = (ImageView) findViewById(R.id.hotseat_right);
hotseatRight.setContentDescription(mHotseatLabels[1]);
hotseatRight.setImageDrawable(mHotseatIcons[1]);
mPreviousView = (ImageView) dragLayer.findViewById(R.id.previous_screen);
mNextView = (ImageView) dragLayer.findViewById(R.id.next_screen);
Drawable previous = mPreviousView.getDrawable();
Drawable next = mNextView.getDrawable();
mWorkspace.setIndicators(previous, next);
mPreviousView.setHapticFeedbackEnabled(false);
mPreviousView.setOnLongClickListener(this);
mNextView.setHapticFeedbackEnabled(false);
mNextView.setOnLongClickListener(this);
//点击事件处理 从这里分析点击时app是如何启动起来的
workspace.setOnLongClickListener(this);
workspace.setDragController(dragController);
workspace.setLauncher(this);
//删除区域设置
deleteZone.setLauncher(this);
deleteZone.setDragController(dragController);
deleteZone.setHandle(findViewById(R.id.all_apps_button_cluster));
dragController.setDragScoller(workspace);
dragController.setDragListener(deleteZone);
dragController.setScrollView(dragLayer);
dragController.setMoveTarget(workspace);
// The order here is bottom to top.
dragController.addDropTarget(workspace);
dragController.addDropTarget(deleteZone);
}

这里主要就是初始化了桌面widget区域,删除区域,以及最重要的WorkSpace区域。

packages\apps\Launcher2\src\com\android\launcher2\Workspace.java

/**
* The workspace is a wide area with a wallpaper and a finite number of screens. Each
* screen contains a number of icons, folders or widgets the user can interact with.
* A workspace is meant to be used with a fixed width only.
工作区是一个宽的区域,有壁纸和有限的屏幕。
每个屏幕都包含一些用户可以与之交互的图标、文件夹或小部件。工作空间只用于固定的宽度。
*/
public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller {
......
}

packages\apps\Launcher2\src\com\android\launcher2\Workspace.java

/**
* The workspace is a wide area with a wallpaper and a finite number of screens. Each
* screen contains a number of icons, folders or widgets the user can interact with.
* A workspace is meant to be used with a fixed width only.
工作区是一个宽的区域,有壁纸和有限的屏幕。
每个屏幕都包含一些用户可以与之交互的图标、文件夹或小部件。工作空间只用于固定的宽度。
*/
public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller {
......
/**
* Used to inflate the Workspace from XML.
*
* @param context The application's context.
* @param attrs The attribtues set containing the Workspace's customization values.
* @param defStyle Unused.
*/
public Workspace(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mWallpaperManager = WallpaperManager.getInstance(context);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Workspace, defStyle, 0);
mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1);
a.recycle();
setHapticFeedbackEnabled(false);
initWorkspace();
}
}

packages\apps\Launcher2\src\com\android\launcher2\Workspace.java#initWorkspace()

/**
* The workspace is a wide area with a wallpaper and a finite number of screens. Each
* screen contains a number of icons, folders or widgets the user can interact with.
* A workspace is meant to be used with a fixed width only.
工作区是一个宽的区域,有壁纸和有限的屏幕。
每个屏幕都包含一些用户可以与之交互的图标、文件夹或小部件。工作空间只用于固定的宽度。
*/
public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller {
......
/**
* Initializes various states for this workspace.
*/
private void initWorkspace() {
Context context = getContext();
mScrollInterpolator = new WorkspaceOvershootInterpolator();
mScroller = new Scroller(context, mScrollInterpolator);
mCurrentScreen = mDefaultScreen;
Launcher.setScreen(mCurrentScreen);
LauncherApplication app = (LauncherApplication)context.getApplicationContext();
mIconCache = app.getIconCache();
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
}
}

1.Scroller初始化,用于滑动。
2.mIconCache,图标信息初始化。
3.到这里执行完了,我们并没有发现有初始化icon图标的操作。我们将函数一步步返回,回到Launcher中mModel.startLoader(this, true);去。

packages\apps\Launcher2\src\com\android\launcher2\LauncherModel.java#startLoader()

public void startLoader(Context context, boolean isLaunching) {
synchronized (mLock) {
if (DEBUG_LOADERS) {
Log.d(TAG, "startLoader isLaunching=" + isLaunching);
}
// Don't bother to start the thread if we know it's not going to do anything
if (mCallbacks != null && mCallbacks.get() != null) {
// If there is already one running, tell it to stop.
LoaderTask oldTask = mLoaderTask;
if (oldTask != null) {
if (oldTask.isLaunching()) {
// don't downgrade isLaunching if we're already running
isLaunching = true;
}
oldTask.stopLocked();
}
//LoaderTask初始化
mLoaderTask = new LoaderTask(context, isLaunching);
sWorker.post(mLoaderTask);
}
}
}

1.初始化LoaderTask
2.执行LoaderTask

packages\apps\Launcher2\src\com\android\launcher2\LauncherModel.java$LoaderTask#run()

/**
* Runnable for the thread that loads the contents of the launcher:
* - workspace icons
* - widgets
* - all apps icons
*/
private class LoaderTask implements Runnable {
......
public void run() {
......
// second step
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();//处理桌面上的图标
}
......
}
}

这个任务里包含了所有的图标。

packages\apps\Launcher2\src\com\android\launcher2\LauncherModel.java$LoaderTask#loadAndBindWorkspace()

private void loadAndBindWorkspace() {
// Load the workspace
......
// Bind the workspace
bindWorkspace();
}

packages\apps\Launcher2\src\com\android\launcher2\LauncherModel.java$LoaderTask#bindWorkspace()

/**
* Read everything out of our database.
*/
private void bindWorkspace() {
......
int N;
// Tell the workspace that we're about to start firing items at it
mHandler.post(new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.startBinding();//1
}
}
});
// Add the items to the workspace.
N = mItems.size();
for (int i=0; i

相关推荐