如何安全退出已调用多个Activity的Application?
转载自:
如何安全退出已调用多个Activity的Application?
使用ActivityManager的forceStopPackage方法结束进程
对于单一Activity的应用来说,退出很简单,直接finish()即可。
当然,也可以用killProcess()和System.exit()这样的方法。
但是,对于多Activity的应用来说,在打开多个Activity后,如果想在最后打开的Activity直接退出,上边的方法都是没有用的,因为上边的方法都是结束一个Activity而已。
当然,网上也有人说可以。
就好像有人问,在应用里如何捕获Home键,有人就会说用keyCode比较KEYCODE_HOME即可,而事实上如果不修改framework,根本不可能做到这一点一样。
所以,最好还是自己亲自试一下。
那么,有没有办法直接退出整个应用呢?
现提供几个方法,供参考:
1、抛异常强制退出:
该方法通过抛异常,使程序Force Close。
验证可以,但是,需要解决的问题是,如何使程序结束掉,而不弹出Force Close的窗口。
2、记录打开的Activity:
每打开一个Activity,就记录下来。在需要退出时,关闭每一个Activity即可。
public class ActivityManagerApplication extends Application {
private static Map<String,Activity> destoryMap = new HashMap<>();
private ActivityManagerApplication() {
}
/**
* 添加到销毁队列
*
* @param activity 要销毁的activity
*/
public static void addDestoryActivity(Activity activity,String activityName) {
destoryMap.put(activityName,activity);
}
/**
*销毁指定Activity
*/
public static void destoryActivity(String activityName) {
Set<String> keySet=destoryMap.keySet();
for (String key:keySet){
destoryMap.get(key).finish();
}
}
}
3、发送特定广播:
在需要结束应用时,发送一个特定的广播,每个Activity收到广播后,关闭即可。
4、递归退出
在打开新的Activity时使用startActivityForResult,然后自己加标志,在onActivityResult中处理,递归关闭。
除了第一个,都是想办法把每一个Activity都结束掉,间接达到目的。
但是这样做同样不完美。你会发现,如果自己的应用程序对每一个Activity都设置了nosensor,在两个Activity结束的间隙,sensor可能有效了。
但至少,我们的目的达到了,而且没有影响用户使用。
为了编程方便,最好定义一个Activity基类,处理这些共通问题。
5、在2.1之前,可以使用ActivityManager的restartPackage方法。
它可以直接结束整个应用。在使用时需要权限Android.permission.RESTART_PACKAGES。
注意不要被它的名字迷惑。
可是,在2.2,这个方法失效了。
在2.2添加了一个新的方法,killBackgroundProcesses(),需要权限 android.permission.KILL_BACKGROUND_PROCESSES。
可惜的是,它和2.2的restartPackage一样,根本起不到应有的效果。
另外还有一个方法,就是系统自带的应用程序管理里,强制结束程序的方法,forceStopPackage()。
它需要权限android.permission.FORCE_STOP_PACKAGES。
并且需要添加android:sharedUserId="android.uid.system"属性
同样可惜的是,该方法是非公开的,他只能运行在系统进程,第三方程序无法调用。
因为需要在Android.mk中添加LOCAL_CERTIFICATE := platform。
而Android.mk是用于在Android源码下编译程序用的。
从以上可以看出,在2.2,没有办法直接结束一个应用,而只能用自己的办法间接办到。
使用forceStopPackage的方法如下:
做一个应用,需要强制关闭进程。
可以使用ActivityManager的killBackgroundProcesses方法,需要权限Android.permission.KILL_BACKGROUND_PROCESSES。但使用此方法杀死进程后,进程会重启。源码中解释如下:
Have the system immediately kill all background processes associated with the given package. This is the same as the kernel killing those processes to reclaim memory; the system will take care of restarting these processes in the future as needed.
为了强制关闭进程,希望使用ActivityManager的另外一个方法,forceStopPackage。源码中解释如下:
Have the system perform a force stop of everything associated with the given application package. All processes that share its uid will be killed, all services it has running stopped, all activities removed, etc. In addition, a {@link Intent#ACTION_PACKAGE_RESTARTED} broadcast will be sent, so that any of its registered alarms can be stopped, notifications removed, etc.
使用这个方法有两点需要注意:
- 此方法是@hide的方法:
解决方案是使用java的反射机制完成调用,代码如下:
- ActivityManager mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
- Method method = Class.forName("android.app.ActivityManager").getMethod("forceStopPackage", String.class);
- method.invoke(mActivityManager, packageName); //packageName是需要强制停止的应用程序包名
- 此方法需要权限:android.permission.FORCE_STOP_PACKAGES
下面着手分析这个权限。
这个权限在frameworks/base/core/res/AndroidManifest.xml文件中声明,如下:
- <permission android:name="android.permission.FORCE_STOP_PACKAGES"
- android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="signature"
- android:label="@string/permlab_forceStopPackages"
- android:description="@string/permdesc_forceStopPackages"/>
注意protectionLevel属性值未signature。看sdk文档http://developer.android.com/guide/topics/manifest/permission-element.html#plevel中对这一属性的解释如下:
A permission that the system grants only if the requesting application is signed with the same certificate as the application that declared the permission. If the certificates match, the system automatically grants the permission without notifying the user or asking for the user's explicit approval.
意思是:app使用FORCE_STOP_PACKAGES权限,app必须和这个权限的声明者的签名保持一致!
FORCE_STOP_PACKAGES的声明者是frameworks/base/core/res/,可以在frameworks/base/core/res/Android.mk中看到它的签名信息:
- LOCAL_NO_STANDARD_LIBRARIES := true
- LOCAL_PACKAGE_NAME := framework-res
- LOCAL_CERTIFICATE := platform
即,签名为platform.
最终得到结论,app需要是platform签名,才可以使用forceStopPackage方法!
网上有很多文章提及,需要在app的AndroidManifest.xml中添加android:sharedUserId="android.uid.system"一句话。看sdk(http://developer.android.com/guide/topics/manifest/manifest-element.html)对此的解释:
android:sharedUserId
- The name of a Linux user ID that will be shared with other applications. By default, Android assigns each application its own unique user ID. However, if this attribute is set to the same value for two or more applications, they will all share the same ID — provided that they are also signed by the same certificate. Application with the same user ID can access each other's data and, if desired, run in the same process.
意思是,两个app使用了相同的user id,就可以互相访问对方的数据。因此,app使用android.uid.system的user id,就可以访问系统数据。注意背景为黄色的一句,这里依然需要两个app有相同的签名才行。