編輯:關於Android編程
前面我們講解了系統截屏按鍵處理流程,HOME按鍵處理流程,今天再來講解一下電源開關機按鍵事件流程,當然這也是系統按鍵處理流程方面的最後一篇博客了。
和截屏按鍵、HOME按鍵的處理流程類似,電源按鍵由於也是系統級別的按鍵,所以對其的事件處理邏輯是和截屏按鍵、HOME按鍵類似,不在某一個App中,而是在PhoneWindowManager的dispatchUnhandledKey方法中。所以和前面兩篇類似,這裡我們也是從PhoneWindowManager的dispatchUnhandledKey方法開始我們今天電源開關機按鍵的事件流程分析。
下面首先看一下dispatchUnhandledKey方法的實現邏輯:
public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
...
KeyEvent fallbackEvent = null;
if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
final KeyCharacterMap kcm = event.getKeyCharacterMap();
final int keyCode = event.getKeyCode();
final int metaState = event.getMetaState();
final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
&& event.getRepeatCount() == 0;
// Check for fallback actions specified by the key character map.
final FallbackAction fallbackAction;
if (initialDown) {
fallbackAction = kcm.getFallbackAction(keyCode, metaState);
} else {
fallbackAction = mFallbackActions.get(keyCode);
}
if (fallbackAction != null) {
if (DEBUG_INPUT) {
Slog.d(TAG, "Fallback: keyCode=" + fallbackAction.keyCode
+ " metaState=" + Integer.toHexString(fallbackAction.metaState));
}
final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
fallbackEvent = KeyEvent.obtain(
event.getDownTime(), event.getEventTime(),
event.getAction(), fallbackAction.keyCode,
event.getRepeatCount(), fallbackAction.metaState,
event.getDeviceId(), event.getScanCode(),
flags, event.getSource(), null);
if (!interceptFallback(win, fallbackEvent, policyFlags)) {
fallbackEvent.recycle();
fallbackEvent = null;
}
if (initialDown) {
mFallbackActions.put(keyCode, fallbackAction);
} else if (event.getAction() == KeyEvent.ACTION_UP) {
mFallbackActions.remove(keyCode);
fallbackAction.recycle();
}
}
}
...
return fallbackEvent;
}
我們知道關於系統按鍵的處理邏輯被下放到了interceptFallback方法中,所以我們繼續看一下interceptFallback方法的實現邏輯。
private boolean interceptFallback(WindowState win, KeyEvent fallbackEvent, int policyFlags) {
int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);
if ((actions & ACTION_PASS_TO_USER) != 0) {
long delayMillis = interceptKeyBeforeDispatching(
win, fallbackEvent, policyFlags);
if (delayMillis == 0) {
return true;
}
}
return false;
}
通過分析interceptFallback方法的源碼,我們知道關於電源按鍵的處理邏輯在interceptKeyBeforeQueueing方法中,所以我們需要繼續看一下interceptKeyBeforeQueueing方法中關於電源按鍵的處理邏輯。
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
...
case KeyEvent.KEYCODE_POWER: {
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down) {
interceptPowerKeyDown(event, interactive);
} else {
interceptPowerKeyUp(event, interactive, canceled);
}
break;
}
...
return result;
}
這裡我們重點看一下電源按鍵的處理事件,可以發現當電源按鍵按下的時候我們調用了interceptPowerKeyDown方法,可以看出,這個方法就是處理電源事件的了,既然如此,我們繼續看一下interceptPowerKeyDown方法的執行邏輯。
private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
...
// Latch power key state to detect screenshot chord.
if (interactive && !mScreenshotChordPowerKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
mScreenshotChordPowerKeyTriggered = true;
mScreenshotChordPowerKeyTime = event.getDownTime();
interceptScreenshotChord();
}
// Stop ringing or end call if configured to do so when power is pressed.
TelecomManager telecomManager = getTelecommService();
boolean hungUp = false;
if (telecomManager != null) {
if (telecomManager.isRinging()) {
// Pressing Power while there's a ringing incoming
// call should silence the ringer.
telecomManager.silenceRinger();
} else if ((mIncallPowerBehavior
& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
&& telecomManager.isInCall() && interactive) {
// Otherwise, if "Power button ends call" is enabled,
// the Power button will hang up any current active call.
hungUp = telecomManager.endCall();
}
}
// If the power key has still not yet been handled, then detect short
// press, long press, or multi press and decide what to do.
mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
|| mScreenshotChordVolumeUpKeyTriggered;
if (!mPowerKeyHandled) {
if (interactive) {
// When interactive, we're already awake.
// Wait for a long press or for the button to be released to decide what to do.
if (hasLongPressOnPowerBehavior()) {
Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg,
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
}
} else {
wakeUpFromPowerKey(event.getDownTime());
if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg,
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
mBeganFromNonInteractive = true;
} else {
final int maxCount = getMaxMultiPressPowerCount();
if (maxCount <= 1) {
mPowerKeyHandled = true;
} else {
mBeganFromNonInteractive = true;
}
}
}
}
}
這裡我們重點看一下if(interactive)分支,在這裡我們發送一個一個異步消息,並且msg的what為MSG_POWER_LONG_PRESS,即長按電源事件的異步消息,所以我們看一下mHandler的handleMessage方法對該what消息的處理邏輯。
case MSG_POWER_LONG_PRESS:
powerLongPress();
break;
我們可以發現在mHandler的handleMessage方法中當msg的what為MSG_POWER_LONG_PRESS時我們調用了powerLongPress方法,這個方法應該就是處理電源按鍵長按的邏輯,下面我們來看一下powerLongPress方法的實現。
private void powerLongPress() {
final int behavior = getResolvedLongPressOnPowerBehavior();
switch (behavior) {
case LONG_PRESS_POWER_NOTHING:
break;
case LONG_PRESS_POWER_GLOBAL_ACTIONS:
mPowerKeyHandled = true;
if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
performAuditoryFeedbackForAccessibilityIfNeed();
}
showGlobalActionsInternal();
break;
case LONG_PRESS_POWER_SHUT_OFF:
case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
mPowerKeyHandled = true;
performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
break;
}
}
可以發現這裡有四個switch分之,其中第一個什麼都不做直接break掉,第二個case則需要彈出選擇操作界面,比如:飛行模式,開關機,靜音模式,重新啟動等,這裡可以參看一下小米手機的關機界面:
然後第三第四個case分之則是直接調用關機方法,這裡我們先看第二個case,看看系統是如何顯示出關機操作界面的。那我們看一下showGlobalActionsInternal方法的實現邏輯。
void showGlobalActionsInternal() {
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
if (mGlobalActions == null) {
mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
}
final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
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.
mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
}
}
可以發現我們首先調用了sendCloseSystemWindows方法,前面我們分析HOME按鍵流程的時候知道該方法用於關機系統彈窗,比如輸入法,壁紙等。然後我們創建了一個GlobalActions對象,並調用了其showDialog方法,通過分析源碼,我們發現該方法就是用於顯示長按電源按鍵彈出操作界面的,我們首先看一下GlobalActions的構造方法:
public GlobalActions(Context context, WindowManagerFuncs windowManagerFuncs) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mDreamManager = IDreamManager.Stub.asInterface(
ServiceManager.getService(DreamService.DREAM_SERVICE));
// receive broadcasts
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
context.registerReceiver(mBroadcastReceiver, filter);
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
// get notified of phone state changes
TelephonyManager telephonyManager =
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
mAirplaneModeObserver);
Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
mHasVibrator = vibrator != null && vibrator.hasVibrator();
mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useFixedVolume);
}
可以看到在GlobalActions對象的構造方法中我們主要用於初始化其成員變量,由於我們的電源長按操作界面是一個全局頁面,所以這裡自定義了一個Window對象,下面我們看一下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();
}
}
可以看到在showDialog方法中我們首先判斷mDialog是否為空,若為空則發送msg的what為MESSAGE_SHOW的異步消息,否則調用handleShow方法,而這裡的mDialog是一個類型為GlobalActionsDialog的變量,由於我們的mDialog為空,所以下面我們看一下handleShow方法。
private void handleShow() {
awakenIfNecessary();
mDialog = createDialog();
prepareDialog();
// If we only have 1 item and it's a simple press action, just do this action.
if (mAdapter.getCount() == 1
&& mAdapter.getItem(0) instanceof SinglePressAction
&& !(mAdapter.getItem(0) instanceof LongPressAction)) {
((SinglePressAction) mAdapter.getItem(0)).onPress();
} else {
WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
attrs.setTitle("GlobalActions");
mDialog.getWindow().setAttributes(attrs);
mDialog.show();
mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
}
在方法體中我們調用了createDialog方法,創建了GlobalActionsDialog類型的mDialog,這裡我們看一下createDialog的實現方法。
private GlobalActionsDialog createDialog() {
...
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);
}
}
@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();
mItems = new ArrayList();
String[] defaultActions = mContext.getResources().getStringArray(
com.android.internal.R.array.config_globalActionsList);
ArraySet addedKeys = new ArraySet();
for (int i = 0; i < defaultActions.length; i++) {
String actionKey = defaultActions[i];
if (addedKeys.contains(actionKey)) {
// If we already have added this, don't add it again.
continue;
}
if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
mItems.add(new PowerAction());
} else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
mItems.add(mAirplaneModeOn);
} else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
if (Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
mItems.add(getBugReportAction());
}
} else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
if (mShowSilentToggle) {
mItems.add(mSilentModeAction);
}
} else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
addUsersToMenu(mItems);
}
} else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
mItems.add(getSettingsAction());
} else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
mItems.add(getLockdownAction());
} else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
mItems.add(getVoiceAssistAction());
} else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
mItems.add(getAssistAction());
} else {
Log.e(TAG, "Invalid global action key " + actionKey);
}
// Add here so we don't add more than one.
addedKeys.add(actionKey);
}
mAdapter = new MyAdapter();
AlertParams params = new AlertParams(mContext);
params.mAdapter = mAdapter;
params.mOnClickListener = this;
params.mForceInverseBackground = true;
GlobalActionsDialog dialog = new GlobalActionsDialog(mContext, params);
dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
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) {
final Action action = mAdapter.getItem(position);
if (action instanceof LongPressAction) {
return ((LongPressAction) action).onLongPress();
}
return false;
}
});
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
dialog.setOnDismissListener(this);
return dialog;
}
方法體的內容比較長,我們看重點的內容,首先我們通過調用mContext.getResources().getStringArray(com.android.internal.R.array.config_globalActionsList)獲得操作列表,這裡可能包含:飛行模式、開關機、靜音模式、重啟等等,然後我們輪訓操作列表,並添加相應的Action最後我們將這個操作列表保存到Dialog的adapter中並返回該dialog,然後我們回到我們剛剛的handleShow方法,在得到返回的dialog之後我們調用了dialog的show方法,這樣我們就顯示出了電源長按操作界面,比如小米的界面:
好吧,繼續我們的分析,當我們長按電源按鍵彈出操作彈窗之後,這時候點擊關機是怎麼樣的流程呢?我們發現在createDialog方法中關機操作adapter的item,我們添加了:
mItems.add(new PowerAction());
這樣不難發現我們對關機按鈕的操作封裝在了PowerAction中,所以我們繼續看一下PowerAction的實現。
private final class PowerAction extends SinglePressAction implements LongPressAction {
private PowerAction() {
super(com.android.internal.R.drawable.ic_lock_power_off,
R.string.global_action_power_off);
}
@Override
public boolean onLongPress() {
UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
mWindowManagerFuncs.rebootSafeMode(true);
return true;
}
return false;
}
@Override
public boolean showDuringKeyguard() {
return true;
}
@Override
public boolean showBeforeProvisioning() {
return true;
}
@Override
public void onPress() {
// shutdown by making sure radio and power are handled accordingly.
mWindowManagerFuncs.shutdown(false /* confirm */);
}
}
可以發現在PowerAction類的成員函數onPress方法中我們調用了mWindowManagerFuncs.showdown方法,而這個方法也就是開始執行我們的關機操作了,那麼這裡的mWindowManagerFuncs又是什麼呢?它是在什麼時候賦值的呢?通過分析我們發現這裡的mWindowManagerFuncs成員變量是在GlobalActions的構造方法中賦值的。
public GlobalActions(Context context, WindowManagerFuncs windowManagerFuncs) {
...
mWindowManagerFuncs = windowManagerFuncs;
...
}
好吧,回到我們的PhoneWindowManager,早構造GlobalActions時,直接傳遞的是PhoneWindowManager的成員變量mWindowManagerFuncs,那麼PhoneWindowManager的mWindowManagerFuncs成員變量又是何時被賦值的呢?通過分析源碼我們能夠看到PhoneWindowManager的mWindowManagerFuncs變量是在PhoneWindowManager的init方法中初始化的,好吧,再次查找PhoneWindowManager的init方法是何時被調用的。
經過查找終於在WindowManagerService中我們找到了PhoneWindowManager的init方法的調用。
private void initPolicy() {
UiThread.getHandler().runWithScissors(new Runnable() {
@Override
public void run() {
WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
}
}, 0);
}
這裡的mPolicy就是一個PhoneWindowManager的實力,可以發現這裡的init方法中mWindowManagerFuncs傳遞的就是一個WindowManagerService的實例,O(∩_∩)O哈哈~,讓我們好找。
然麼在PowerAction的onPress方法中調用的mWindowManagerFuncs.shutdown(false /* confirm */);方法,實際上調用的就是WindowManagerService的shutdown方法,這樣我們繼續看一下WindowManagerService的shutdown方法的實現。
@Override
public void shutdown(boolean confirm) {
ShutdownThread.shutdown(mContext, confirm);
}
好吧,這裡很簡單就是直接調用了ShutdownThread的shutdown方法,看樣子這裡就是執行關機操作的封裝了,繼續看一下ShutdownThread的shutdown方法。
public static void shutdown(final Context context, boolean confirm) {
mReboot = false;
mRebootSafeMode = false;
shutdownInner(context, confirm);
}
可以看到在ShutdownThread的shutdown方法中代碼很簡單,具體的操作下發到了shutdownInner方法中,那麼我們繼續看一下shutdownInner方法的實現。
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);
if (sConfirmDialog != null) {
sConfirmDialog.dismiss();
}
sConfirmDialog = 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 = sConfirmDialog;
sConfirmDialog.setOnDismissListener(closer);
sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
sConfirmDialog.show();
} else {
beginShutdownSequence(context);
}
}
可以看到方法體中,首先判斷若用戶點擊了關機按鍵是否彈出確認框,若彈出則彈出關機確認框,若不需要確認,則直接調用beginShutdownSequence方法,執行關機操作。而在關機確認框中我們的確認按鈕也是執行了beginShutdownSequence方法,所以我們繼續看一下關機方法beginShutdownSequence。
private static void beginShutdownSequence(Context context) {
synchronized (sIsStartedGuard) {
if (sIsStarted) {
Log.d(TAG, "Shutdown sequence already running, returning.");
return;
}
sIsStarted = true;
}
...
if (PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) {
mRebootUpdate = new File(UNCRYPT_PACKAGE_FILE).exists();
if (mRebootUpdate) {
pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
pd.setMessage(context.getText(
com.android.internal.R.string.reboot_to_update_prepare));
pd.setMax(100);
pd.setProgressNumberFormat(null);
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd.setProgress(0);
pd.setIndeterminate(false);
} else {
// Factory reset path. Set the dialog message accordingly.
pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
pd.setMessage(context.getText(
com.android.internal.R.string.reboot_to_reset_message));
pd.setIndeterminate(true);
}
} else {
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();
sInstance.mProgressDialog = pd;
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() {
};
sInstance.start();
}
在方法beginShutdownSequence中我們首先初始化了一個Process的dialog,該dialog用於顯示關機界面,然後我們調用了sInstance.start方法,再往下的方法中就是真正的shutdown方法的實現,同時也是native方法,我們這裡就不做過得解讀了。。。
總結:
電源按鍵是系統按鍵,所以對電源按鍵的處理邏輯也是在PhoneWindowManager的dispatchUnhandledKey方法中;
在PhoneWindowManager的dispatchUnhandleKey方法處理Power按鍵之後會首先顯示系統操作彈窗,一般包括但不限於:飛行模式,靜音模式,重新啟動,關機等;
當用戶點擊關機按鈕是調用的是WindowManagerService.shutdown方法,而內部調用的是ShutdownThread.shutdown方法;
本文主要介紹三級緩存的原理解析與實現方式。以前一直覺得三級緩存圖片加載是一個很難理解的東西,但是自己看了一下午再試著寫了一遍之後感覺還是只要沉下心思考還時很容易熟悉掌握的
本文實例講述了Android編程之ICS式下拉菜單PopupWindow實現方法。分享給大家供大家參考,具體如下:運行效果截圖如下:右邊這個就是下拉菜單啦,看見有的地方叫
一. 問題提出 在Android程序中,我們通常需要使用DatePicker來設置日期,TimePicker來設置時間。其基本步驟是: 1.先定義DatePicker和
在做商城的項目中,有這麼個需求,就是一個產品下有兩個價格,一個是市場價,一個是銷售價,這時要把市場價添加個刪除線;剛開始遇到這個時,在網上找了半天的資料,看到最多的就是用