前言
之前调研了动态权限的申请方法,可是国内rom千奇百怪,真正实施起来的时候却不见得那么顺利,以前我们在Application里面承载了太多了业务逻辑,其中包含了需要READ_PHONE_STATE和WRITE_EXTERNAL_STORAGE权限的操作,所以这样的逻辑存在于Application里变得不合理,当然本来存放耗时逻辑在Application的onCreate里面就会影响APP启动速度,权限申请的方法在源码里面,存在于ActivityCompat里面的,所以Activity都没创建的时候是不可能完成权限申请的,所以权限动态申请的难点存在于对以往的逻辑进行搬家方面比较多,本文就传统App如何变成动态申请权限APP做一个探究。
方案
在我们APP里面目前在Activity尚未启动起来就需要权限的有两个地方:
权限 | 用途 |
---|---|
READ_PHONE_STATE | 友盟统计里面appsecurity需要deviceId、osName |
READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE | api请求缓存和插件下载都需要读写权限 |
目前其实还有地图定位的sdk需要定位权限,但是这个倒是可以往后放一放,但是插件化和统计需要的权限无法后移,必须一上来就有这两个权限,不然接口请求之类的都无法进行。就现在这个情况,有如下三种场景:
场景 | 周期方法 |
---|---|
正常启动 | Application onCreate ----> splashActivity —> 首页 |
尝试中偶尔有手机这样 | Application onCreate -----> 其他页面 |
权限手动回收 | 无固定页面 |
总结一下上面的三个情况,其实有一个共同特点,就是启动都经过Activity的生命周期方法。对上述三个场景做公共部分提取:onCreate—>BaseActivity(权限申请和必要初始化)—>相关页面,该方案还需要做到在BaseActivity页面承载一个顶层view,实现和Splash一样的展示效果,对于真正的splash页面我们不做改动,只需要继承BaseActivity页面即可。
方案第一步–敲击鉴权
对于正常的rom,直接调用google api来检测是否有权限,但是vivo和oppo就不行,所以对于这两个品牌单独取出来,做敲击鉴权,随代码一份,测试敲击时间根据权限不同在虚拟机上耗时1-20ms不等
package com.ymt360.app.mass.permissionUtil.utils;
import android.annotation.SuppressLint;
import android.content.Context;
import android.database.Cursor;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.location.LocationManager;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.ContactsContract;
import android.support.v4.content.PermissionChecker;
import android.telephony.TelephonyManager;
import android.Manifest;
import android.hardware.Camera;
import java.lang.reflect.Field;
import com.ymt360.app.mass.YMTApp;
import com.ymt360.app.mass.util.OSUtil;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import static android.content.Context.TELEPHONY_SERVICE;
/**
* 对所有危险权限分组做权限判断
* 所属权限组 权限名 权限等级 解释
* 日历 READ_CALENDAR 危险 允许应用程序读取用户的日历数据
* 日历 WRITE_CALENDAR 危险 允许应用程序写入用户的日历数据
* 相机 CAMERA 危险 使用摄像头做相关工作
* 联系人 READ_CONTACTS 危险 读取联系人
* 联系人 WRITE_CONTACTS 危险 写入联系人
* 联系人 GET_ACCOUNTS 危险 允许访问帐户服务中的帐户列表
* 位置 ACCESS_FINE_LOCATION 危险 允许应用访问精确位置
* 位置 ACCESS_COARSE_LOCATION 危险 允许应用访问大致位置
* 麦克风 RECORD_AUDIO 危险 麦克风的使用
* 电话 READ_PHONE_STATE 危险 允许对电话状态进行只读访问,包括设备的电话号码,当前蜂窝网络信息,任何正在进行的呼叫的状态以及设备上注册的任何PhoneAccounts列表
* 电话 CALL_PHONE 危险 允许应用程序在不通过拨号器用户界面的情况下发起电话呼叫,以便用户确认呼叫
* 电话 READ_CALL_LOG 危险 允许应用程序读取用户的通话记录
* 电话 WRITE_CALL_LOG 危险 允许应用程序写入(但不读取)用户的呼叫日志数据
* 电话 ADD_VOICEMAIL 危险 允许应用程序将语音邮件添加到系统中
* 电话 USE_SIP 危险 允许应用程序使用SIP服务
* 电话 PROCESS_OUTGOING_CALLS 危险 允许应用程序查看拨出呼叫期间拨打的号码,并选择将呼叫重定向到其他号码或完全中止呼叫
* 传感器 BODY_SENSORS 危险 允许应用程序访问来自传感器的数据
* 短信 SEND_SMS 危险 允许应用程序发送SMS消息
* 短信 RECEIVE_SMS 危险 允许应用程序接收SMS消息
* 短信 READ_SMS 危险 允许应用程序读取SMS消息
* 短信 RECEIVE_WAP_PUSH 危险 允许应用程序接收WAP推送消息
* 短信 RECEIVE_MMS 危险 允许应用程序监视传入的MMS消息(彩信)
* 存储 READ_EXTERNAL_STORAGE 危险 允许应用程序从外部存储读取
* 存储 WRITE_EXTERNAL_STORAGE 危险 允许应用程序写入外部存储
*
*
* 防止权限判断错误,使用尝试性敲击权限检查,对分组的权限做哥敲击就性
*
*
*/
public class YMTPermissionCheckUtil {
/**
* 权限查询对外公布方法
* @param permission
* @return
*/
public static boolean checkSelfPermission(String permission) {
// 对于非oppo和vivo手机,做正常的权限申请
if (permissionIsNormal()){
return selfPermissionGranted(permission);
}
// 对于国内部分机型做非正常权限申请
switch (permission){
case Manifest.permission.CAMERA:
return checkCameraPermissions();
case Manifest.permission.RECORD_AUDIO:
return checkAudioPermission();
case Manifest.permission.BODY_SENSORS:
return checkSensorsPermission();
case Manifest.permission.READ_CALENDAR: