前言

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

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

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

流程代码分析

1.Launcher是如何启动起来的

流程图

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

class ServerThread extends Thread {private static final String TAG = "SystemServer";......@Overridepublic 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.javaresumeTopActivityLocked()方法内又去调用了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查询满足条件的ActivityInfoActivityInfo 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) {//启动Homeintent.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.启动起来之后自身的流程是怎样初始化点击事件的

流程图

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

@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//获取launcher的applicationLauncherApplication app = ((LauncherApplication)getApplication());//LauncherModemModel = app.setLauncher(this);//iconCatchemIconCache = app.getIconCache();mDragController = new DragController(this);//inflatermInflater = 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 keysmDefaultKeySsb = 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 anythingif (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 runningisLaunching = 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 stepif (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 workspacebindWorkspace();}

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 itmHandler.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<N; i+=ITEMS_CHUNK) {final int start = i;final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);mHandler.post(new Runnable() {public void run() {Callbacks callbacks = tryGetCallbacks(oldCallbacks);if (callbacks != null) {callbacks.bindItems(mItems, start, start+chunkSize);//2}}});}mHandler.post(new Runnable() {public void run() {Callbacks callbacks = tryGetCallbacks(oldCallbacks);if (callbacks != null) {callbacks.bindFolders(mFolders);//3}}});// Wait until the queue goes empty.mHandler.post(new Runnable() {public void run() {if (DEBUG_LOADERS) {Log.d(TAG, "Going to start binding widgets soon.");}}});// Bind the widgets, one at a time.// WARNING: this is calling into the workspace from the background thread,// but since getCurrentScreen() just returns the int, we should be okay.  This// is just a hint for the order, and if it's wrong, we'll be okay.// TODO: instead, we should have that push the current screen into here.final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen();N = mAppWidgets.size();// once for the current screenfor (int i=0; i<N; i++) {final LauncherAppWidgetInfo widget = mAppWidgets.get(i);if (widget.screen == currentScreen) {mHandler.post(new Runnable() {public void run() {Callbacks callbacks = tryGetCallbacks(oldCallbacks);if (callbacks != null) {callbacks.bindAppWidget(widget);//4}}});}}// once for the other screensfor (int i=0; i<N; i++) {final LauncherAppWidgetInfo widget = mAppWidgets.get(i);if (widget.screen != currentScreen) {mHandler.post(new Runnable() {public void run() {Callbacks callbacks = tryGetCallbacks(oldCallbacks);if (callbacks != null) {callbacks.bindAppWidget(widget);//5}}});}}// Tell the workspace that we're done.mHandler.post(new Runnable() {public void run() {Callbacks callbacks = tryGetCallbacks(oldCallbacks);if (callbacks != null) {callbacks.finishBindingItems();//6}}});// If we're profiling, this is the last thing in the queue.mHandler.post(new Runnable() {public void run() {if (DEBUG_LOADERS) {Log.d(TAG, "bound workspace in "+ (SystemClock.uptimeMillis()-t) + "ms");}}});}

这里代码多,有许多bind方法,不必全部看,关注本文分析的主线,注释1处是准备开始bind做的一些准备,我们从注释2处切入进去。
看到它是一个抽象接口进行的一个bind操作。那么这个接口的实现是啥?它的实现类就是我们的Launcher这个类。

packages\apps\Launcher2\src\com\android\launcher2\Launcher.java#bindItems()【重点】

/*** Bind the items start-end from the list.** Implementation of the method from LauncherModel.Callbacks.*/public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end) {setLoadOnResume();final Workspace workspace = mWorkspace;for (int i=start; i<end; i++) {final ItemInfo item = shortcuts.get(i);mDesktopItems.add(item);switch (item.itemType) {case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:final View shortcut = createShortcut((ShortcutInfo)item);workspace.addInScreen(shortcut, item.screen, item.cellX, item.cellY, 1, 1,false);  //1...break;case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:final FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,(ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),(UserFolderInfo) item);workspace.addInScreen(newFolder, item.screen, item.cellX, item.cellY, 1, 1,false); //2...break;case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:final FolderIcon newLiveFolder = LiveFolderIcon.fromXml(R.layout.live_folder_icon, this,(ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),(LiveFolderInfo) item);workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1,false); //3...break;}}workspace.requestLayout();}

1.这个方法就到了本文的主线目的地,首先它通过createShortcut方法来对每个item点击时启动的app进行初始化。
2.这里可以看到3处都调用了workspace.addInScreen。这里边会初始化CellLayout。一会我们再去WorkSpace这个类里边分析addInScreen这个方法

packages\apps\Launcher2\src\com\android\launcher2\Launcher.java#createShortcut(ShortcutInfo info)

  /*** Creates a view representing a shortcut.** @param info The data structure describing the shortcut.** @return A View inflated from R.layout.application.*/View createShortcut(ShortcutInfo info) {return createShortcut(R.layout.application,(ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);}

packages\apps\Launcher2\src\com\android\launcher2\Launcher.java#createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info)

/*** Creates a view representing a shortcut inflated from the specified resource.** @param layoutResId The id of the XML layout used to create the shortcut.* @param parent The group the shortcut belongs to.* @param info The data structure describing the shortcut.** @return A View inflated from layoutResId.*/View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {TextView favorite = (TextView) mInflater.inflate(layoutResId, parent, false);favorite.setCompoundDrawablesWithIntrinsicBounds(null,new FastBitmapDrawable(info.getIcon(mIconCache)),null, null);favorite.setText(info.title);favorite.setTag(info);favorite.setOnClickListener(this);//这里点击时 启动appreturn favorite;}

这里首先inflater出item的布局,然后设置text和OnClickListener,还有tag,这个tag是ApplicationInfo,里面包含了各种App信息,是从App的AndroidManifest.xml的标签中解析出来的。(TODO具体是如何解析的流程,本文暂时不去深究,后续分析到了这部分再补上)既然设置了点击事件,显然,点击后应该会打开对应的App才对。所以继续看onClick方法

packages\apps\Launcher2\src\com\android\launcher2\Launcher.java#onClick()

    /*** Launches the intent referred by the clicked shortcut.** @param v The view representing the clicked shortcut.*/public void onClick(View v) {Object tag = v.getTag();if (tag instanceof ShortcutInfo) {// Open shortcut  主要看这里 final Intent intent = ((ShortcutInfo) tag).intent;int[] pos = new int[2];v.getLocationOnScreen(pos);intent.setSourceBounds(new Rect(pos[0], pos[1],pos[0] + v.getWidth(), pos[1] + v.getHeight()));//启动appstartActivitySafely(intent, tag);} else if (tag instanceof FolderInfo) {handleFolderClick((FolderInfo) tag);} else if (v == mHandleView) {if (isAllAppsVisible()) {closeAllApps(true);} else {showAllApps(true);}}}

这里的intent.setSourceBounds很奇怪,这里我们先不管他,重点需要注意是Object tag = v.getTag();得到之前我们从createShortcut中设置的appInfo信息,所以当我们点击icon的时候才能准确的跳转到指定app。

这里也推翻了我最开始的一个猜想,我一开始想得很简单,laucher就一个列表,然后一个itemClickListener设置完点击启动app完事,但是经过分析发现,laucher点击启动app和绘制完全是分开的。

packages\apps\Launcher2\src\com\android\launcher2\Launcher.java#startActivitySafely()

void startActivitySafely(Intent intent, Object tag) {intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);try {//去打开activitystartActivity(intent);} catch (ActivityNotFoundException e) {Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);} catch (SecurityException e) {Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();Log.e(TAG, "Launcher does not have the permission to launch " + intent +". Make sure to create a MAIN intent-filter for the corresponding activity " +"or use the exported attribute for this activity. "+ "tag="+ tag + " intent=" + intent, e);}}

OK,到此本文的任务就完成了,但是我们接下来的还需要把剩下的另外一个方法分析完。这个方法就是WorkSpace.java#addInScreen,通过它我们才能了解到我们之前所猜想的”列表”这个东西。

packages\apps\Launcher2\src\com\android\launcher2\WorkSpace.java#addInScreen()

/*** Adds the specified child in the specified screen. The position and dimension of* the child are defined by x, y, spanX and spanY.** @param child The child to add in one of the workspace's screens.* @param screen The screen in which to add the child.* @param x The X position of the child in the screen's grid.* @param y The Y position of the child in the screen's grid.* @param spanX The number of cells spanned horizontally by the child.* @param spanY The number of cells spanned vertically by the child.* @param insert When true, the child is inserted at the beginning of the children list.*/void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) {......final CellLayout group = (CellLayout) getChildAt(screen);CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();if (lp == null) {lp = new CellLayout.LayoutParams(x, y, spanX, spanY);// 1.cellLayout} else {lp.cellX = x;lp.cellY = y;lp.cellHSpan = spanX;lp.cellVSpan = spanY;}group.addView(child, insert ? 0 : -1, lp);if (!(child instanceof Folder)) {child.setHapticFeedbackEnabled(false);child.setOnLongClickListener(mLongClickListener);}if (child instanceof DropTarget) {mDragController.addDropTarget((DropTarget)child);}}

CellLayout及时我们前文猜想的列表,只是它并不是真正的列表,只是一个轻量级的GridView。这里简单说下,它有个内部类CellInfo存储了一堆的屏幕坐标,它是通过坐标来实现一个gridview的效果。其他它内部具体的实现本文暂时不进行深究。

下一篇分析当点击了launcher桌面图标之后执行startActivity(intent);的具体启动流程细节。也就是app启动流程

Thanks

《深入理解Android卷2》6.2.4章节

Launcher启动流程及初始化相关推荐

  1. Android系统开机到Launcher启动流程分析

    本文基于Android10.0的源码. 由于google团队在对framework层代码进行大量重构,所以代码变动还是挺大的. 常见基础问题: SystemServer系统服务进程是如何创建的?Lau ...

  2. Launcher启动流程加载流程学习

     声明: 图片本来是有的 涉及到有些代码不能示人没有贴上,不过仅文字说也足够了,请广大老爷们自行下载源码参看流程分析阅读. 目录 一.认识Launcher: 1 1.功能 1 2.样式 2 3.And ...

  3. 分析Spring容器启动流程 Spring初始化

    分析Spring容器启动流程 Spring初始化 每当启动Web容器时(例如Tomcat),会读取Web应用中的web.xml文件.以下这段代码就是启动Spring容器的关键代码. ContextLo ...

  4. Android 8.0学习(27)--- SystemUI(二)启动流程和初始化

    Android 8.0 SystemUI(二):启动流程和初始化 这篇的话,将对SystemUI的启动和大体的初始化作描述.篇幅应该比上篇多了些.哈哈. 老样子,先上目录,简洁明了. 概述 由于需要实 ...

  5. JPOM - Server启动流程脚本初始化源码解析

    文章目录 地址 版本 Server启动流程&脚本初始化流程 地址 Gitee: https://gitee.com/dromara/Jpom 官网: https://jpom.io/ 一款简而 ...

  6. Android4.0源码Launcher启动流程分析【android源码Launcher系列一】

    最近研究ICS4.0的Launcher,发现4.0和2.3有稍微点区别,但是区别不是特别大,所以我就先整理一下Launcher启动的大致流程. Launcher其实是贯彻于手机的整个系统的,时时刻刻都 ...

  7. Android中ICS4.0源码Launcher启动流程分析【android源码Launcher系列一】

    最近研究ICS4.0的Launcher,发现4.0和2.3有稍微点区别,但是区别不是特别大,所以我就先整理一下Launcher启动的大致流程.Launcher其实是贯彻于手机的整个系统的,时时刻刻都在 ...

  8. Launcher启动流程

    Launcher即桌面,是Android智能设备的窗口,用户使用最频繁的软件之一.Launhcer是Android所有应用的入口,也提供窗口小部件等功能 当然,Launcher本身就是一个APP,一个 ...

  9. Android Launcher启动流程

    Launcher App 由SystemServer启动,而SystemServer由Zygote进程孵化出来的. Zygote是孵化器,所有其他Dalvik/ART虚拟机进程都是用过zygote孵化 ...

最新文章

  1. 计数排序、桶排序和基数排序的运算性能对比及总结区别(附python代码)
  2. 不再有神的概念,将所有存在称之为文明,这很有意思
  3. 线程的start()方法
  4. ASP.NET 2.0个性化配置(profile)
  5. hsrp 切换_HSRP、VRRP、GLBP | 网络工程师之网关高可用、冗余
  6. jmu-枚举WeekDay
  7. 共享会话怎么设置没访问自动断开_谁总结的JavaWeb会话技术了?太全面了...
  8. mantis config_inc.php g_source,CentOS7下Mantis安装与配置
  9. redis数据类型之String入门
  10. 夏天到啦!你的mac过热怎么办?教你几招过热技巧~
  11. 谷歌代码规范的中文版
  12. 一文JDK动态代理的那点事儿
  13. 奥地利邮政服务推出加密收藏邮票
  14. 图片格式转换怎么做?教你几招搞定图片格式转换
  15. WEB数据挖掘相关术语整理
  16. 图书馆管理系统(详细)(C语言版本)
  17. 【转载】Attention Mechanism in Deep Learning
  18. 数据运营-计算留存率和转化率(漏斗分析Python)
  19. 天馈线测试仪 是什么 具备什么样的功能
  20. 华硕vm510l拆电池图解_华硕vm510l的拆机教程详解 参数报价

热门文章

  1. Linux_Linux_sort 命令
  2. 网络舆情分析系统的研究与设计
  3. asp.net基于net的小美果蔬批发网-蔬菜商城系统-计算机毕业设计
  4. 亚马逊广告接口 amazon advert api 申请流程
  5. 维骨力Glucosamine的最关键的几点...
  6. 2018-10-29 直播课笔记
  7. [Python] 错误“IndentationError: unindent does not match any outer indentation level”是什么意思?...
  8. 云南大学计算机值得调剂吗,这4所211报名无人问,调剂人爆满!今年是不是你的菜?...
  9. Cesium-监听地图服务是否加载完成的方法
  10. python建立复数数组_深入理解NumPy简明教程---数组1