一、启动窗口的类型

 private static final int STARTING_WINDOW_TYPE_NONE = 0; //不使用启动窗口private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1; //应用热启动,启动窗口是activity的一张截图private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2; //应用冷启动,启动窗口是应用配置的一张图片

二、启动窗口的意义

应用有三种启动状态:冷启动、温启动、热启动
冷启动:应用自设备开机后首次启动;系统终止应用后,应用首次启动。时间开销最大。
热启动:应用的所有Activity仍驻留在内存中,系统的全部工作只是将Activity重新带到前台。时间开销最小。
温启动:应用进程已经存在,但应用必须通过调用onCreate()从头重新创建Activity。时间开销中间。

应用冷启动时,自点击home launcher的应用Icon起,到应用完成第一次绘制,系统进程、应用进程要完成大量的初始化工作,非常费时。假如没有starting window,这一过程给用户的感受就是:这应用好卡,点击之后3秒钟过去了,还没反应;或者,我是不是没点到应用图标上。starting window的意义就在于,用户点击应用Icon之后,启动窗口会立即打开,等应用完成第一次绘制后,启动窗口被系统销毁,纵享丝滑。

三、启动窗口的流程

3.1 ActivityStack.SHOW_APP_STARTING_PREVIEW

    // Set to false to disable the preview that is shown while a new activity// is being started.private static final boolean SHOW_APP_STARTING_PREVIEW = true; // 1. starting window的总开关...
void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,boolean newTask, boolean keepCurTransition, ActivityOptions options) {Task rTask = r.getTask();final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();final boolean isOrhasTask = rTask == this || hasChild(rTask);...} else if (SHOW_APP_STARTING_PREVIEW && doShow) {// Figure out if we are transitioning from another activity that is// "has the same starting icon" as the next one.  This allows the// window manager to keep the previous window it had previously// created, if it still had one.Task prevTask = r.getTask();ActivityRecord prev = prevTask.topActivityWithStartingWindow();if (prev != null) {// We don't want to reuse the previous starting preview if:// (1) The current activity is in a different task.if (prev.getTask() != prevTask) {prev = null;}// (2) The current activity is already displayed.else if (prev.nowVisible) {prev = null;}}r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity)); // 2. 调用ActivityRecord默认方法,两个类在同一个package,无需import}} else {// If this is the first activity, don't do any fancy animations,// because there is nothing for it to animate on top of.ActivityOptions.abort(options);}}

注释1:该boolean值是ActivityStack类的全局变量,正如英文注释,此布尔值是startingwindow的总开关。
注释2:r是startActivityLocked方法的一个形参,是ActivityRecord类型的变量,调用ActivityRecord.showStartingWindow()方法。

3.1 ActivityRecord.showStartingWindow()

    static final int STARTING_WINDOW_NOT_SHOWN = 0;static final int STARTING_WINDOW_SHOWN = 1;static final int STARTING_WINDOW_REMOVED = 2;int mStartingWindowState = STARTING_WINDOW_NOT_SHOWN; // 1. 启动窗口的状态void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch) {if (mTaskOverlay) {// We don't show starting window for overlay activities.return;}if (pendingOptions != null&& pendingOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {// Don't show starting window when using shared element transition.return;}final CompatibilityInfo compatInfo =mAtmService.compatibilityInfoForPackageLocked(info.applicationInfo);final boolean shown = addStartingWindow(packageName, theme,prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),allowTaskSnapshot(),mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal()); // 2. 调用本类方法if (shown) {mStartingWindowState = STARTING_WINDOW_SHOWN; // 3. 置启动窗口的状态}}

注释1:全局变量,访问权限为default。描述启动窗口的状态,一共有3种状态:默认的not_shown;添加成功后的shown;移除后的removed。
注释2:调用本类的addStartingWindow方法,返回值类型为boolean值,代表启动窗口是否添加成功。
注释3:启动窗口添加成功后,其状态设置为shown。

3.3 ActivityRecord.addStartingWindow()

boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,boolean allowTaskSnapshot, boolean activityCreated) {...final int type = getStartingWindowType(newTask, taskSwitch, processRunning,allowTaskSnapshot, activityCreated, snapshot);if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {if (isActivityTypeHome()) {// The snapshot of home is only used once because it won't be updated while screen// is on (see {@link TaskSnapshotController#screenTurningOff}).mWmService.mTaskSnapshotController.removeSnapshotCache(task.mTaskId);if ((mDisplayContent.mAppTransition.getTransitFlags()& WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0) {// Only use snapshot of home as starting window when unlocking directly.return false;}}return createSnapshot(snapshot); // 1. 应用热启动}// If this is a translucent window, then don't show a starting window -- the current// effect (a full-screen opaque starting window that fades away to the real contents// when it is ready) does not work for this.ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Checking theme of starting window: 0x%x", theme);if (theme != 0) {AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,com.android.internal.R.styleable.Window,mWmService.mCurrentUserId);if (ent == null) {// Whoops!  App doesn't exist. Um. Okay. We'll just pretend like we didn't// see that.return false;}final boolean windowIsTranslucent = ent.array.getBoolean(com.android.internal.R.styleable.Window_windowIsTranslucent, false); // 2. 查看启动Activity主题的windowIsTranslucent属性final boolean windowIsFloating = ent.array.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false); // 3. 查看windowIsFloating属性final boolean windowShowWallpaper = ent.array.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false);final boolean windowDisableStarting = ent.array.getBoolean(com.android.internal.R.styleable.Window_windowDisablePreview, false); // 4.查看windowDisablePreview属性ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Translucent=%s Floating=%s ShowWallpaper=%s",windowIsTranslucent, windowIsFloating, windowShowWallpaper);if (windowIsTranslucent) {return false;}if (windowIsFloating || windowDisableStarting) {return false;}if (windowShowWallpaper) {if (getDisplayContent().mWallpaperController.getWallpaperTarget() == null) {// If this theme is requesting a wallpaper, and the wallpaper// is not currently visible, then this effectively serves as// an opaque window and our starting window transition animation// can still work.  We just need to make sure the starting window// is also showing the wallpaper.windowFlags |= FLAG_SHOW_WALLPAPER;} else {return false;}}}if (transferStartingWindow(transferFrom)) {return true;}// There is no existing starting window, and we don't want to create a splash screen, so// that's it!if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) { // 5. 代码走到这里,说明启动窗口的类型不是snapshot。如果也不是splash,那就只能是none.return false;}ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Creating SplashScreenStartingData");mStartingData = new SplashScreenStartingData(mWmService, pkg, // 6. mStartingData赋值theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,// getMergedOverrideConfiguration());getMergedOverrideConfiguration(), overrideTheme);scheduleAddStartingWindow(); // 7. 发送消息return true;}

注释1:return createSnapshot(snapshot);启动窗口类型为snapshot,给mStartingData赋值snapshotdata,然后发送消息。

    private boolean createSnapshot(ActivityManager.TaskSnapshot snapshot) {if (snapshot == null) {return false;}ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Creating SnapshotStartingData");mStartingData = new SnapshotStartingData(mWmService, snapshot); // 给mStartingData赋值snapshotdata,类比注释6scheduleAddStartingWindow(); // 对应注释7return true;}

注释2、3、4:代码走到这,说明是应用冷启动。查看应用launcher activity的主题,如果其主题将这3个属性中的任何一个置为true,冷启动的时候,不会创建splash screen。
注释5:代码走到这,说明启动窗口的type既不是snapshot,也不是splashscreen,只能是none,直接return false即可,即根本没有启动窗口。
注释6 、7类比注释1。

3.4 ActivityRecord.scheduleAddStartingWindow()

 private final AddStartingWindow mAddStartingWindow = new AddStartingWindow();...void scheduleAddStartingWindow() {// Note: we really want to do sendMessageAtFrontOfQueue() because we// want to process the message ASAP, before any other queued// messages.if (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Enqueueing ADD_STARTING");mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow); // 1. Handler机制,跨线程通信。}}private class AddStartingWindow implements Runnable {@Overridepublic void run() {// Can be accessed without holding the global lockfinal StartingData startingData;synchronized (mWmService.mGlobalLock) {// There can only be one adding request, silly caller!mWmService.mAnimationHandler.removeCallbacks(this);if (mStartingData == null) {// Animation has been canceled... do nothing.ProtoLog.v(WM_DEBUG_STARTING_WINDOW,"startingData was nulled out before handling"+ " mAddStartingWindow: %s", ActivityRecord.this);return;}///M: Check starting surface aleady add or not.if (startingSurface != null) {Slog.v(TAG_WM, "already has a starting surface!!!");return;}startingData = mStartingData; // 2. 可能有两种类型。}ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Add starting %s: startingData=%s",this, startingData);WindowManagerPolicy.StartingSurface surface = null;try {surface = startingData.createStartingSurface(ActivityRecord.this); // 3. 可能两种类型} catch (Exception e) {Slog.w(TAG, "Exception when adding starting window", e);}if (surface != null) {boolean abort = false;synchronized (mWmService.mGlobalLock) {// If the window was successfully added, then we need to remove it.if (mStartingData == null) {ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Aborted starting %s: startingData=%s",ActivityRecord.this, mStartingData);startingWindow = null;mStartingData = null;abort = true;} else {startingSurface = surface;}if (!abort) {ProtoLog.v(WM_DEBUG_STARTING_WINDOW,"Added starting %s: startingWindow=%s startingView=%s",ActivityRecord.this, startingWindow, startingSurface);}}if (abort) {surface.remove();}} else {ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Surface returned was null: %s",ActivityRecord.this);}}}

注释1: mAddStartingWindow是AddStartingWindow类型的变量,AddStartingWindow实现了Runnable接口,执行mAddStartingWindow的run()方法。
注释2、3:可能有snapshot、splash两种类型,下面主要介绍splash的情况。

3.5 SplashScreenStartingData.createStartingSurface()

    StartingSurface createStartingSurface(ActivityRecord activity) {return mService.mPolicy.addSplashScreen(activity.token, mPkg, mTheme, mCompatInfo,mNonLocalizedLabel, mLabelRes, mIcon, mLogo, mWindowFlags,// mMergedOverrideConfiguration, activity.getDisplayContent().getDisplayId());mMergedOverrideConfiguration, activity.getDisplayContent().getDisplayId(),mOverrideTheme);}

3.6 PhoneWindowManager.addSplashScreen()

public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,int logo, int windowFlags, Configuration overrideConfig, int displayId) {if (!SHOW_SPLASH_SCREENS) {return null;}if (packageName == null) {return null;}WindowManager wm = null;View view = null;try {Context context = mContext;if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen " + packageName+ ": nonLocalizedLabel=" + nonLocalizedLabel + " theme="+ Integer.toHexString(theme));// Obtain proper context to launch on the right display.final Context displayContext = getDisplayContext(context, displayId);if (displayContext == null) {// Can't show splash screen on requested display, so skip showing at all.return null;}context = displayContext;if (theme != context.getThemeResId() || labelRes != 0) {try {context = context.createPackageContext(packageName, CONTEXT_RESTRICTED);context.setTheme(theme);} catch (PackageManager.NameNotFoundException e) {// Ignore}}if (overrideConfig != null && !overrideConfig.equals(EMPTY)) {if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen: creating context based"+ " on overrideConfig" + overrideConfig + " for splash screen");final Context overrideContext = context.createConfigurationContext(overrideConfig);overrideContext.setTheme(theme);final TypedArray typedArray = overrideContext.obtainStyledAttributes(com.android.internal.R.styleable.Window);final int resId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);if (resId != 0 && overrideContext.getDrawable(resId) != null) {// We want to use the windowBackground for the override context if it is// available, otherwise we use the default one to make sure a themed starting// window is displayed for the app.if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen: apply overrideConfig"+ overrideConfig + " to starting window resId=" + resId);context = overrideContext;}typedArray.recycle();}final PhoneWindow win = new PhoneWindow(context);win.setIsStartingWindow(true);CharSequence label = context.getResources().getText(labelRes, null);// Only change the accessibility title if the label is localizedif (label != null) {win.setTitle(label, true);} else {win.setTitle(nonLocalizedLabel, false);}win.setType(WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);synchronized (mWindowManagerFuncs.getWindowManagerLock()) {// Assumes it's safe to show starting windows of launched apps while// the keyguard is being hidden. This is okay because starting windows never show// secret information.// TODO(b/113840485): Occluded may not only happen on default displayif (displayId == DEFAULT_DISPLAY && mKeyguardOccluded) {windowFlags |= FLAG_SHOW_WHEN_LOCKED;}}// Force the window flags: this is a fake window, so it is not really// touchable or focusable by the user.  We also add in the ALT_FOCUSABLE_IM// flag because we do know that the next window will take input// focus, so we want to get the IME window up on top of us right away.win.setFlags(windowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,windowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);win.setDefaultIcon(icon);win.setDefaultLogo(logo);win.setLayout(WindowManager.LayoutParams.MATCH_PARENT,WindowManager.LayoutParams.MATCH_PARENT);final WindowManager.LayoutParams params = win.getAttributes();params.token = appToken;params.packageName = packageName;params.windowAnimations = win.getWindowStyle().getResourceId(com.android.internal.R.styleable.Window_windowAnimationStyle, 0);params.privateFlags |=WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED;params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;if (!compatInfo.supportsScreen()) {params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;}params.setTitle("Splash Screen " + packageName);// addSplashscreenContent(win, context);boolean contentAdded = addSplashscreenContent(win, context);wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);view = win.getDecorView();if ((context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)!= Configuration.UI_MODE_NIGHT_YES) {PhoneWindowManagerInjector.addStartingWindow(context, view, win, label != null ? label : nonLocalizedLabel);}if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "Adding splash screen window for "+ packageName + " / " + appToken + ": " + (view.getParent() != null ? view : null));wm.addView(view, params); // 1. 添加view// Only return the view if it was successfully added to the// window manager... which we can tell by it having a parent.return view.getParent() != null ? new SplashScreenSurface(view, appToken) : null;} catch (WindowManager.BadTokenException e) {// ignoreLog.w(TAG, appToken + " already running, starting window not displayed. " +e.getMessage());} catch (RuntimeException e) {// don't crash if something else bad happens, for example a// failure loading resources because we are loading from an app// on external storage that has been unmounted.Log.w(TAG, appToken + " failed creating starting window", e);} finally {if (view != null && view.getParent() == null) {Log.w(TAG, "view not successfully added to wm, removing view");wm.removeViewImmediate(view);}}return null;}

注释1:wm.addview。wm是WindowManager类型的,addview()方法的真正实现在WindowManagerImpl类中。

3.7 WindowManagerImpl.addview()

    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {applyDefaultToken(params);mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,mContext.getUserId());}

3.8 WindowManagerGlobal.addView()

    public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow, int userId) {if (view == null) {throw new IllegalArgumentException("view must not be null");}if (display == null) {throw new IllegalArgumentException("display must not be null");}if (!(params instanceof WindowManager.LayoutParams)) {throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");}final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;if (parentWindow != null) {parentWindow.adjustLayoutParamsForSubWindow(wparams);} else {// If there's no parent, then hardware acceleration for this view is// set from the application's hardware acceleration setting.final Context context = view.getContext();if (context != null&& (context.getApplicationInfo().flags& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;}}ViewRootImpl root;View panelParentView = null;synchronized (mLock) {// Start watching for system property changes.if (mSystemPropertyUpdater == null) {mSystemPropertyUpdater = new Runnable() {@Override public void run() {synchronized (mLock) {for (int i = mRoots.size() - 1; i >= 0; --i) {mRoots.get(i).loadSystemProperties();}}}};SystemProperties.addChangeCallback(mSystemPropertyUpdater);}int index = findViewLocked(view, false);if (index >= 0) {if (mDyingViews.contains(view)) {// Don't wait for MSG_DIE to make it's way through root's queue.mRoots.get(index).doDie();} else {throw new IllegalStateException("View " + view+ " has already been added to the window manager.");}// The previous removeView() had not completed executing. Now it has.}// If this is a panel window, then find the window it is being// attached to for future reference.if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {final int count = mViews.size();for (int i = 0; i < count; i++) {if (mRoots.get(i).mWindow.asBinder() == wparams.token) {panelParentView = mViews.get(i);}}}root = new ViewRootImpl(view.getContext(), display);view.setLayoutParams(wparams);mViews.add(view);mRoots.add(root);mParams.add(wparams);// do this last because it fires off messages to start doing thingstry {root.setView(view, wparams, panelParentView, userId); // 1. 调用} catch (RuntimeException e) {// BadTokenException or InvalidDisplayException, clean up.index = findViewLocked(view, false);if (index >= 0) {Log.e(TAG, "BadTokenException or InvalidDisplayException, clean up.");removeViewLocked(index, true);}throw e;}}}

注释1:调用ViewRootImpl的setView()方法

3.9 ViewRootImpl.setView()

try {mOrigWindowType = mWindowAttributes.type;mAttachInfo.mRecomputeGlobalAttributes = true;collectViewAttributes();adjustLayoutParamsForCompatibility(mWindowAttributes);res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,mAttachInfo.mDisplayCutout, inputChannel,mTempInsets, mTempControls); // 1. 跨进程通信setFrame(mTmpFrame);} catch (RemoteException e) {mAdded = false;mView = null;mAttachInfo.mRootView = null;inputChannel = null;mFallbackEventHandler.setView(null);unscheduleTraversals();setAccessibilityFocus(null, null);throw new RuntimeException("Adding window failed", e);} finally {if (restore) {attrs.restore();}}

3.10 Session.addToDisplayAsUser()

public int addToDisplayAsUser(IWindow window, int seq, WindowManager.LayoutParams attrs,int viewVisibility, int displayId, int userId, Rect outFrame,Rect outContentInsets, Rect outStableInsets,DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,outInsetsState, outActiveControls, userId);}

3.11 WindowManagerService.addWindow()

启动窗口 [Android R]相关推荐

  1. Activity启动窗口(StartingWindow)的添加流程

    本篇基于Android Q分析 在Activity启动的时候,Android系统会为它添加一个启动窗口,作用是在应用程序主Activity还没有显示出来的时候,它作为一个预览窗口先让用户能看到一个画面 ...

  2. android R vendor.boot-hal-1-1启动失败问题分析

    记一个android R上开机启动vendor.boot-hal-1-1进程启动失败的过程分析,总结一下下,也给需要的提供个参考. 问题: 在开机启动过程中,一直报错,vendor.boot-hal- ...

  3. android R启动找不到super分区问题

    总结一个android R打开super动态分区后,init第一阶段启动失败的例子,也为了自己后面看看趟过的坑. 在移植适配android R项目,主要做了如下事情: 打开BOARD_AVB_ENAB ...

  4. Android R camera Hal启动(下)

    文章目录 前言 代码流程分析 总结 前言 接上一篇Android R camera Hal启动(上)接着写,把谷歌的代码都分析完成,高通/MTK的代码就不贴了. 代码流程分析 上一篇说到getProv ...

  5. Android6.0 WMS(八) 显示Activity的启动窗口

     在Android系统中,Activity组件在启动之后,并且在它的窗口显示出来之前,可以显示一个启动窗口.这个启动窗口可以看作是Activity组件的预览窗口,是由WindowManagerSe ...

  6. Android 10.0 PackageManagerService(一)工作原理及启动流程-[Android取经之路]

    摘要:PackageManagerService是Android系统核心服务之一,在Android中的非常重要,主要负责APK.jar包等的管理. 阅读本文大约需要花费50分钟. 文章的内容主要还是从 ...

  7. Android10.0系统启动之Launcher(桌面)启动流程-[Android取经之路]

    摘要:上一节我们讲完了Android10.0的ActivityManagerService的启动流程,在AMS的最后启动了Launcher进程,今天我们就来看看Launcher的真正启动流程. 阅读本 ...

  8. Android 10.0 系统服务之ActivityMnagerService-AMS启动流程-[Android取经之路]

    摘要:上一节我们讲完了SystemServer的启动过程,这一节接着上一节的步骤,来讲解ActivityManagerService的启动过程. ActivityManagerService简称AMS ...

  9. Android研究-linux内核启动到android系统

    很多人阅读代码,总喜欢从头开始,这样觉得很安全,有依靠,无论如何总是能知道"头",有头就能找到任何需要的部分. Android生在linux内核基础上,linux内核启动的最后一步 ...

最新文章

  1. ftp上传当天文件的方法_2种windows到linux上传文件的方法
  2. 推荐一个很好用的脚本session snapper
  3. Linux命令学习总结:dos2unix - unix2dos
  4. VS2017创建ASP.NET Core Web程序
  5. mysql innodb事务中_MySQL InnoDB如何保证事务特性示例详解
  6. LeetCode--33. 搜索旋转排序数组(二分法)
  7. 在ubuntu上启动一个vue项目
  8. php bmp中创建图像bmp2gd,让GD支持32位BMP
  9. “落花有意随流水,流水无情恋落花。”出处
  10. 华为回应申请大量“鸿蒙”商标;5G第一个演进版本标准正式完成;SUSE 收购 Rancher Labs| 极客头条
  11. Windows无法安装到这个磁盘。选中的磁盘具有MBR分区表。在EFI系统上,Windows只能安装到GPT磁盘
  12. 简述osi参考模型各层主要功能_简述OSI参考模型定义及各层的主要功能
  13. uniapp项目 App端实现微信登录、QQ登录
  14. 程序员必读书单 1.0
  15. 基于FBX SDK的FBX模型解析与加载 -(一)
  16. Arista教你如何讨容器、白盒基础架构数据中心玩家的欢心
  17. 《无尽的拉格朗日》--Day10体验
  18. 笔记:二元Probit与Logit模型
  19. 被物联网卡套路了怎么办,物联卡这几种“套路”要分清了!
  20. 微型计算机 杂志以马,马寅 | 创造我们的实践智慧

热门文章

  1. ISCC2021-这是啥
  2. java爬虫编写步骤_JAVA爬虫--编写第一个网络爬虫程序
  3. 高群耀电影垂直社交新体验恢复百亿观影人次
  4. c语言中recvfrom函数,C语言sendto()函数-经socket传送数据以及recvfrom函数《转》
  5. 计算机和智能12 【A. M. TURING】(由于要写作业所以就把一篇29页的英文论文翻译一下!个人翻译,水平很菜,纯属爱好!仅供参考!)
  6. [转] 【卡饭首发】卡巴斯基2016年Q1威胁演化报告
  7. 当下最火的中台到底是个什么鬼,看完这一篇最通俗易懂的文章后,你就会彻底明白了!...
  8. 解决VS无法识别手动创建的app.manifest文件的问题
  9. 云南医药杂志云南医药杂志社云南医药编辑部2023年第2期目录
  10. 爱情的诗·16~20节