1. 通知权限

NotificationChannel是android8.0新增的特性,如果App的targetSDKVersion>=26,没有设置channel通知渠道的话,就会导致通知无法展示。

Android O 引入了 通知渠道(Notification Channels),以提供统一的系统来帮助用户管理通知,如果是针对 android O 为目标平台时,必须实现一个或者多个通知渠道,以向用户显示通知。比如聊天软件,为每个聊天组设置一个通知渠道,指定特定声音、灯光等配置。

因此 Android 8.0(包含)以上系统版本和 Android 8.0 以下系统版本的弹出通知是不同的,代码如下:

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {        //Build.VERSION_CODES.O 等于 Android 8.0(对应 API 26)    BluetoothDefine.DEBUG_PRINTLN("--->In if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)");    NotificationManager notifymanager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);    Intent notifyintent = new Intent(this, NotificationActivity.class);    PendingIntent notifypendingintent = PendingIntent.getActivity(this, 0, notifyintent, PendingIntent.FLAG_UPDATE_CURRENT);    NotificationChannel channel = new NotificationChannel(channel_id, channel_name, NotificationManager.IMPORTANCE_DEFAULT);    channel.enableLights(false);    if(notifymanager != null) {        notifymanager.createNotificationChannel(channel);        Notification.Builder builder = new Notification.Builder(this, channel_id)            .setSmallIcon(R.mipmap.ic_launcher)            .setTicker(getResources().getString(R.string.app_name))            .setAutoCancel(true)            .setContentTitle(getResources().getString(R.string.app_name))            .setContentText(contenttext)            .setContentIntent(notifypendingintent);        Notification notification = builder.build();        notifymanager.notify(notification_id, notification);    }} else {                                //Build.VERSION_CODES.O 以下系统版本的弹出通知方式    NotificationManager notifymanager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);    Intent notifyintent = new Intent(this, NotificationActivity.class);    PendingIntent notifypendingintent = PendingIntent.getActivity(this, 0, notifyintent, PendingIntent.FLAG_UPDATE_CURRENT);    NotificationCompat.Builder builder = new NotificationCompat.Builder(this, null)        .setSmallIcon(R.mipmap.ic_launcher)        .setTicker(getResources().getString(R.string.app_name))        .setAutoCancel(true)        .setContentTitle(getResources().getString(R.string.app_name))        .setContentText(contenttext)        .setContentIntent(notifypendingintent);    Notification notification = builder.build();    notifymanager.notify(notification_id, notification);}

2. 悬浮窗权限

在其他应用和系统窗口上方显示提醒窗口(悬浮窗)的差异(注:这只是显示悬浮窗时设置的差异,与获取悬浮窗权限不同,在不同系统版本和不同品牌手机中差别比较大,较复杂)

这些行为变更专门应用于针对 8.O 平台或更高平台版本的应用。针对 Android8.0 或更高平台版本进行编译,或将 targetSdkVersion设为 Android 8.0 或更高版本的应用开发者必须修改其应用以正确支持这些行为(如果适用)。

提醒窗口

使用SYSTEM_ALERT_WINDOW 权限的应用无法再使用以下窗口类型来在其他应用和系统窗口上方显示提醒窗口:

•TYPE_PHONE

•TYPE_PRIORITY_PHONE

•TYPE_SYSTEM_ALERT

•TYPE_SYSTEM_OVERLAY

•TYPE_SYSTEM_ERROR

相反,应用必须使用名为 TYPE_APPLICATION_OVERLAY 的新窗口类型。

使用TYPE_APPLICATION_OVERLAY 窗口类型显示应用的提醒窗口时,请记住新窗口类型的以下特性:

•应用的提醒窗口始终显示在状态栏和输入法等关键系统窗口的下面。

•系统可以移动使用 TYPE_APPLICATION_OVERLAY窗口类型的窗口或调整其大小,以改善屏幕显示效果。

•通过打开通知栏,用户可以访问设置来阻止应用显示使用 TYPE_APPLICATION_OVERLAY 窗口类型显示的提醒窗口。

因此 Android 8.0(包含)以上系统版本和 Android 8.0 以下系统版本的弹出悬浮窗是不同的,代码如下:

WindowManager windowmanager = getWindowManager(context);int screenwidth = windowmanager.getDefaultDisplay().getWidth();int screenHeight = windowmanager.getDefaultDisplay().getHeight();if(suspendwindow == null) {    suspendwindow = new SuspendWindowView(context);    if(suspendwindowparams == null) {        suspendwindowparams = new WindowManager.LayoutParams();        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {        //Android 8.0(包含)以上 必须使用 TYPE_APPLICATION_OVERLAY 窗口类型,            suspendwindowparams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;        } else {                                 //Android 8.0 以下,可以使用之前的窗口类型            suspendwindowparams.type = WindowManager.LayoutParams.TYPE_PHONE;//               suspendwindowparams.type = WindowManager.LayoutParams.TYPE_TOAST;        }        suspendwindowparams.format = PixelFormat.RGBA_8888;        suspendwindowparams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;        suspendwindowparams.gravity = Gravity.LEFT | Gravity.TOP;        suspendwindowparams.width = SuspendWindowView.viewWidth;        suspendwindowparams.height = SuspendWindowView.viewHeight;        suspendwindowparams.x = screenwidth;        suspendwindowparams.y = screenHeight / 20;    }    suspendwindow.setParams(suspendwindowparams);    suspendwindow.setBluetootnParams(status, brightness, colortemp);    windowmanager.addView(suspendwindow, suspendwindowparams);}

3. 录音权限

Android 6.0 以下系统版本,要开启录音权限,只需要在 AndroidManifest.xml 中增加以下权限声明即可:

但 Android 6.0(包含)以上系统版本,即使声明了上述权限,仍然没有开启录音权限,需要在代码中动态的判断权限是否打开,若没有打开,需要动态的请求录音权限开启窗口让使用者选择允许或禁止录音权限;

相关代码如下:

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {        //Build.VERSION_CODES.M 等于 Android 6.0(对应 API 23)    if (ContextCompat.checkSelfPermission(DeviceIPCameraPlayerActivity.this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {    //判断录音权限是否已开启,若未开启则弹出请求窗口        DeviceIPCameraPlayerActivity.this.requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, MainDefine.ActivityRequest.LOCAL_RECORD_AUDIO_REQUEST_CODE);    } else {                            //若录音权限已开始,则直接打开录音功能        istalk = true;        m_arrObjCam[0].startTalk();    }} else {                                //Android 6.0 以下系统在 AndroidManifest.xml 中声明权限后可直接打开录音功能    istalk = true;    m_arrObjCam[0].startTalk();}

4. Android 6.0 以上系统通用的权限的动态申请方式

权限配置类:

public class PermisionUtils {    private static String[] PERMISSIONS_STORAGE = {        Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE,        Manifest.permission.ACCESS_COARSE_LOCATION};    public static void verifyStoragePermissions(Activity activity, int request_code) {        int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);        if(permission != PackageManager.PERMISSION_GRANTED) {            ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE, request_code);        }    }}

在需要的地方调用方法:

if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {    // checkPermission();    PermisionUtils.verifyStoragePermissions(this, BluetoothDefine.ActivityRequest.BLUETOOTH_PERMISSION_REQUEST);}

对请求权限的返回进行处理:

@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {    super.onRequestPermissionsResult(requestCode, permissions, grantResults);    switch(requestCode) {        case BluetoothDefine.ActivityRequest.BLUETOOTH_PERMISSION_REQUEST:            if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {                BluetoothDefine.DEBUG_PRINTLN("->ACCESS_COARSE_LOCATION PERMISSION GRANTED");            } else {                BluetoothDefine.DEBUG_PRINTLN("->ACCESS_COARSE_LOCATION PERMISSION NOT GRANTED");            }            break;        default:            break;    }}