Receiver里面启动dialog引发的一系列错误解决(SYSTEM_ALERT_WINDOW)

来源:互联网 发布:php空间试用 编辑:程序博客网 时间:2024/05/22 07:54

首先,在receiver中show出dialog的做法:

@Overridepublic void onReceive(final Context context, Intent intent) {    if (GlobalConstantHolder.sTimer != null) {        GlobalConstantHolder.sTimer.cancel();        GlobalConstantHolder.sTimer = null;    }    //对话框    AlertDialog.Builder builder = new AlertDialog.Builder(TheApplication.getContext());    builder.setTitle("强制下线通知");    //正文    builder.setMessage("您的账号已在别地登录,如不是本人操作,请尽快修改密码");    //不可取消    builder.setCancelable(false);    //按钮    builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {        @Override        public void onClick(DialogInterface dialog, int which) {            //销毁所有活动            GlobalConstantHolder.finishAll();            //启动登陆活动            Intent intent = new Intent(TheApplication.getContext(),LoginActivity.class);            //在广播中启动活动,需要添加如下代码            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);            context.startActivity(intent);        }    });    AlertDialog alterDialog = builder.create();    //添加对话框类型:保证在广播中正常弹出    alterDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);    //对话框展示    alterDialog.show();}

其中所有的TheApplication.getContext()方法原来我是写成context的,就是传过来的context,但是有一些手机不行,会崩溃,所以我就换成了TheApplication.getContext(),他是全局的context(Application级别的)。

本来以为这就解决了,然而啊然而,居然在6.0及以上的手机中又蹦出了权限问题,检查AndroidManifest文件中的权限,发现漏掉了

<!--不加这个receiver弹出AlertDialog会出错.而且6.0以上要判断是否授权--><uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
这个权限。

加上之后,发现还是不行,查阅资料知,原来6.0之后,权限的配置没有那么简单了,原来6.0以上的权限分成了3个部分:normal permission、dangerous permission和special permission,其中前两种随便百度都能找到解释,第三种请看这位大神的链接点击打开链接。

第一种的权限只需要在AndroidManifest配置即可;

第二种则不仅仅需要在manifest中配置,而且还需要在java代码中设置:

首先

//先判断android版本(大于6.0执行)int SDK_VERSION = android.os.Build.VERSION.SDK_INT;if (SDK_VERSION >= 23) {    //检测权限授权(针对6.0以上先安装后检查权限的情况)    List<String> permissionsList = new ArrayList<>();    String[] permissions = null;    if (ActivityCompat.checkSelfPermission(this,android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {        permissionsList.add(android.Manifest.permission.WRITE_EXTERNAL_STORAGE);    }    if(ActivityCompat.checkSelfPermission(this,android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED){        permissionsList.add(android.Manifest.permission.CAMERA) ;    }    if (permissionsList.size()!=0)    {        permissions = new String[permissionsList.size()];        for (int i = 0; i < permissionsList.size(); i++) {            permissions[i] = permissionsList.get(i);        }        //此句调起权限授权框        ActivityCompat.requestPermissions(this, permissions, MY_PERMISSIONS_REQUEST_CODE);    }
然后在要使用的Activity中重写

@Overridepublic void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {    if (requestCode == MY_PERMISSIONS_REQUEST_CODE)    {        for (int i = 0; i < grantResults.length; i++) {            if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {                // Permission Denied                //                Toast.makeText(MainActivity.this, "Permission Denied", Toast.LENGTH_SHORT).show();                //若没有通过授权就直接退出程序,要不然后面需要权限的时候会崩                GlobalConstantHolder.finishAll();                finish();                break;            }        }        return;    }    super.onRequestPermissionsResult(requestCode, permissions, grantResults);}
我在这做的是,如果有一个我需要的权限没授权就退出程序不让用,防止之后因权限导致的问题出现。

第三种还不一样,不一样在于在代码中设置的规则不一样:

// 判断是否有SYSTEM_ALERT_WINDOW权限(特殊!既不属于normal permission,也不属于dangerous permission,属于special permission)if(!Settings.canDrawOverlays(this)) {    // 申请SYSTEM_ALERT_WINDOW权限    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,            Uri.parse("package:" + getPackageName()));    // REQUEST_CODE2是本次申请的请求码    startActivityForResult(intent, SYSTEM_ALERT_WINDOW_REQUEST_CODE2);} else {    //Do some thing else here.}

先是判断是否有SYSTEM_ALERT_WINDOW权限的方法是Settings.canDrawOverlays(context),为true为已授权,反之亦然。然后调起授权的方式不一样,它会以action的形式启动一个Activity来设置权限授权,SYSTEM_ALERT_WINDOW_REQUEST_CODE2是我定义的请求码,因为授权完成要在onActivityForResult中处理,根据这个请求码来判断:

@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {    switch (requestCode) {        case SYSTEM_ALERT_WINDOW_REQUEST_CODE2:            // 判断是否有SYSTEM_ALERT_WINDOW权限            if (Settings.canDrawOverlays(this)) {            }else{                GlobalConstantHolder.finishAll();            }            break;    }}
同样我做的是如果未授权就退出程序。

需要注意的是,这种类型的权限,不一定会使程序崩溃,像CAMERA和WRITE_EXTERNAL_STORAGE等权限,如果只在manifest中配置了而没有在代码中设置,则可能会出现拍的照存不上或者相册里的取不出来等问题。

这个第三种还有一个权限:WRITE_SETTINGS。不过我现在并不知道他是干什么的,等到以后用时再说。

最后,附上各种权限的分类:(以下内容出自点击打开链接)

Normal Permissions如下:

ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS

Dangerous Permissions:

group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS

group:android.permission-group.PHONE
permission:android.permission.READ_CALL_LOG
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.USE_SIP
permission:android.permission.PROCESS_OUTGOING_CALLS
permission:com.android.voicemail.permission.ADD_VOICEMAIL

group:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDAR
permission:android.permission.WRITE_CALENDAR

group:android.permission-group.CAMERA
permission:android.permission.CAMERA

group:android.permission-group.SENSORS
permission:android.permission.BODY_SENSORS

group:android.permission-group.LOCATION
permission:android.permission.ACCESS_FINE_LOCATION
permission:android.permission.ACCESS_COARSE_LOCATION

group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE

group:android.permission-group.MICROPHONE
permission:android.permission.RECORD_AUDIO

group:android.permission-group.SMS
permission:android.permission.READ_SMS
permission:android.permission.RECEIVE_WAP_PUSH
permission:android.permission.RECEIVE_MMS
permission:android.permission.RECEIVE_SMS
permission:android.permission.SEND_SMS
permission:android.permission.READ_CELL_BROADCASTS

补充优化:

当你第一次拒绝后再次启动程序就会有“拒绝后不再提醒”的选项,选了之后会不再提醒,根据以上的代码则不会退出程序,所以就会有潜在危险存在。怎么办呢?

先看我在SYSTEM_ALERT_WINDOW权限返回方法里加入:

//如果没有对SYSTEM_ALERT_WINDOW权限授权,则弹出框提示new AlertDialog.Builder(this).setCancelable(false)        .setTitle("警告").setMessage("您还没有对悬浮窗权限授权,将无法继续使用该程序!\n\n是否去开启悬浮窗权限?")        .setPositiveButton("", new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {                //以下代码可以调到该应用相关信息框,然后那个页点击权限管理就可以设置了                Intent localIntent = new Intent();                localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                if (Build.VERSION.SDK_INT >= 9) {                    localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");                    localIntent.setData(Uri.fromParts("package", getPackageName(), null));                } else if (Build.VERSION.SDK_INT <= 8) {                    localIntent.setAction(Intent.ACTION_VIEW);                    localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");                    localIntent.putExtra("com.android.settings.ApplicationPkgName", getPackageName());                }                startActivity(localIntent);                //若没有通过授权就直接退出程序,要不然后面需要权限的时候会崩                GlobalConstantHolder.finishAll();            }        }).setNegativeButton("", new DialogInterface.OnClickListener() {    @Override    public void onClick(DialogInterface dialog, int which) {        //若没有通过授权就直接退出程序,要不然后面需要权限的时候会崩        GlobalConstantHolder.finishAll();    }}).show();
如果未授权,则弹出框提示他,点击“是”跳到权限设置相关界面让他选择,之所以“是”和“否”都关闭程序是为了防止他点击了“是”,但是并没有实际选择打开权限,所以都要关闭,权限设置了之后他自己再去打开app。

同样的,看dangerous permission的返回方法:

for (int i = 0; i < grantResults.length; i++) {    if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {        // Permission Denied        //                Toast.makeText(MainActivity.this, "Permission Denied", Toast.LENGTH_SHORT).show();        new AlertDialog.Builder(this).setCancelable(false)                .setTitle("警告").setMessage("您有必要的权限还没有授权,将无法继续使用该程序!\n\n是否去开启权限?")                .setPositiveButton("", new DialogInterface.OnClickListener() {                    @Override                    public void onClick(DialogInterface dialog, int which) {                        //以下代码可以调到该应用相关信息框,然后那个页点击权限管理就可以设置了                        Intent localIntent = new Intent();                        localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                        if (Build.VERSION.SDK_INT >= 9) {                            localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");                            localIntent.setData(Uri.fromParts("package", getPackageName(), null));                        } else if (Build.VERSION.SDK_INT <= 8) {                            localIntent.setAction(Intent.ACTION_VIEW);                            localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");                            localIntent.putExtra("com.android.settings.ApplicationPkgName", getPackageName());                        }                        startActivity(localIntent);                        //若没有通过授权就直接退出程序,要不然后面需要权限的时候会崩                        GlobalConstantHolder.finishAll();                    }                }).setNegativeButton("", new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {                //若没有通过授权就直接退出程序,要不然后面需要权限的时候会崩                GlobalConstantHolder.finishAll();            }        }).show();        break;    }}
如果有拒绝的,就会弹框,哪怕你选择“拒绝之后不再提示”,他下次打开时还会回调这个方法,只不过
//此句调起权限授权框ActivityCompat.requestPermissions(this, permissions, MY_PERMISSIONS_REQUEST_CODE);

这一句没有调起权限授权框而已,这么做的原因同上。

还有一点,看过一个资料,说是fragment中动态设置权限调用requestPermissions(permissions, MY_PERMISSIONS_REQUEST_CODE);这个方法才行,并且他依赖的activity的最终父类必须是AppCompatActivity,不知道真假,先记着。



0 0