Android6.0~9.0適配

1.前言

  你們還記得Android 6.0權限適配的淚水嗎?而如今谷歌已經出了Android P的穩定版,並且谷歌粑粑,爲了你們能給辛苦熬夜加班,特意的和個大市場合做,要強制推出9.0的適配,而近期在下不才,爲了報着多踩坑的心態,作了一下7.0~9.0的適配,臉頰也是老淚兩行java

2.安卓6.0的適配

2.1 怎麼適配android

● 在6.0全部權限都須要申請?git

曰:固然不是。只有屬於危險權限的才須要申請。危險權限看下錶1-2github

● 那危險權限也不少啊,也要一個個申請?apache

曰:固然不是。你看看下面的表,都分好組了(9組),對於同一組內的權限,只要有一個被贊成,其餘的都會被贊成。編程

● 誰最帥canvas

曰:固然是子信。安全

2.2 列舉權限的分組bash

表1-2危險權限分組網絡

分組 名字 分割線
PHONE android.permission.READ_PHONE_STATE
android.permission.CALL_PHONE
android.permission.READ_CALL_LOG
android.permission.ADD_VOICEMAIL
android.permission.WRITE_CALL_LOG
android.permission.USE_SIP
android.permission.PROCESS_OUTGOING_CALLS
CALENDAR android.permission.READ_CALENDAR
android.permission.WRITE_CALENDAR
CAMERA android.permission.CAMERA
CONTACTS android.permission.READ_CONTACTS
android.permission.WRITE_CONTACTS
android.permission.GET_ACCOUNTS
LOCATION android.permission.ACCESS_FINE_LOCATION
android.permission.ACCESS_COARSE_LOCATION
MICROPHONE android.permission.RECORD_AUDIO
SENSORS android.permission.BODY_SENSORS
SMS android.permission.SEND_SMS
android.permission.RECEIVE_SMS
android.permission.READ_SMS
android.permission.RECEIVE_WAP_PUSH
android.permission.RECEIVE_MMS
STORAGE android.permission.READ_EXTERNAL_STORAGE
android.permission.WRITE_EXTERNAL_STORAGE
<!-- 危險權限 start -->
<!--PHONE-->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
<uses-permission android:name="android.permission.ADD_VOICEMAIL"/>
<uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
<uses-permission android:name="android.permission.USE_SIP"/>
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<!--CALENDAR-->
<uses-permission android:name="android.permission.READ_CALENDAR"/>
<uses-permission android:name="android.permission.WRITE_CALENDAR"/>
<!--CAMERA-->
<uses-permission android:name="android.permission.CAMERA"/>
<!--CONTACTS-->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<!--LOCATION-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<!--MICROPHONE-->
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<!--SENSORS-->
<uses-permission android:name="android.permission.BODY_SENSORS"/>
<!--SMS-->
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_WAP_PUSH"/>
<uses-permission android:name="android.permission.RECEIVE_MMS"/>
<!--STORAGE-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- 危險權限 Permissions end -->
複製代碼

  以上是列出9組須要動態申請的權限,建議本身代碼統一封裝成一個工具類,這裏就不細說了, Android6.0權限工具

3.Android 7.0的適配

3.1 應用間共享文件

  在targetSdkVersion大於等於的24的App中,可是咱們沒有去適配7.0。那麼在調用安裝頁面,或修改用戶頭像操做時,就會失敗。那麼就須要你去適配7.0或是將targetSdkVersion改成24如下(不推薦)。適配的方法這裏就不細講,你們能夠看鴻洋大神的 Android 7.0 行爲變動 經過FileProvider在應用間共享文件這篇文章

3.2 APK signature scheme v2

Android 7.0 引入一項新的應用簽名方案 APK Signature Scheme v2,它能提供更快的應用安裝時間和更多針對未受權 APK 文件更改的保護。在默認狀況下,Android Studio 2.2 和 Android Plugin for Gradle 2.2 會使用 APK Signature Scheme v2 和傳統簽名方案來簽署您的應用。

簽名版.png

1)只勾選v1簽名就是傳統方案簽署,可是在7.0上不會使用V2安全的驗證方式。

2)只勾選V2簽名7.0如下會顯示未安裝,7.0上則會使用了V2安全的驗證方式。

3)同時勾選V1和V2則全部版本都沒問題。

3.3 org.apache不支持問題

// build.gradle裏面加上這句話
defaultConfig {
        useLibrary 'org.apache.http.legacy'
    }
複製代碼

3.3 SharedPreferences閃退

SharedPreferences read = getSharedPreferences(RELEASE_POOL_DATA, MODE_WORLD_READABLE);
//MODE_WORLD_READABLE :7.0之後不能使用這個獲取,會閃退,修改爲MODE_PRIVATE
複製代碼

4.Android 8.0的適配

4.1 安卓8.0中PHONE權限組新增兩個權限

ANSWER_PHONE_CALLS:容許您的應用經過編程方式接聽呼入電話。要在您的應用中處理呼入電話,您可使用 acceptRingingCall() 函數。
READ_PHONE_NUMBERS :權限容許您的應用讀取設備中存儲的電話號碼。
複製代碼

4.2 通知適配

  安卓8.0中,爲了更好的管制通知的提醒,不想一些不重要的通知打擾用戶,新增了通知渠道,用戶能夠根據渠道來屏蔽一些不想要的通知

兼容的代碼

/**
 * 安卓8。0通知的兼容類哦,
 * NotifyCompatYc   yc : 是雨辰的簡寫,謝謝哦,嘿嘿 ----高貴的子信
 */
public class NotifyCompatYc {

    public static final String QFMD_CHANNEL_ID = "com.oms.mingdeng";
    public static final String QFMD_CHANNEL_NAME = "祈福明燈";
    public static final String LJMS_DEFAULT_CHANNEL_NAME = "靈機妙算";
    public static final String LJMS_CHANNEL_ID = "com.oms.mmcnotity";
    public static final String XYS_CHANNEL_ID = "com.oms.xuyuanshu";
    public static final String XYS_CHANNEL_NAME = "許願樹";

    public static void setONotifyChannel(NotificationManager manager, NotificationCompat.Builder builder, String channeId, String channelName) {
        if (TextUtils.isEmpty(channeId)||TextUtils.isEmpty(channelName)){
            L.e("NotifyCompatYc: ".concat("安卓8.0的通知兼容庫中 channeId 與 channelName 不能爲empty"));
        }
        if (Build.VERSION.SDK_INT >= 26) {
            //第三個參數設置通知的優先級別
            NotificationChannel channel =
                    new NotificationChannel(channeId, channelName, NotificationManager.IMPORTANCE_DEFAULT);
            channel.canBypassDnd();//是否能夠繞過請勿打擾模式
            channel.canShowBadge();//是否能夠顯示icon角標
            channel.enableLights(true);//是否顯示通知閃燈
            channel.enableVibration(true);//收到小時時震動提示
            channel.setBypassDnd(true);//設置繞過免打擾
            channel.setLockscreenVisibility(NotificationCompat.VISIBILITY_SECRET);
            channel.setLightColor(Color.RED);//設置閃光燈顏色
            channel.getAudioAttributes();//獲取設置鈴聲設置
            channel.setVibrationPattern(new long[]{100, 200, 100});//設置震動模式
            channel.shouldShowLights();//是否會閃光
            if (manager != null) {
                manager.createNotificationChannel(channel);
            }
            if (builder != null) {
                builder.setChannelId(channeId);//這個id參數要與上面channel構建的第一個參數對應
            }
        }
    }

    public static void setONotifyChannel(NotificationManager manager,  String channeId, String channelName) {
        setONotifyChannel(manager,null,channeId,channelName);
    }

    public static Notification getNotification(Context context, String channelId) {
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, channelId);
        Notification notification = notificationBuilder.setOngoing(true)
                .setSmallIcon(R.drawable.ic_launcher)
                .setPriority(NotificationManager.IMPORTANCE_MIN)
                .setCategory(Notification.CATEGORY_SERVICE)
                .build();
        return notification;
    }
}
複製代碼
public class NotifyManager {

    // 單例開始
    private volatile static NotifyManager INSTANCE;

    private NotifyManager(Context context) {
        initNotifyManager(context);
    }

    public static NotifyManager getInstance(Context context) {
        if (INSTANCE == null) {
            synchronized (NotifyManager.class) {
                if (INSTANCE == null) {
                    INSTANCE = new NotifyManager(context);
                }
            }
        }
        return INSTANCE;
    }
    // 單例結束

    private NotificationManager manager;
   // NotificationManagerCompat
    private NotificationCompat.Builder builder;

    //初始化通知欄配置
    private void initNotifyManager(Context context) {
        context = context.getApplicationContext();
        manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        // 若是存在則清除上一個消息
//        manager.cancel(news_flag);
        builder = new NotificationCompat.Builder(context,NotifyCompatYc.QFMD_CHANNEL_ID);

        NotifyCompatYc.setONotifyChannel(manager,builder,NotifyCompatYc.QFMD_CHANNEL_ID,NotifyCompatYc.QFMD_CHANNEL_NAME);

        // 設置標題
        builder.setContentTitle(context.getResources().getString(R.string.qfmd_notify_title1));
        // 狀態欄的動畫提醒語句
        builder.setTicker(context.getResources().getString(R.string.qfmd_notify_ticker));
        // 何時提醒的
        builder.setWhen(System.currentTimeMillis());
        // 設置通知欄的優先級
        builder.setPriority(Notification.PRIORITY_DEFAULT);
        // 設置點擊可消失
        builder.setAutoCancel(true);
        // 設置是否震動等
        builder.setDefaults(Notification.DEFAULT_VIBRATE);
        // 設置icon
        builder.setSmallIcon(R.drawable.lingji_icon);
        // 設置點擊意圖
        Intent intent = new Intent(context, GongdenggeActivity.class);
        Bundle bundle = new Bundle();
        bundle.putBoolean(Contants.INTENT_GOTO_MYLMAP, true);
        intent.putExtras(bundle);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 230, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        builder.setContentIntent(pendingIntent);
    }

    /**
     * 顯示祈福明燈過時通知
     */
    public void showQiFuLampOutOfDateNotify(Context context) {
        // 設置內容
        builder.setContentText(context.getResources().getString(R.string.qfmd_notify_content1));
        manager.notify(13251, builder.build());
    }

    public void showQiFuLampBlessNotify(Context context) {
        builder.setContentText(context.getResources().getString(R.string.qfmd_notify_content2));
        manager.notify(13255, builder.build());
    }
}
複製代碼

4.3 安裝APK

  首先在AndroidManifest文件中添加安裝未知來源應用的權限:

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
複製代碼

  這樣系統會自動詢問用戶完成受權。固然你也能夠先使用 canRequestPackageInstalls()查詢是否有此權限,若是沒有的話使用Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES這個action將用戶引導至安裝未知應用權限界面去受權。

private static final int REQUEST_CODE_UNKNOWN_APP = 100;

    private void installAPK(){

        if (Build.VERSION.SDK_INT >= 26) {
            boolean hasInstallPermission = getPackageManager().canRequestPackageInstalls();
            if (hasInstallPermission) {
                //安裝應用
            } else {
                //跳轉至「安裝未知應用」權限界面,引導用戶開啓權限
                Uri selfPackageUri = Uri.parse("package:" + this.getPackageName());
                Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, selfPackageUri);
                startActivityForResult(intent, REQUEST_CODE_UNKNOWN_APP);
            }
        }else {
            //安裝應用
        }

    }

    //接收「安裝未知應用」權限的開啓結果
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_UNKNOWN_APP) {
            installAPK();
        }
    }


複製代碼

4.4 SecurityException的閃退

  問題緣由:項目使用了ActiveAndroid,在 8.0 或 8.1 系統上使用 26 或以上的版本的 SDK 時,調用 ContentResolver 的 notifyChange 方法通知數據更新,或者調用 ContentResolver 的 registerContentObserver 方法監聽數據變化時,會出現上述異常。

解決方案:

(1)在清單文件配置

<provider
        android:name="com.activeandroid.content.ContentProvider"
        android:authorities="com.ylmf.androidclient"
        android:enabled="true"
        android:exported="false">
</provider>
複製代碼

(2)去掉這個監聽刷新的方法,改成廣播刷新

4.5 靜態廣播沒法正常接收

  問題緣由: Android 8.0 引入了新的廣播接收器限制,所以您應該移除全部爲隱式廣播 Intent 註冊的廣播接收器

解決方案: 使用動態廣播代替靜態廣播

4.6 Caused by: java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation

問題緣由: Android 8.0 非全屏透明頁面不容許設置方向(後面8.1系統谷歌就去掉了這個限制,可能不少人真的不習慣吧)

解決方案:
        (1)android:windowIsTranslucent設置爲false
         (2)若是仍是想用的話,就去掉清單文件中Activity中的android:screenOrientation="portrait",
        (3)就是使用透明的dialog或者PopupWindow來代替,也能夠用DialogFragment,看本身的需求和喜愛

複製代碼

Android 9.0的適配

9.1 CLEARTEXT communication to life.115.com not permitted by network security policy

  問題緣由: Android P 限制了明文流量的網絡請求,非加密的流量請求都會被系統禁止掉

解決方案:

  在資源文件新建xml目錄,新建文件

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>
複製代碼

  清單文件配置:

<application
        android:networkSecurityConfig="@xml/network_security_config">
        <!--9.0加的,哦哦-->
        <uses-library
            android:name="org.apache.http.legacy"
            android:required="false" />
    </application>
複製代碼

  但仍是建議都使用https進行傳輸

9.2 其餘Api的修改

java.lang.IllegalArgumentException: Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed

if (Build.VERSION.SDK_INT >= 26) {
                    canvas.clipPath(mPath);
                } else {
                    canvas.clipPath(mPath, Region.Op.REPLACE);
                }
複製代碼

總結

  通過幾天的踩坑,終於把targetSdkVersion升級到28,對於以上的經驗,也許還存在某些疏漏的,也但願你們能夠指正,補充,告訴,但願對你有必定的幫助,鄙人也很開心

相關文章
相關標籤/搜索