权限设立的目的是保护安全。
一.权限机制:
Android底层是基于Linux系统的,而Linux权限访问由进程和文件两个部分组成。
系统权限分为三种类型:
- Android所有者权限,相当于拥有Android Rom开发权限,可以获取所有的权限;
- Android Root权限,相当于取得Linux系统中的最高用户权限,可以任意对文件进行修改;
- Android应用程序权限,获取只能通过AndroidManifest中声明权限,然后由用户授权来获取。
protectionLevel权限水平分为:normal/dangerous/signature/signatureOrSystem:
normal:风险普通,安装时不会直接提示用户,点击全部才会展示;
dangerous:风险较高,任何应用都可以申请,安装时需要用户确认才能使用;
signature:仅当申请改权限的应用程序与声明改权限的应用程序位于相同的签名时,才能赋予权限;
signatureOrSystem:仅当申请权限的应用程序位于相同的Android系统镜像中,或者申请权限的应用程序和声明该权限的程序拥有相同签名时,才能赋予权限。
二.动态权限AppOps:
在原生的ROM中,AppOps权限设置在系统的SettingsApp中,路径为Settings->权限管理。在AppOps中可以看到安装的应用所要申请的权限列表,可以选择允许权限和禁止权限,还有使用时提示。
AppOps运行原理:
- 在SettingsUI中请求更改调整权限的变更;
- AppOpsManager提供访问接口;
- AppOpsService提供真正实现权限控制;
- app_ops.xml位于每个App的/data/system/中,存储各个App的权限设置和操作信息;
- 连接到其他不同的服务,告知其需要变更响应的设置。
从Android6.0(API23)开始便具有AppOps,这样可以让用户在安装时节省时间,而且可以更方便地控制应用的权限。用户可以按照对应用的需求控制应用的权限。
在Android O(API26)之前,如应用在运行时请求权限并且被授予该权限,系统会错误地将属于同一权限组在清单中注册的其他权限也一起授予该应用。在Android O之后的应用,则此行为被纠正。系统只会授予应用明确的请求权限,然而一旦用户为一应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准。
三.组件化权限:
我们可以将normal级别的权限申请都放到Base module中,然后在各个module中分别申请dangerous的权限。这样分配的好处在于当添加或移除单一权限时,隐私权限的申请也会跟随移除,能做到最大程序的权限解耦。
当项目需要适配到Android6.0以上的动态权限申请时,需要在Base module中添加自己封装的一套权限申请工具,其他组件层的module都可以使用这套工具。
四.动态权限框架:
AndPermission是一款封装性并使用的工具。
- 请求权限使用建造者模式来封装:
AndPermission.with(this)
.runtime()
.permission(Permission.Group.STORAGE)
.onGranted(permissions -> {
// Storage permission are allowed.
})
.onDenied(permissions -> {
// Storage permission are not allowed.
})
.start();
- 适配方案:
部分国内ROM的rational功能,第一次拒绝后,不会再返回true,并回调申请失效,因为第一次拒绝后默认勾选不再提示,建议使用settingDialog,提示用户在系统设置中授权。
如使用权限提示框选择权限,AppOpsManger中的并为同步。这里可以判断为运行时拥有权限或AppOpsManger。
四.路由拦截:
当调用其他模块的功能时,很可能独立跳转到该模块的一个单独页面。此时就是路由拦截就起作用了。将路由拦截和权限申请结合在一起,拦截跳转的同时进行权限申请的验证处理。当ARouter跳转前会遍历Intercept,然后通过判断跳转路径来来找到需要拦截的对象。
在Application的registerActivityLifecycleCallback方法可以当前最顶层Activity对象。
public class TopBaseApplication extends Application {
private static Activity topConext;
@RequiresApi(api = Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public void onCreate() {
super.onCreate();
this.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
//在创建时设置
topConext = activity;
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
//在Activity声明周期时设置Activity对象
topConext = activity;
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
}
//获取底层Activity对象
public static Activity getTopConext() {
return topConext;
}
}
在Application需要继承TopBaseApplication。其他功能模块都可以使用全局方法getTopContext获取到顶层Activity对象。
如一个ActivityA返回到它之前的ActivityB页面后,在生命周期中设置context全局对象,ActivityB调用destroy方法时,ActivityB对象还被全局content引用,将引起内存泄露。
ActivityB返回会先调用ActivityB onPause的生命周期,然后再调用Activity onstart和onResume生命周期,稍后Activity会调用onPause和onDestroy。
可以肯定的是,ActivityA的onResume肯定会比ActivityB的onDestroy提前完成,这意味着,在onResume已经设定了返回页面的Activity对象为顶层Activity时,ActivityB调用onDestroy,静态context不会强引用ActivityB对象,也不会造成内存泄露。