Android10填坑適配指南仔役,實際經驗代碼掷伙,拒絕翻譯

Android10填坑適配指南,包含實際經驗代碼又兵,絕不照搬翻譯文檔

1.Region.Op相關異常:java.lang.IllegalArgumentException: Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed

targetSdkVersion >= Build.VERSION_CODES.P 時調用 canvas.clipPath(path, Region.Op.XXX); 引起的異常任柜,參考源碼如下:

@Deprecated
public boolean clipPath(@NonNull Path path, @NonNull Region.Op op) {
     checkValidClipOp(op);
     return nClipPath(mNativeCanvasWrapper, path.readOnlyNI(), op.nativeInt);
}

private static void checkValidClipOp(@NonNull Region.Op op) {
     if (sCompatiblityVersion >= Build.VERSION_CODES.P
         && op != Region.Op.INTERSECT && op != Region.Op.DIFFERENCE) {
         throw new IllegalArgumentException(
                    "Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed");
     }
}

我們可以看到當目標版本從Android P開始,Canvas.clipPath(@NonNull Path path, @NonNull Region.Op op) ; 已經被廢棄沛厨,而且是包含異常風險的廢棄API乘盼,只有 Region.Op.INTERSECT 和 Region.Op.DIFFERENCE 得到兼容,幾乎所有的博客解決方案都是如下簡單粗暴:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    canvas.clipPath(path);
} else {
    canvas.clipPath(path, Region.Op.XOR);// REPLACE俄烁、UNION 等
}

但我們一定需要一些高級邏輯運算效果怎么辦?如小說的仿真翻頁閱讀效果级野,解決方案如下页屠,用Path.op代替粹胯,先運算Path,再給canvas.clipPath:

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){
    Path mPathXOR = new Path();
    mPathXOR.moveTo(0,0);
    mPathXOR.lineTo(getWidth(),0);
    mPathXOR.lineTo(getWidth(),getHeight());
    mPathXOR.lineTo(0,getHeight());
    mPathXOR.close();
    //以上根據實際的Canvas或View的大小辰企,畫出相同大小的Path即可
    mPathXOR.op(mPath0, Path.Op.XOR);
    canvas.clipPath(mPathXOR);
}else {
    canvas.clipPath(mPath0, Region.Op.XOR);
}

2.明文HTTP限制

targetSdkVersion >= Build.VERSION_CODES.P 時风纠,默認限制了HTTP請求,并出現相關日志:

java.net.UnknownServiceException: CLEARTEXT communication to xxx not permitted by network security policy

第一種解決方案:在AndroidManifest.xml中Application添加如下節(jié)點代碼

<application android:usesCleartextTraffic="true">

第二種解決方案:在res目錄新建xml目錄牢贸,已建的跳過 在xml目錄新建一個xml文件network_security_config.xml竹观,然后在AndroidManifest.xml中Application添加如下節(jié)點代碼

android:networkSecurityConfig="@xml/network_config"

名字隨機,內容如下:

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

3.Android Q中的媒體資源讀寫

1潜索、掃描系統(tǒng)相冊臭增、視頻等,圖片竹习、視頻選擇器都是通過ContentResolver來提供誊抛,主要代碼如下:

private static final String[] IMAGE_PROJECTION = {
            MediaStore.Images.Media.DATA,
            MediaStore.Images.Media.DISPLAY_NAME,
            MediaStore.Images.Media._ID,
            MediaStore.Images.Media.BUCKET_ID,
            MediaStore.Images.Media.BUCKET_DISPLAY_NAME};

 Cursor imageCursor = mContext.getContentResolver().query(
                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                    IMAGE_PROJECTION, null, null, IMAGE_PROJECTION[4] + " DESC");

String path = imageCursor.getString(imageCursor.getColumnIndexOrThrow(IMAGE_PROJECTION[0]));
String name = imageCursor.getString(imageCursor.getColumnIndexOrThrow(IMAGE_PROJECTION[1]));
int id = imageCursor.getInt(imageCursor.getColumnIndexOrThrow(IMAGE_PROJECTION[2]));
String folderPath = imageCursor.getString(imageCursor.getColumnIndexOrThrow(IMAGE_PROJECTION[3]));
String folderName = imageCursor.getString(imageCursor.getColumnIndexOrThrow(IMAGE_PROJECTION[4]));

//Android Q 公有目錄只能通過Content Uri + id的方式訪問,以前的File路徑全部無效整陌,如果是Video拗窃,記得換成MediaStore.Videos
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
      path  = MediaStore.Images.Media
                       .EXTERNAL_CONTENT_URI
                       .buildUpon()
                       .appendPath(String.valueOf(id)).build().toString();
 }

2、判斷公有目錄文件是否存在泌辫,自Android Q開始随夸,公有目錄File API都失效,不能直接通過new File(path).exists();判斷公有目錄文件是否存在震放,正確方式如下:

public static boolean isAndroidQFileExists(Context context, String path){
        AssetFileDescriptor afd = null;
        ContentResolver cr = context.getContentResolver();
        try {
            Uri uri = Uri.parse(path);
            afd = cr.openAssetFileDescriptor(uri, "r");
            if (afd == null) {
                return false;
            } else {
                close(afd);
            }
        } catch (FileNotFoundException e) {
            return false;
        }finally {
            close(afd);
        }
        return true;
}

3宾毒、copy或者下載文件到公有目錄,保存Bitmap同理澜搅,如Download伍俘,MIME_TYPE類型可以自行參考對應的文件類型,這里只對APK作出說明,從私有目錄copy到公有目錄demo如下(遠程下載同理勉躺,只要拿到OutputStream即可癌瘾,亦可下載到私有目錄再copy到公有目錄):

public static void copyToDownloadAndroidQ(Context context, String sourcePath, String fileName, String saveDirName){
        ContentValues values = new ContentValues();
        values.put(MediaStore.Downloads.DISPLAY_NAME, fileName);
        values.put(MediaStore.Downloads.MIME_TYPE, "application/vnd.android.package-archive");
        values.put(MediaStore.Downloads.RELATIVE_PATH, "Download/" + saveDirName.replaceAll("/","") + "/");

        Uri external = MediaStore.Downloads.EXTERNAL_CONTENT_URI;
        ContentResolver resolver = context.getContentResolver();

        Uri insertUri = resolver.insert(external, values);
        if(insertUri == null) {
            return;
        }

        String mFilePath = insertUri.toString();

        InputStream is = null;
        OutputStream os = null;
        try {
            os = resolver.openOutputStream(insertUri);
            if(os == null){
                return;
            }
            int read;
            File sourceFile = new File(sourcePath);
            if (sourceFile.exists()) { // 文件存在時
                is = new FileInputStream(sourceFile); // 讀入原文件
                byte[] buffer = new byte[1444];
                while ((read = is.read(buffer)) != -1) {
                    os.write(buffer, 0, read);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            close(is,os);
        }

}

4、保存圖片相關

 /**
     * 通過MediaStore保存饵溅,兼容AndroidQ妨退,保存成功自動添加到相冊數據庫,無需再發(fā)送廣播告訴系統(tǒng)插入相冊
     *
     * @param context      context
     * @param sourceFile   源文件
     * @param saveFileName 保存的文件名
     * @param saveDirName  picture子目錄
     * @return 成功或者失敗
     */
    public static boolean saveImageWithAndroidQ(Context context,
                                                  File sourceFile,
                                                  String saveFileName,
                                                  String saveDirName) {
        String extension = BitmapUtil.getExtension(sourceFile.getAbsolutePath());

        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.DESCRIPTION, "This is an image");
        values.put(MediaStore.Images.Media.DISPLAY_NAME, saveFileName);
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
        values.put(MediaStore.Images.Media.TITLE, "Image.png");
        values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/" + saveDirName);

        Uri external = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        ContentResolver resolver = context.getContentResolver();

        Uri insertUri = resolver.insert(external, values);
        BufferedInputStream inputStream = null;
        OutputStream os = null;
        boolean result = false;
        try {
            inputStream = new BufferedInputStream(new FileInputStream(sourceFile));
            if (insertUri != null) {
                os = resolver.openOutputStream(insertUri);
            }
            if (os != null) {
                byte[] buffer = new byte[1024 * 4];
                int len;
                while ((len = inputStream.read(buffer)) != -1) {
                    os.write(buffer, 0, len);
                }
                os.flush();
            }
            result = true;
        } catch (IOException e) {
            result = false;
        } finally {
            close(os, inputStream);
        }
        return result;
}

4.EditText默認不獲取焦點蜕企,不自動彈出鍵盤

該問題出現在 targetSdkVersion >= Build.VERSION_CODES.P 情況下咬荷,且設備版本為Android P以上版本,解決方法在onCreate中加入如下代碼轻掩,可獲得焦點幸乒,如需要彈出鍵盤可延遲一下:

mEditText.post(() -> {
       mEditText.requestFocus();
       mEditText.setFocusable(true);
       mEditText.setFocusableInTouchMode(true);
});

5.安裝APK Intent及其它共享文件相關Intent

/*
* 自Android N開始,是通過FileProvider共享相關文件唇牧,但是Android Q對公有目錄 File API進行了限制罕扎,只能通過Uri來操作聚唐,
* 從代碼上看,又變得和以前低版本一樣了腔召,只是必須加上權限代碼Intent.FLAG_GRANT_READ_URI_PERMISSION
*/ 
private void installApk() {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
            //適配Android Q,注意mFilePath是通過ContentResolver得到的杆查,上述有相關代碼
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setDataAndType(Uri.parse(mFilePath) ,"application/vnd.android.package-archive");
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            startActivity(intent);
            return ;
        }

        File file = new File(saveFileName + "demo.apk");
        if (!file.exists())
            return;
        Intent intent = new Intent(Intent.ACTION_VIEW);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            Uri contentUri = FileProvider.getUriForFile(getApplicationContext(), "net.oschina.app.provider", file);
            intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
        } else {
            intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }
        startActivity(intent);
}

6.Activity透明相關,windowIsTranslucent屬性

Android Q 又一個天坑臀蛛,如果你要顯示一個半透明的Activity亲桦,這在android10之前普通樣式Activity只需要設置windowIsTranslucent=true即可,但是到了AndroidQ浊仆,它沒有效果了客峭,而且如果動態(tài)設置View.setVisibility(),界面還會出現殘影...

解決辦法:使用Dialog樣式Activity,且設置windowIsFloating=true氧卧,此時問題又來了桃笙,如果Activity根布局沒有設置fitsSystemWindow=true,默認是沒有侵入狀態(tài)欄的沙绝,使界面看上去正常搏明。

7.剪切板兼容

Android Q中只有當應用處于可交互情況(默認輸入法本身就可交互)才能訪問剪切板和監(jiān)聽剪切板變化,在onResume回調也無法直接訪問剪切板闪檬,這么做的好處是避免了一些應用后臺瘋狂監(jiān)聽響應剪切板的內容星著,瘋狂彈窗。

因此如果還需要監(jiān)聽剪切板粗悯,可以使用應用生命周期回調虚循,監(jiān)聽APP后臺返回,延遲幾毫秒訪問剪切板样傍,再保存最后一次訪問得到的剪切板內容横缔,每次都比較一下是否有變化,再進行下一步操作衫哥。

8.第三方分享圖片等操作茎刚,直接使用文件路徑的,如QQ圖片分享撤逢,都需要注意膛锭,這是不可行的,都只能通過MediaStore等API蚊荣,拿到Uri來操作

目前我們實際遇到的問題就這些
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末初狰,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子互例,更是在濱河造成了極大的恐慌奢入,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件媳叨,死亡現場離奇詭異俊马,居然都是意外死亡丁存,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門柴我,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人扩然,你說我怎么就攤上這事艘儒。” “怎么了夫偶?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵界睁,是天一觀的道長。 經常有香客問我兵拢,道長翻斟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任说铃,我火速辦了婚禮访惜,結果婚禮上,老公的妹妹穿的比我還像新娘腻扇。我一直安慰自己债热,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布幼苛。 她就那樣靜靜地躺著窒篱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪舶沿。 梳的紋絲不亂的頭發(fā)上墙杯,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音括荡,去河邊找鬼高镐。 笑死,一個胖子當著我的面吹牛一汽,可吹牛的內容都是我干的避消。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼召夹,長吁一口氣:“原來是場噩夢啊……” “哼岩喷!你這毒婦竟也來了?” 一聲冷哼從身側響起监憎,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤纱意,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鲸阔,有當地人在樹林里發(fā)現了一具尸體偷霉,經...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡迄委,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了类少。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叙身。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖硫狞,靈堂內的尸體忽然破棺而出信轿,到底是詐尸還是另有隱情,我是刑警寧澤残吩,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布财忽,位于F島的核電站,受9級特大地震影響泣侮,放射性物質發(fā)生泄漏即彪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一活尊、第九天 我趴在偏房一處隱蔽的房頂上張望隶校。 院中可真熱鬧,春花似錦酬凳、人聲如沸惠况。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽稠屠。三九已至,卻和暖如春翎苫,著一層夾襖步出監(jiān)牢的瞬間权埠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工煎谍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留攘蔽,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓呐粘,卻偏偏與公主長得像满俗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子作岖,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容