上一篇文章Android 开关机动画显示源码分析详细介绍了开关机动画的显示过程,Android系统开机时,在启动SurfaceFlinger服务过程中通过Android属性系统方式来启动bootanim进程,实现开机动画显示过程;当系统关机时,又是如何启动关机动画的呢?Android系统的整个关机流程又是怎样的呢?本文就针对这两个问题透过源码来给出具体的分析。我们知道,当长按电源键,系统会弹出关机提示对话框

当点击选择关机时,系统就会完成整个关机流程。接下来就通过源码来介绍Android关机流程的完整实现过程。当长按电源键时,按键消息被分发到PhoneWindowManager的interceptKeyBeforeQueueing函数中处理:

public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
...
switch (keyCode) {
...
case KeyEvent.KEYCODE_POWER: {
result &= ~ACTION_PASS_TO_USER;
if (down) {
if (isScreenOn && !mPowerKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
mPowerKeyTriggered = true;
mPowerKeyTime = event.getDownTime();
interceptScreenshotChord();//抓屏
}
ITelephony telephonyService = getTelephonyService();
boolean hungUp = false;
if (telephonyService != null) {
try {
if (telephonyService.isRinging()) {
//当来电时按下电源键,启动静音
telephonyService.silenceRinger();
} else if ((mIncallPowerBehavior
& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
&& telephonyService.isOffhook()) {
// Otherwise, if "Power button ends call" is enabled,
// the Power button will hang up any current active call.
hungUp = telephonyService.endCall();
}
} catch (RemoteException ex) {
Log.w(TAG, "ITelephony threw RemoteException", ex);
}
}
interceptPowerKeyDown(!isScreenOn || hungUp
|| mVolumeDownKeyTriggered || mVolumeUpKeyTriggered);
} else {
...
}
break;
}
...
}
return result;
}

电源键和音量键的组合可以实现特定功能,比如按下电源键和音量向下键,可实现抓屏,interceptKeyBeforeQueueing函数首先根据条件处理电源键按下的特定任务,然后调用interceptPowerKeyDown做进一步处理

private void interceptPowerKeyDown(boolean handled) {
mPowerKeyHandled = handled;
if (!handled) {
//隔500ms处理电源按键事件
mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout());
}
}

这里的mHandler是在初始化PhoneWindowManager对象时创建的

public void init(Context context, IWindowManager windowManager,WindowManagerFuncs windowManagerFuncs,
LocalPowerManager powerManager) {
...
mHandler = new PolicyHandler();
...
}

将一个Runnable对象mPowerLongPress发送到PolicyHandler中进行处理

private final Runnable mPowerLongPress = new Runnable() {
public void run() {
// The context isn't read
if (mLongPressOnPowerBehavior < 0) {
mLongPressOnPowerBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnPowerBehavior);
}
switch (mLongPressOnPowerBehavior) {
case LONG_PRESS_POWER_NOTHING:
break;
case LONG_PRESS_POWER_GLOBAL_ACTIONS:
mPowerKeyHandled = true;
performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
showGlobalActionsDialog();
break;
case LONG_PRESS_POWER_SHUT_OFF:
mPowerKeyHandled = true;
performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
mWindowManagerFuncs.shutdown();
break;
}
}
};

在处理电源长按事件时,根据mLongPressOnPowerBehavior完成不同的处理过程,mLongPressOnPowerBehavior的值是通过配置文件来设置的,在frameworks/base/core/res/values/config.xml中有以下一段配置:


通过读取配置文件取得config_longPressOnPowerBehavior配置的值为1,因此将显示关机对话框

case LONG_PRESS_POWER_GLOBAL_ACTIONS:
mPowerKeyHandled = true;
performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
//向ActivityManagerService请求关闭所有窗口
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
//显示关机对话框
showGlobalActionsDialog();
break;

关机对话框显示:

void showGlobalActionsDialog() {
//创建GlobalActions对象
if (mGlobalActions == null) {
mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
}
final boolean keyguardShowing = keyguardIsShowingTq();
//显示对话框
mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
if (keyguardShowing) {
// since it took two seconds of long press to bring this up,
// poke the wake lock so they have some time to see the dialog.
mKeyguardMediator.pokeWakelock();
}
}

通过GlobalActions的showDialog函数来显示对话框

public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
mKeyguardShowing = keyguardShowing;
mDeviceProvisioned = isDeviceProvisioned;
if (mDialog != null) {
mDialog.dismiss();
mDialog = null;
// Show delayed, so that the dismiss of the previous dialog completes
mHandler.sendEmptyMessage(MESSAGE_SHOW);
} else {
handleShow();//关机对话框显示
}
}
private void handleShow() {
//创建对话框
mDialog = createDialog();
prepareDialog();//设置对话框属性
mDialog.show();//显示对话框
mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
}

createDialog()函数用于创建一个即将显示的关机对话框,就是前面图片显示的对话框。

private AlertDialog createDialog() {
//=========================创建对话框显示的列表项视图=====================================
//每一个列表项被被抽象为Action对象,
// Simple toggle style if there's no vibrator, otherwise use a tri-state
if (!mHasVibrator) {
mSilentModeAction = new SilentModeToggleAction();
} else {
mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);
}
//创建飞行模式切换ToggleAction对象
mAirplaneModeOn = new ToggleAction(
R.drawable.ic_lock_airplane_mode,
R.drawable.ic_lock_airplane_mode_off,
R.string.global_actions_toggle_airplane_mode,
R.string.global_actions_airplane_mode_on_status,
R.string.global_actions_airplane_mode_off_status) {
void onToggle(boolean on) {
if (mHasTelephony && Boolean.parseBoolean(SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
mIsWaitingForEcmExit = true;
// Launch ECM exit dialog
Intent ecmDialogIntent =new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(ecmDialogIntent);
} else {
changeAirplaneModeSystemSetting(on);
mHandler.removeMessages(EVENT_SERVICE_CHANGE_WAIT_TIMEOUT);
mHandler.sendEmptyMessageDelayed(EVENT_SERVICE_CHANGE_WAIT_TIMEOUT,DELAY_AIRPLANE_SET_TIME);
}
}
@Override
protected void changeStateFromPress(boolean buttonOn) {
if (!mHasTelephony) return;
// In ECM mode airplane state cannot be changed
if (!(Boolean.parseBoolean(SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
mState = buttonOn ? State.TurningOn : State.TurningOff;
mAirplaneState = mState;
}
}
public boolean showDuringKeyguard() {
return true;
}
public boolean showBeforeProvisioning() {
return false;
}
};
//更新当前飞行模式状态
onAirplaneModeChanged();
onRadioBusyStateChanged();
mItems = new ArrayList<Action>();
//向mItems列表中依次添加关机选择,飞行模式切换,静音模式选择
// first: power off
mItems.add(
//创建关机SinglePressAction
new SinglePressAction(com.android.internal.R.drawable.ic_lock_power_off,
R.string.global_action_power_off) {
public void onPress() {
// shutdown by making sure radio and power are handled accordingly.
mWindowManagerFuncs.shutdown();
}
public boolean onLongPress() {
mWindowManagerFuncs.rebootSafeMode();
return true;
}
public boolean showDuringKeyguard() {
return true;
}
public boolean showBeforeProvisioning() {
return true;
}
});
// next: airplane mode
mItems.add(mAirplaneModeOn);
// last: silent mode
if (SHOW_SILENT_TOGGLE) {
mItems.add(mSilentModeAction);
}
//获取系统中所有用户信息
List<UserInfo> users = mContext.getPackageManager().getUsers();
if (users.size() > 1) {//对于多用户Android系统,在显示的对话框下面添加用户切换选项
UserInfo currentUser;
try {
currentUser = ActivityManagerNative.getDefault().getCurrentUser();
} catch (RemoteException re) {
currentUser = null;
}
for (final UserInfo user : users) {
boolean isCurrentUser = currentUser == null
? user.id == 0 : (currentUser.id == user.id);
SinglePressAction switchToUser = new SinglePressAction(
com.android.internal.R.drawable.ic_menu_cc,
(user.name != null ? user.name : "Primary")
+ (isCurrentUser ? " \u2714" : "")) {
public void onPress() {
try {
ActivityManagerNative.getDefault().switchUser(user.id);
getWindowManager().lockNow();
} catch (RemoteException re) {
Log.e(TAG, "Couldn't switch user " + re);
}
} public boolean showDuringKeyguard() {
return true;
} public boolean showBeforeProvisioning() {
return false;
}
};
mItems.add(switchToUser);
}
}
mAdapter = new MyAdapter();//创建适配器,保存了所有数据,这里用MyAdapter保存列表项视图
//=========================创建对话框=========================================
final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
ab.setAdapter(mAdapter, this).setInverseBackgroundForced(true);
final AlertDialog dialog = ab.create();
dialog.getListView().setItemsCanFocus(true);
dialog.getListView().setLongClickable(true);
dialog.getListView().setOnItemLongClickListener(
new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
long id) {
return mAdapter.getItem(position).onLongPress();//视图和数据相关联
}
});
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
dialog.setOnDismissListener(this);
return dialog;
}

这里需要介绍对话框列表创建的方法,代码实现比较灵魂,值得学习。首先将显示对话框列表中的每一项用应该Action来描述,Action是一个接口,定义了每一个列表项的动作方法

各个列表项定义不同类型的Action,比如关机选项使用SinglePressAction来描述,目前Android系统定义了几种类型的Action,对应关机对话框中的不同选项。

将创建的列表选项添加到动态数组mItems中,并且为该对话框定义一个适配器MyAdapter,在单击对话框列表项时,调用适配器中对应的项来响应单击事件。Android适配器的使用实现了MVC编程模式,将数据和视图分开。通常我们将数据保存在一个数组中,通过适配器和视图控件关联显示,只不过这里的数据比较特殊,它是用来描述列表项显示的内容。

public boolean onItemLongClick(AdapterView<?> parent, View view, int position,long id) {
return mAdapter.getItem(position).onLongPress();
} public void onClick(DialogInterface dialog, int which) {
if (!(mAdapter.getItem(which) instanceof SilentModeTriStateAction)) {
dialog.dismiss();
}
mAdapter.getItem(which).onPress();
}

对于关机选项,其单击和长按事件处理过程如下:

public void onPress() {
// shutdown by making sure radio and power are handled accordingly.
mWindowManagerFuncs.shutdown();
} public boolean onLongPress() {
mWindowManagerFuncs.rebootSafeMode();
return true;
}

对关机的处理都是调用mWindowManagerFuncs来完成的,mWindowManagerFuncs的类型为WindowManagerFuncs,在调用PhoneWindowManager的init函数时通过参数传进来。mWindowManagerFuncs对象在那里构造呢?WindowManagerService定义了一个WindowManagerPolicy类型变量:

final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();

通过策略管理类PolicyManager对象的makeNewWindowManager函数创建一个窗口策略管理对象

public static WindowManagerPolicy makeNewWindowManager() {
return sPolicy.makeNewWindowManager();
}

该函数通过Binder通信方式请求服务端Policy来完成对象的创建过程

Policy.java

public WindowManagerPolicy makeNewWindowManager() {
return new PhoneWindowManager();
}

在WindowManagerService的构造过程中,会创建一个PolicyThread类来初始化窗口管理策略WindowManagerPolicy

private WindowManagerService(Context context, PowerManagerService pm,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {
...
PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
thr.start();
synchronized (thr) {
while (!thr.mRunning) {
try {
thr.wait();
} catch (InterruptedException e) {
}
}
}
...
}

在PolicyThread线程中执行初始化函数

static class PolicyThread extends Thread {
@Override
public void run() {
Looper.prepare();
...
mPolicy.init(mContext, mService, mService, mPM);
synchronized (this) {
mRunning = true;
notifyAll();
}
...
Looper.loop();
}
}

mPolicy的类型定义为WindowManagerPolicy类型,而WindowManagerPolicy是一个接口类型,PhoneWindowManager实现了该接口,为mPolicy创建的真正对象是PhoneWindowManager对象,因此PolicyThread线程将调用PhoneWindowManager的init函数

public void init(Context context, IWindowManager windowManager,
WindowManagerFuncs windowManagerFuncs,LocalPowerManager powerManager) {
...
mWindowManagerFuncs = windowManagerFuncs;
...
}

传进来的参数windowManager和windowManagerFuncs都是WindowManagerService对象,因为WindowManagerService继承于IWindowManager.Stub类同时实现了WindowManagerPolicy.WindowManagerFuncs接口,这下就很清晰地知道GlobalActions类中的mWindowManagerFuncs变量其实是WindowManagerService对象,因此关机的单击及长按事件由WindowManagerService实现:

public void shutdown() {
ShutdownThread.shutdown(mContext, true);
} public void rebootSafeMode() {
ShutdownThread.rebootSafeMode(mContext, true);
}

WindowManagerService也不真正实现关机操作,而是转交个ShutdownThread来完成。对于关机处理过程:

public static void shutdown(final Context context, boolean confirm) {
mReboot = false;
mRebootSafeMode = false;
shutdownInner(context, confirm);
}

该函数实现非常简单,只是设置一些标志位,然后将关机任务又转交给shutdownInner来处理,这里的参数confirm用于标识是否需要弹出关机确认对话框。

static void shutdownInner(final Context context, boolean confirm) {
// ensure that only one thread is trying to power down.
// any additional calls are just returned
synchronized (sIsStartedGuard) {
if (sIsStarted) {
Log.d(TAG, "Request to shutdown already running, returning.");
return;
}
}
final int longPressBehavior = context.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnPowerBehavior);
final int resourceId = mRebootSafeMode
? com.android.internal.R.string.reboot_safemode_confirm
: (longPressBehavior == 2
? com.android.internal.R.string.shutdown_confirm_question
: com.android.internal.R.string.shutdown_confirm);
Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
//显示关机确认对话框
if (confirm) {
final CloseDialogReceiver closer = new CloseDialogReceiver(context);
final AlertDialog dialog = new AlertDialog.Builder(context)
.setTitle(mRebootSafeMode
? com.android.internal.R.string.reboot_safemode_title
: com.android.internal.R.string.power_off)
.setMessage(resourceId)
.setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
beginShutdownSequence(context);
}
})
.setNegativeButton(com.android.internal.R.string.no, null)
.create();
closer.dialog = dialog;
dialog.setOnDismissListener(closer);
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
dialog.show();
} else {//不显示关机确认对话框,直接进入关机流程
beginShutdownSequence(context);
}
}

调用beginShutdownSequence函数进入关机流程。

private static void beginShutdownSequence(Context context) {
synchronized (sIsStartedGuard) {
if (sIsStarted) {
Log.d(TAG, "Shutdown sequence already running, returning.");
return;
}
sIsStarted = true;
}
// throw up an indeterminate system dialog to indicate radio is
// shutting down.
ProgressDialog pd = new ProgressDialog(context);
pd.setTitle(context.getText(com.android.internal.R.string.power_off));
pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
pd.setIndeterminate(true);
pd.setCancelable(false);
pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
//pd.show();
shutdownTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_TIME;
//执行关机动画
String[] bootcmd = {"bootanimation", "shutdown"} ;
try {
Log.i(TAG, "exec the bootanimation ");
SystemProperties.set("service.bootanim.exit", "0");
Runtime.getRuntime().exec(bootcmd);
} catch (Exception e){
Log.e(TAG,"bootanimation command exe err!");
}
//初始化关机线程ShutdownThread
sInstance.mContext = context;
sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
// make sure we never fall asleep again
sInstance.mCpuWakeLock = null;
try {
sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
sInstance.mCpuWakeLock.setReferenceCounted(false);
sInstance.mCpuWakeLock.acquire();
} catch (SecurityException e) {
Log.w(TAG, "No permission to acquire wake lock", e);
sInstance.mCpuWakeLock = null;
}
// also make sure the screen stays on for better user experience
sInstance.mScreenWakeLock = null;
if (sInstance.mPowerManager.isScreenOn()) {
try {
sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
sInstance.mScreenWakeLock.setReferenceCounted(false);
sInstance.mScreenWakeLock.acquire();
} catch (SecurityException e) {
Log.w(TAG, "No permission to acquire wake lock", e);
sInstance.mScreenWakeLock = null;
}
}
// start the thread that initiates shutdown
sInstance.mHandler = new Handler() {
};
//启动关机线程ShutdownThread
sInstance.start();
}

这个函数执行两个任务,1)通过虚拟机Runtime启动关机动画进程bootanimation,用于显示关机动画,开关机动画在 Android 开关机动画显示源码分析进行了详细的分析介绍。2)启动关机线程ShutdownThread来完成关机操作

public void run() {
BroadcastReceiver br = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
// 用于接收关机广播
actionDone();
}
};
//写属性"sys.shutdown.requested"保存关机原因
{
String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
}
//如果是安全模式关机,写属性"persist.sys.safemode"
if (mRebootSafeMode) {
SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
}
Log.i(TAG, "Sending shutdown broadcast...");
// First send the high-level shut down broadcast.
mActionDone = false;
//发送关机广播
mContext.sendOrderedBroadcast(new Intent(Intent.ACTION_SHUTDOWN), null,
br, mHandler, 0, null, null);
//等待10S,前面定义的广播接收器收到关机广播时mActionDone设置为true,同时取消等待
final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
synchronized (mActionDoneSync) {
while (!mActionDone) {
long delay = endTime - SystemClock.elapsedRealtime();
if (delay <= 0) {
Log.w(TAG, "Shutdown broadcast timed out");
break;
}
try {
mActionDoneSync.wait(delay);
} catch (InterruptedException e) {
}
}
}
//10S时间内关闭ActivityManager服务
final IActivityManager am =ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
if (am != null) {
try {
am.shutdown(MAX_BROADCAST_TIME);
} catch (RemoteException e) {
}
}
//12s内关闭radios.
shutdownRadios(MAX_RADIO_WAIT_TIME);
//10s内关闭ICCS
shutdownIccs(MAX_ICC_WAIT_TIME);
// Shutdown MountService to ensure media is in a safe state
IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
public void onShutDownComplete(int statusCode) throws RemoteException {
Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
actionDone();
}
};
Log.i(TAG, "Shutting down MountService");
//20s内关闭MountService服务
mActionDone = false;
final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
synchronized (mActionDoneSync) {
try {
final IMountService mount = IMountService.Stub.asInterface(ServiceManager.checkService("mount"));
if (mount != null) {
mount.shutdown(observer);
} else {
Log.w(TAG, "MountService unavailable for shutdown");
}
} catch (Exception e) {
Log.e(TAG, "Exception during MountService shutdown", e);
}
while (!mActionDone) {
long delay = endShutTime - SystemClock.elapsedRealtime();
if (delay <= 0) {
Log.w(TAG, "Shutdown wait timed out");
break;
}
try {
mActionDoneSync.wait(delay);
} catch (InterruptedException e) {
}
}
}
//关机时间定义为5s,这里计算关机超过的时间
long shutdownDelay = shutdownTime - SystemClock.elapsedRealtime();
if (shutdownDelay > 0) {
Log.i(TAG, "Shutdown delay:"+shutdownDelay);
SystemClock.sleep(shutdownDelay);
}
//继续关机
rebootOrShutdown(mReboot, mRebootReason);
}

该函数内主要完成以下一些工作:

(1)发送关机广播ACTION_SHUTDOWN

(2)关闭ActivityManager 服务

(3)关闭无线相关的服务

(4)关闭Iccs

(5)关闭MountService服务

public static void rebootOrShutdown(boolean reboot, String reason) {
if (reboot) {//是否重启
Log.i(TAG, "Rebooting, reason: " + reason);
try {
PowerManagerService.lowLevelReboot(reason);
} catch (Exception e) {
Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
}
} else if (SHUTDOWN_VIBRATE_MS > 0) {//震动时间为500ms
// vibrate before shutting down
Vibrator vibrator = new SystemVibrator();
try {
//关机震动500ms
vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
} catch (Exception e) {
// Failure to vibrate shouldn't interrupt shutdown. Just log it.
Log.w(TAG, "Failed to vibrate during shutdown.", e);
}
// vibrator is asynchronous so we need to wait to avoid shutting down too soon.
try {
Thread.sleep(SHUTDOWN_VIBRATE_MS);
} catch (InterruptedException unused) {
}
}
//关闭电源
Log.i(TAG, "Performing low-level shutdown...");
PowerManagerService.lowLevelShutdown();
}

这里是启动关机震动并关闭电源

public static void lowLevelShutdown() {
nativeShutdown();
}

这里通过JNI调用C++的关机函数

static void nativeShutdown(JNIEnv *env, jobject clazz) {
delFlag();
android_reboot(ANDROID_RB_POWEROFF, 0, 0);
}

android_reboot函数最终通过Linux系统调用关闭系统。到此关机操作就基本完成了。

Android关机流程源码分析的更多相关文章

  1. [Android]Android系统启动流程源码分析

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5013863.html Android系统启动流程源码分析 首先 ...

  2. [Android]从Launcher开始启动App流程源码分析

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5017056.html 从Launcher开始启动App流程源码 ...

  3. Android笔记--View绘制流程源码分析(二)

    Android笔记--View绘制流程源码分析二 通过上一篇View绘制流程源码分析一可以知晓整个绘制流程之前,在activity启动过程中: Window的建立(activit.attach生成), ...

  4. Android笔记--View绘制流程源码分析(一)

    Android笔记--View绘制流程源码分析 View绘制之前框架流程分析 View绘制的分析始终是离不开Activity及其内部的Window的.在Activity的源码启动流程中,一并包含 着A ...

  5. Spring加载流程源码分析03【refresh】

      前面两篇文章分析了super(this)和setConfigLocations(configLocations)的源代码,本文来分析下refresh的源码, Spring加载流程源码分析01[su ...

  6. Spark(五十一):Spark On YARN(Yarn-Cluster模式)启动流程源码分析(二)

    上篇<Spark(四十九):Spark On YARN启动流程源码分析(一)>我们讲到启动SparkContext初始化,ApplicationMaster启动资源中,讲解的内容明显不完整 ...

  7. Spark(四十九):Spark On YARN启动流程源码分析(一)

    引导: 该篇章主要讲解执行spark-submit.sh提交到将任务提交给Yarn阶段代码分析. spark-submit的入口函数 一般提交一个spark作业的方式采用spark-submit来提交 ...

  8. spring boot 加载web容器tomcat流程源码分析

    spring boot 加载web容器tomcat流程源码分析 我本地的springboot版本是2.5.1,后面的分析都是基于这个版本 <parent> <groupId>o ...

  9. springboot 事务创建流程源码分析

    springboot 事务创建流程源码分析 目录 springboot 事务创建流程源码分析 1. 自动加载配置 2. InfrastructureAdvisorAutoProxyCreator类 3 ...

随机推荐

  1. css3之3D翻牌效果

      最近一直在学css3,发现他真的是越来越牛逼.现在的css3已经不在是以前的css了,它能做出的功能效果是我们没法想象的了.它可以实现flash,可以制作一些js能做出来的效果,还可以写出ps做出 ...

  2. android上传json与服务器交互

    http://www.2cto.com/kf/201403/289328.html http://www.tuicool.com/articles/FZJR3eB

  3. 通过实例深入理解lec和yacc

    本框架是一个lex/yacc完整的示例,包括详细的注释,用于学习lex/yacc程序基本的搭建方法,在linux/cygwin下敲入make就可以编译和执行.大部分框架已经搭好了,你只要稍加扩展就可以 ...

  4. C Traps and Pitfallss

    第一章 词法“陷阱” 发送阿罡发公司阿发送个发送阿罡发公司阿发送个 第二章

  5. 同步Flex Chart的数据提示

    原文 http://www.riafan.com/sync-datatips-for-flex-chart/ 图表数据提示的同步不仅包含单个图表内多个系列的数据提示的同步,也包含多个图表的数据提示的同 ...

  6. docker 容器管理上

    Docker 容器管理: docker create -it centos //这样可以创建一个容器,但该容器并没有启动: docker start container_id //启动容器后,可以使用 ...

  7. 图像处理库的比较:OpenCV,FreeImage,CImg,CxImage

    1.对OpenCV 的印象:功能十分的强大,而且支持目前先进的图像处理技术,体系十分完善,操作手册很详细,手册首先给大家补计算机视觉的知识,几乎涵盖了近10年内的主流算法: 然后将图像格式和矩阵运算, ...

  8. Linux是什么

    计算机主要以二进制为单位,而目前常用的磁盘容量单位为B,其单位换算为1B=8bit,其他的以1024为其倍数,如1GB=1024MB等. 操作系统(Operation System)主要用于管理与驱动 ...

  9. C#中关于DateTime的最大值和最小值

    System.DateTime的最小可能值:DateTime.MinValue.ToString()=0001-1-1 0:00:00 我们实际用的时候会指定一个默认值DateTime.Parse(& ...

  10. Zookeeper介绍

    Zookeeper是一个分布式的开源系统,目的是为分布式应用提供协调一致性服务. 分布式应用可以在Zookeeper提供的简单原语集之上构造更高层次的服务.比如统一命名服务.状态同步服务.集群管理.分 ...