Android6.0权限机制(三):6.0以前国产手机权限处理

本篇文章已授权微信公众号 hongyangAndroid (鸿洋)独家发布
Android6.0权限机制(一):介绍
Android6.0权限机制(二):封装
Android6.0权限机制(三):6.0以前国产手机权限处理

前言

Android是在6.0加入的权限机制,但是不少国产手机比如华为小米等,在6.0之前的设备已经在设置里面有权限开关。调皮的某个用户会关掉这个权限,然后去拍照什么的,直接崩掉了。之前大牛郭霖在csdn做了一期权限机制的教学直播,结束后我问了他这个问题,他的回答是简单try catch即可无需深究。那我想结合实际项目研究下这个问题。

Android权限机制简介

Android是在6.0之前只需要在menifest注册,用户安装app会有一个权限列表,类似一份协议表示用户对此已经知晓。但是实际中哪个用户会认真去看呢?导致很多app滥用权限给用户造成风险,于是在6.0后Android推出9组危险权限,要求开发者不仅要在menifest注册还要动态申请权限,比如调用拍照会弹出权限提示,只有用户自己点了确定才能继续拍照。

测试

了解了Android6.0权限机制后,我们用系统的权限API对国产手机测试一下。

测试设备:华为,系统版本4.4
测试内容:拍照,通话,录音
测试API:

  1. ContextCompat.checkSelfPermission:检查是否有某个权限
  2. requestPermission:主动申请某个权限,看看回调结果
    测试代码:
String[] permissions = new String[]{Manifest.permission.CALL_PHONE,Manifest.permission.CAMERA,Manifest.permission.RECORD_AUDIO};
                for(int i=0;i<permissions.length;i++){
                    String permission = permissions[i];
                    int check = ContextCompat.checkSelfPermission(MyActivity.this, permission);
                    if(check== PackageManager.PERMISSION_GRANTED){
                        Log.d("dml","权限通过");
                    }else{
                        Log.d("dml","无权限");
                    }
                    requestPermission(permissions, new OnPermissionCallback() {
                        @Override
                        public void onGranted() {
                            Log.d("dml","权限申请成功");
                        }
                        @Override
                        public void onDenied(List<String> deniedPermissions) {
                            Log.d("dml","权限被拒绝");
                        }
                    });
                }

先进入设置关闭这个app的电话,照相和录音权限

Paste_Image.png

运行app,发现并没有弹出让用户开关权限的Dialiog,并且检查权限返回权限通过。看看日志:

D/dml: android.permission.CALL_PHONE权限通过
D/dml: android.permission.CALL_PHONE权限申请成功
D/dml: android.permission.CAMERA权限通过
D/dml: android.permission.CAMERA权限申请成功
D/dml: android.permission.RECORD_AUDIO权限通过
D/dml: android.permission.RECORD_AUDIO权限申请成功

结论

检查权限和申请权限的api在华为4.4手机 完全失效,看下源码也不难得到印证:

Paste_Image.png

当运行设备在23以下也就是6.0以下的设备时,权限申请其实是通过PackageManager.checkPermission()来进行,看下这个方法:

Paste_Image.png

是个抽象方法,不过注释看出:只是判断你的apk也就是你的Manifest.xml有没有注册这个权限,有那么就返回true。

实际开发的问题

  1. 如果不去做检测,直接调用会如何呢?

经过测试以华为Android4.4的手机为例,关闭上述三项权限进行操作,发现系统都弹出了没有权限的Toast,不一样的是:
打电话:不能进入拨号界面,也没有闪退,没有异常输出
打开摄像头:直接闪退,有异常输出
录音:没有闪退,没有异常,录音开启了但是没有数据(如果我们直接用了这些空数据,极大可能崩溃)

厂商ROM只是给出了无权限提示,但我们要做的就是保证app不能崩溃!

Paste_Image.png

解决方案

  1. 在代码中我们针对Android6.0的权限检测(ContextCompat.checkSelfPermission和requestPermission)按照正常的写,保证在Android6.0以上的设备正常运行。
  2. 然后在具体的操作比如拨号,拍照或者录音,加一层tyr catch,能捕获到异常最好,不能捕获到的话继续第三步。
  3. 对具体机型我们加入if判断,对操作数据做合法性判断,比如录音生成的数据,下面以华为为例:

还是上面的代码,我们对三种操作都加上try catch:


public class MyActivity extends BaseActivity {
    private Button btn1,btn2,btn3;
    private Camera camera;
    private AudioRecord mRecorder;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity);
        btn1 = (Button) findViewById(R.id.button);
        btn2 = (Button) findViewById(R.id.button2);
        btn3 = (Button) findViewById(R.id.button3);
        btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try{
                    call();
                }catch (Exception e){
                    Log.e("dml","exception = " + e.getMessage());
                }
            }
        });
        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try{
                    openCamera();
                }catch (Exception e){
                    Log.e("dml","exception = " + e.getMessage());
                }
            }
        });
        btn3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try{
                    startRecord();
                }catch (Exception e){
                    Log.e("dml","exception = " + e.getMessage());
                }
            }
        });
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(mRecorder!=null){
            mRecorder.stop();
        }
    }
    private void call(){
        Intent intent = new Intent();
        intent.setData(Uri.parse("tel://1212121212"));
        intent.setAction(Intent.ACTION_CALL);
        startActivity(intent);
    }
    private void openCamera(){
        camera = Camera.open(0);
    }
    private void startRecord(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                int bufferSize = AudioRecord.getMinBufferSize(8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
                mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize * 2);
                mRecorder.startRecording();
                byte[] tempBuffer = new byte[bufferSize];
                while(true){
                    int recordCountSize = mRecorder.read(tempBuffer, 0, bufferSize);
                }
            }
        }).start();
    }
}

拍照

这次拍照没有闪退了,并且捕获到了异常,那直接在catch里面弹出Dialog就行了

E/dml: exception = Fail to connect to camera service

拨号

但是拨号失败却没有任何异常,如何处理呢?我们可以在设置一个布尔值标志位(isNewCal),监听到拨打电话的状态设为true,在拨出电话延迟500ms检测这个标志位,如果还是false说明拨打失败,然后排除移动网络异常,其他情况全部算作没有权限:


TelephonyManager tm = (TelephonyManager)getSystemService(Service.TELEPHONY_SERVICE);
        tm.listen(new PhoneStateListener(){
            @Override
            public void onCallStateChanged(int state, String incomingNumber) {
                // TODO Auto-generated method stub
                super.onCallStateChanged(state, incomingNumber);
                if(state==TelephonyManager.CALL_STATE_OFFHOOK){
                    isNewCall = true;
                }
            }
        }, PhoneStateListener.LISTEN_CALL_STATE);

第二种方法处理拨号权限问题,就是换一种Intent,不要直接拨号而是跳到系统拨号界面,让用户自己点击拨号。


private void jumpToDial(){
        Intent intent = new Intent(Intent.ACTION_DIAL,Uri.parse("tel:1234567"));
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }
Paste_Image.png

录音

录音代码加了try catch,但是关闭权限没有捕获任何异常(这里指华为,像三星手机startRecording会抛出异常),但是可以看出录出来的数据缓冲区全部为0:

Paste_Image.png

我们打开权限再看下:


Paste_Image.png

因此我们可以取录音缓冲区的部分数据,如果全部为零,那么当作没权限立即通知录音并给出Dialog提示,这里我取缓冲区的前20个byte:

    private void startRecord(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                int bufferSize = AudioRecord.getMinBufferSize(8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
                mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize * 2);
                mRecorder.startRecording();
                byte[] tempBuffer = new byte[bufferSize];
                byte[] checkBuffer = null;
                int valuePlus = 0;
                while(true){
                    int recordCountSize = mRecorder.read(tempBuffer, 0, bufferSize);
                    if(recordCountSize<=0){
                              if(mRecorder!=null){
                                mRecorder.stop();//这个和机型无关,recordCountSize<=0肯定是异常
                            }
                          }
                    //注意下面的代码要加上机型判断(根据你们测试的反馈),因为有的机型就算打开权限  这里前面几千个byte就是为0
                    if(checkBuffer==null){
                        checkBuffer = new byte[20];
                        System.arraycopy(tempBuffer,0,checkBuffer,0,20);
                        for(int value:checkBuffer){
                            valuePlus+=value;
                        }
                        if(valuePlus==0){
                            if(mRecorder!=null){
                                mRecorder.stop();
                            }
                            break;
                        }
                    }
                }
            }
        }).start();
    }

总结

try catch + 数据异常判断的思路,上面我主要针对华为手机做了部分权限的适配,不过在公司测试来看也兼容魅族三星,如果除了大部分主流机型还有个别不能兼容怎么办?我的建议是让这些调皮的用户自己玩去吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容