Android6.0,7.0適配

Android6.0適配

從Android 6.0 MarshMallow開始泰佳,Android支持動態(tài)權(quán)限管理,即有些權(quán)限需要在使用到的時候動態(tài)申請。

這些權(quán)限會關(guān)系到用戶的隱私或影響到其他應(yīng)用的運行,這些危險權(quán)限,谷歌還做了一個權(quán)限組凰盔,以分組的形式來呈現(xiàn):

[圖片上傳失敗...(image-3f71a8-1642080911915)]

由于運行權(quán)限機制的出現(xiàn),變得我們需要對新開發(fā)的應(yīng)用去做適配倦春,當(dāng)然有人會問户敬,那我之前開發(fā)的老應(yīng)用不就完蛋了,是不是運行到6.0系統(tǒng)上就會發(fā)生各種崩潰睁本?

其實不會的尿庐,谷歌做了良好的適配:
當(dāng)你的應(yīng)用targetSdkVersion小于23的時候,就算你運行在Android6.0系統(tǒng)上呢堰,它也會默認(rèn)采用以前的權(quán)限管理機制抄瑟,也就是一刀切。當(dāng)你的targetSdkVersion大于等于23的時候且在Andorid6.0(M)系統(tǒng)上枉疼,它才會采用新的這套權(quán)限管理機制皮假。

所以如果你想逃開這個“麻煩”鞋拟,只要把targetSdkVersion的版本設(shè)置為低于23就可以了,不過不建議采用這種方案惹资。

當(dāng)你的targetSdkVersion大于等于23的時候且在Andorid6.0(M)系統(tǒng)上贺纲,若不做適配,程序則會崩潰褪测,報錯信息如下:

01-16 02:23:46.181 E/CrashHandler Permission Denial: starting Intent { act=android.media.action.IMAGE_CAPTURE flg=0x3 cmp=com.android.camera/.Camera clip={text/uri-list U:file:///storage/emulated/0/teacher/head.jpg} (has extras) } from ProcessRecord{f7b731d 16568:com.lanxum.diagnosis.teacher.hg/u0a71} (pid=16568, uid=10071) with revoked permission android.permission.CAMERA
java.lang.SecurityException: Permission Denial: starting Intent { act=android.media.action.IMAGE_CAPTURE flg=0x3 cmp=com.android.camera/.Camera clip={text/uri-list U:file:///storage/emulated/0/teacher/head.jpg} (has extras) } from ProcessRecord{f7b731d 16568:com.lanxum.diagnosis.teacher.hg/u0a71} (pid=16568, uid=10071) with revoked permission android.permission.CAMERA

說了這么多猴誊,那么來看下怎么進行Android6.0(M)的權(quán)限管理適配吧,其實很簡單侮措,只需要記住下面幾個API方法就可以:(API23之后提供懈叹,support-v4包下也有提供)

int checkSelfPermission(String permission) 用來檢測應(yīng)用是否已經(jīng)具有權(quán)限
void requestPermissions(String[] permissions, int requestCode) 進行請求單個或多個權(quán)限
void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 請求權(quán)限結(jié)果回調(diào)

[圖片上傳失敗...(image-193991-1642080911916)]

以評測教師端app修改頭像功能模塊為例,該操作需要拍照和讀寫sd卡權(quán)限

下面來段代碼示例(當(dāng)用戶點擊照相機時)

//判斷當(dāng)前系統(tǒng)是否高于或等于6.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    //通過調(diào)用support-v4包提供的方法
    if (ContextCompat.checkSelfPermission(getActivity(), android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
        //具有拍照權(quán)限和讀寫SD卡權(quán)限分扎,直接調(diào)用相機
        //具體調(diào)用代碼
        takePhoto();
    } else {
        //不具有拍照權(quán)限和讀寫SD卡權(quán)限澄成,需要進行權(quán)限申請;在Fragment中申請權(quán)限笆包,不可以通過ActivityCompat.requestPermissions調(diào)用,直接使用Fragment的requestPermissions方法略荡,否則會回調(diào)到Activity的onRequestPermissionsResult
        requestPermissions(new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION_CAMERA_CODE);
    }
} else {
    //當(dāng)前系統(tǒng)小于6.0庵佣,直接調(diào)用拍照
    takePhoto();
}

private void takePhoto() {
    //照相
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    String externalStorageState = Environment.getExternalStorageState();
    if (externalStorageState.equals(Environment.MEDIA_MOUNTED)) {
        File dir = new File(Environment.getExternalStorageDirectory() + "/teacher");
        if (!dir.exists()) dir.mkdir();
        File f = new File(dir, "head.jpg");
        Uri u = Uri.fromFile(f);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, u);
        getActivity().startActivityForResult(intent, RESULT_CAPTURE);
    }
}

REQUEST_PERMISSION_CAMERA_CODE是個標(biāo)識碼,類似Intent跳轉(zhuǎn)的REQUEST_CODE的汛兜,然后我們就可以在onRequestPermissionsResult進行權(quán)限申請的回調(diào)處理:

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_PERMISSION_CAMERA_CODE) {
        if (!ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.CAMERA) || !ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE)) {
            ////如果用戶選擇了不再提示系統(tǒng)授權(quán)彈框,則自定義對話框引導(dǎo)用戶進行授權(quán)
            showMissingPermissionDialog();
            return;
        }
        if (grantResults.length >= 2) {
            int cameraResult = grantResults[0];
            int storageResult = grantResults[1];
            if (cameraResult == PackageManager.PERMISSION_GRANTED && storageResult == PackageManager.PERMISSION_GRANTED) {
                takePhoto();
            }
        }
    }
}

private void showMissingPermissionDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    builder.setTitle(R.string.help);
    builder.setMessage(R.string.string_help_text);

    // 關(guān)閉對話框
    builder.setNegativeButton(R.string.quit, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            dialog.dismiss();
        }
    });
    builder.setPositiveButton(R.string.settings, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            startAppSettings();
        }
    });
    builder.show();
}

// 啟動應(yīng)用的設(shè)置
private void startAppSettings() {
    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
    intent.setData(Uri.parse("package:" + getActivity().getPackageName()));
    startActivity(intent);
}

[圖片上傳失敗...(image-909967-1642080911916)]

注:shouldShowRequestPermissionRationale方法:當(dāng)app完全沒有機會被授權(quán)的時候巴粪,比如用戶在權(quán)限對話框中選擇了"不再顯示”,調(diào)用shouldShowRequestPermissionRationale() 則返回false粥谬,這種情況下肛根,我們可以通過自定義對話框引導(dǎo)用戶進行授權(quán),如下圖

[圖片上傳失敗...(image-30ea9-1642080911916)]

Android7.0適配

在Android7.0版本上漏策,Android系統(tǒng)強制執(zhí)行了StrictMode API 政策派哲,禁止向你的應(yīng)用外公開File://URI。如果一項包含文件File://URI類型的Intent離開你的應(yīng)用掺喻,應(yīng)用失敗芭届,并出現(xiàn)FileUriExposedException異常,比如系統(tǒng)相機拍照感耙,裁剪照片

經(jīng)測試褂乍,F(xiàn)ile api在應(yīng)用內(nèi)讀取文件存儲依然可以繼續(xù)使用,應(yīng)用間(主要指調(diào)用部分系統(tǒng)應(yīng)用)進行共享會直接報錯即硼。由于評測教師端裁剪照片功能通過自定義的ClipHeaderActivity類來實現(xiàn)逃片,屬于應(yīng)用內(nèi),所以不會報錯

在Android7.0之前只酥,如果你想調(diào)用系統(tǒng)相機拍照可以通過以下代碼來進行:

    //照相
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    String externalStorageState = Environment.getExternalStorageState();
    if (externalStorageState.equals(Environment.MEDIA_MOUNTED)) {
        File dir = new File(Environment.getExternalStorageDirectory() + "/teacher");
        if (!dir.exists()) dir.mkdir();
        File f = new File(dir, "head.jpg");
        Uri u = Uri.fromFile(f);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, u);
        getActivity().startActivityForResult(intent, RESULT_CAPTURE);
    }   

應(yīng)對策略:若要在應(yīng)用間共享文件褥实,可以發(fā)送 content:// URI類型的Uri呀狼,并授予 URI 臨時訪問權(quán)限。 進行此授權(quán)的最簡單方式是使用 FileProvider類

使用FileProvider

使用FileProvider的大致步驟如下:

第一步:在manifest清單文件中注冊provider

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${FILE_PROVIDER}"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths">
        </meta-data>
    </provider>

注:${FILE_PROVIDER}的值定義在主工程app目錄下的build.gradle文件內(nèi)性锭,不同渠道的app需要配置不同的值赠潦,否則會和多渠道打包沖突

productFlavors {
    ningxia {
        applicationId "com.lanxum.diagnosis.teacher.nx"
        resValue "string", "app_name", "全優(yōu)學(xué)教師-寧夏正式版"
        buildConfigField "String", "PREFIX_URL", "\"http://218.95.173.132\""
        buildConfigField "int", "VERSION", "2"
        buildConfigField "String","FILE_PROVIDER","\"com.lanxum.diagnosis.teacher.nx.takephoto.fileprovider\""
        manifestPlaceholders = [FILE_PROVIDER: "com.lanxum.diagnosis.teacher.nx.takephoto.fileprovider"]
    }
    ningxiatest {
        applicationId "com.lanxum.diagnosis.teacher.nxtest"
        resValue "string", "app_name", "全優(yōu)學(xué)教師-寧夏測試版"
        buildConfigField "String", "PREFIX_URL", "\"http://223.202.64.196:8084\""
        buildConfigField "int", "VERSION", "2"
        buildConfigField "String","FILE_PROVIDER","\"com.lanxum.diagnosis.teacher.nxtest.takephoto.fileprovider\""
        manifestPlaceholders = [FILE_PROVIDER: "com.lanxum.diagnosis.teacher.nxtest.takephoto.fileprovider"]
    }
    huanggang {
        applicationId "com.lanxum.diagnosis.teacher.hg"
        resValue "string", "app_name", "全優(yōu)學(xué)教師-黃岡版"
        buildConfigField "String", "PREFIX_URL", "\"http://221.235.188.44:51311\""
        buildConfigField "int", "VERSION", "1"
        buildConfigField "String","FILE_PROVIDER","\"com.lanxum.diagnosis.teacher.hg.takephoto.fileprovider\""
        manifestPlaceholders = [FILE_PROVIDER: "com.lanxum.diagnosis.teacher.hg.takephoto.fileprovider"]
    }
}

第二步:指定共享的目錄

為了指定共享的目錄我們需要在資源(res)目錄下創(chuàng)建一個xml目錄,然后創(chuàng)建一個名為“file_paths”(名字可以隨便起草冈,只要和在manifest注冊的provider所引用的resource保持一致即可)的資源文件她奥,內(nèi)容如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths>
<external-path path="" name="camera_photos" />
</paths>
</resources>

<files-path/>代表的根目錄: Context.getFilesDir()

<external-path/>代表的根目錄: Environment.getExternalStorageDirectory()

<cache-path/>代表的根目錄: getCacheDir()

注:上述代碼中path="",是有特殊意義的怎棱,它代碼根目錄哩俭,也就是說你可以向其它的應(yīng)用共享根目錄及其子目錄下任何一個文件了,如果你將path設(shè)為path="pictures"拳恋,
那么它代表著根目錄下的pictures目錄(eg:/storage/emulated/0/pictures)凡资,如果你向其它應(yīng)用分享pictures目錄范圍之外的文件是不行的。

第三步:使用FileProvider

上述準(zhǔn)備工作做完之后谬运,現(xiàn)在我們就可以使用FileProvider了隙赁。
還是以調(diào)用系統(tǒng)相機拍照為例,我們需要將上述拍照代碼修改為如下:

//照相

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
String externalStorageState = Environment.getExternalStorageState();
if (externalStorageState.equals(Environment.MEDIA_MOUNTED)) {
    File dir = new File(Environment.getExternalStorageDirectory() + "/teacher");
    if (!dir.exists()) dir.mkdir();
        File f = new File(dir, "head.jpg");
    Uri u;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        u = FileProvider.getUriForFile(getContext(), BuildConfig.FILE_PROVIDER, f);
    } else {
        u = Uri.fromFile(f);
    }
    intent.putExtra(MediaStore.EXTRA_OUTPUT, u);
    getActivity().startActivityForResult(intent, RESULT_CAPTURE);
}

以上內(nèi)容均為參考以下鏈接梆暖,上述示例代碼我也只在模擬器上測試過伞访,尚未在真機上測試,可能還存在不兼容和待優(yōu)化的地方轰驳,想要了解更詳細具體的內(nèi)容厚掷,可以直接查看以下鏈接地址

參考鏈接:

http://www.reibang.com/p/56b9fb319310

http://www.reibang.com/p/a37f4827079a

http://www.reibang.com/p/feb6dd8d2212

http://www.reibang.com/p/d3a998ec04ad

https://www.aswifter.com/2015/11/04/android-6-permission/

http://www.chaisong.xyz/2016/11/20/2016-11-20/

https://my.oschina.net/u/990728/blog/549914

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市级解,隨后出現(xiàn)的幾起案子冒黑,更是在濱河造成了極大的恐慌,老刑警劉巖勤哗,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抡爹,死亡現(xiàn)場離奇詭異,居然都是意外死亡芒划,警方通過查閱死者的電腦和手機豁延,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來腊状,“玉大人诱咏,你說我怎么就攤上這事〗赏冢” “怎么了袋狞?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我苟鸯,道長同蜻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任早处,我火速辦了婚禮湾蔓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘砌梆。我一直安慰自己默责,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布咸包。 她就那樣靜靜地躺著桃序,像睡著了一般。 火紅的嫁衣襯著肌膚如雪烂瘫。 梳的紋絲不亂的頭發(fā)上媒熊,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機與錄音坟比,去河邊找鬼芦鳍。 笑死,一個胖子當(dāng)著我的面吹牛葛账,可吹牛的內(nèi)容都是我干的柠衅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼注竿,長吁一口氣:“原來是場噩夢啊……” “哼茄茁!你這毒婦竟也來了魂贬?” 一聲冷哼從身側(cè)響起巩割,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎付燥,沒想到半個月后宣谈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡键科,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年闻丑,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片勋颖。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡嗦嗡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出饭玲,到底是詐尸還是另有隱情侥祭,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站矮冬,受9級特大地震影響谈宛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜胎署,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一吆录、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧琼牧,春花似錦恢筝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至抱究,卻和暖如春恢氯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鼓寺。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工勋拟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人妈候。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓敢靡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親苦银。 傳聞我的和親對象是個殘疾皇子啸胧,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容