欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2022-05-14 08:25:49
android6.0权限机制(三):6.0以前国产手机权限处理。 android是在6.0加入的权限机制,但是不少国产手机比如华为小米等,在6.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:

contextcompat.checkselfpermission:检查是否有某个权限 requestpermission:主动申请某个权限,看看回调结果
测试代码:
string[] permissions = new string[]{manifest.permission.call_phone,manifest.permission.camera,manifest.permission.record_audio};
                for(int i=0;i deniedpermissions) {
                            log.d("dml","权限被拒绝");
                        }
                    });
                }
;i++){>

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

运行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手机 完全失效,看下也不难得到印证:

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

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

实际开发的问题

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

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

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

解决方案

在代码中我们针对android6.0的权限检测(contextcompat.checkselfpermission和requestpermission)按照正常的写,保证在android6.0以上的设备正常运行。 然后在具体的操作比如拨号,拍照或者录音,加一层tyr catch,能捕获到异常最好,不能捕获到的话继续第三步。 对具体机型我们加入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);
    }

录音

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

我们打开权限再看下:


因此我们可以取录音缓冲区的部分数据,如果全部为零,那么当作没权限立即通知录音并给出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 + 数据异常判断的思路,上面我主要针对华为手机做了部分权限的适配,不过在公司测试来看也兼容魅族三星,如果除了大部分主流机型还有个别不能兼容怎么办?我的建议是让这些调皮的用户自己玩去吧。