很多app都会将返回键模拟成home效果,代码如下:
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
IntentWrapper.onBackPressed(this);
moveTaskToBack(true);
}
return true;
}
return super.onKeyDown(keyCode, event);
}
这是之前写的代码,但是在bugtags统计上出现很多异常:
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.android.server.am.TaskRecord.setTaskToReturnTo(int)' on a null object reference at android.os.Parcel.readException(Parcel.java:1565)
at android.os.Parcel.readException(Parcel.java:1512)
at android.app.ActivityManagerProxy.moveActivityTaskToBack(ActivityManagerNative.java:3291)
at android.app.Activity.moveTaskToBack(Activity.java:5088)
at com.icourt.alpha.activity.MainActivity.onKeyDown(MainActivity.java)
at android.view.KeyEvent.dispatch(KeyEvent.java:2651)
at android.app.Activity.dispatchKeyEvent(Activity.java:2785)
at android.support.v7.app.AppCompatActivity.dispatchKeyEvent(AppCompatActivity.java)
at android.support.v7.view.WindowCallbackWrapper.dispatchKeyEvent(WindowCallbackWrapper.java)
at android.support.v7.app.AppCompatDelegateImplBase$AppCompatWindowCallbackBase.dispatchKeyEvent(AppCompatDelegateImplBase.java)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:2331)
at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:4230)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4184)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3724)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3787)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3743)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3870)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3751)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3927)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3724)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3787)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3743)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3751)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3724)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3787)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3743)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3903)
at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:4064)
at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:2218)
at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:1859)
at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:1850)
at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:2195)
at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:141)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:150)
at android.os.Looper.loop(Looper.java:139)
at android.app.ActivityThread.main(ActivityThread.java:5532)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:964)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:759)
在MainActivity.onKeyDown这个方法中出现的异常,看源码:
/**
* Move the task containing this activity to the back of the activity
* stack. The activity's order within the task is unchanged.
*
* @param nonRoot If false then this only works if the activity is the root
* of a task; if true it will work for any activity in
* a task.
*
* @return If the task was moved (or it was already at the
* back) true is returned, else false.
*/
public boolean moveTaskToBack(boolean nonRoot) {
try {
return ActivityManagerNative.getDefault().moveActivityTaskToBack(
mToken, nonRoot);
} catch (RemoteException e) {
// Empty
}
return false;
}
大概意思是说,nonRoot 为 false 时,当前activity必须为栈底,也就是最底层的activity,如果其他activity没有及时finish掉,就会出现异常,导致崩溃;nonRoot 为 true 时,不需要考虑当前activity是否在栈底。看完源码瞬间柳暗花明又一村,nice。
在这记录一下,出现的手机机型:android 5.1.1 (22),OPPO:OPPO R7sm:arm64-v8a 特别注意!!!
补充说明:
经过反复测试,如果设置为true,签名打包之后会失效:
在第一次安装后,activity管理栈似乎对进入的activity不做任何判断与标记(是否是栈顶的activity),每次都会入栈,在将栈清空后又回复正常(就是Task对activity做记录了,哪个处于栈顶,是否在栈里等);
1、moveTaskToBack(true);不判断当前activity是否在栈底
2、moveTaskToBack(false);先判断当前activity是否在栈底
再根据第一次安装,如果设为true,每次点击icon,都会重启app,如果设为false,则会找到栈内的activity,直接显示。
最终代码:
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
IntentWrapper.onBackPressed(this);
try {
moveTaskToBack(false);
} catch (Exception e) {
bugSync("返回键模拟HOME出错", e);
return super.onKeyDown(keyCode, event);
}
}
return super.onKeyDown(keyCode, event);
}
/**
* 防止华为机型未加入白名单时按返回键回到桌面再锁屏后几秒钟进程被杀
*/
public static void onBackPressed(Activity a) {
try {
Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
launcherIntent.addCategory(Intent.CATEGORY_HOME);
a.startActivity(launcherIntent);
} catch (Throwable e) {
e.printStackTrace();
}
}