android 6.0 權(quán)限管理

1炕横、問題

運(yùn)行在android 6.0上恩脂,出現(xiàn)了這樣的錯(cuò)誤:

java.lang.SecurityException: getDeviceId: Neither user xxxxx nor current process has Android.permission.READ_PHONE_STATE

在API 23 以上召噩,如果沒有給應(yīng)用加上動(dòng)態(tài)權(quán)限請(qǐng)求儡湾,應(yīng)用不會(huì)獲得任何權(quán)限8廊荨H嘌帧庄撮!

2、解決方案

在應(yīng)用沒有切換到運(yùn)行時(shí)權(quán)限模型Runtime Permissions之前毙籽,以下兩種方式可以保證應(yīng)用不出錯(cuò):

1洞斯、compileSdkVersion 24、buildToolsVersion "24.0.1"坑赡,targetSdkVersion 22烙如,采用的是 API 23以下的權(quán)限管理模型。如果寫成24會(huì)報(bào)錯(cuò)毅否,因?yàn)闆]有權(quán)限亚铁。

2、compileSdkVersion 22螟加、buildToolsVersion "22.0.1"徘溢、targetSdkVersion 22,同時(shí)注意所有l(wèi)ibrary引用的是22這個(gè)版本號(hào)捆探。

在...XXX.../merged/debug/values-v23/values-v23.xml下然爆,會(huì)報(bào)錯(cuò):Error retrieving parent for item

在它引用的library中,引用了版本號(hào)為23的庫(kù),如:

dependencies {
compile 'com.android.support:appcompat-v7:23.1.1'
}

所以,去除 com.hqgj:myvideo:0.3.5的appcompat-v7的模塊

compile ('com.hqgj:myvideo:0.3.5'){
  exclude module: 'appcompat-v7'
}

關(guān)于compileSdkVersion黍图、targetSdkVersion

compileSdkVersion

compileSdkVersion 告訴 Gradle 用哪個(gè) Android SDK 版本編譯你的應(yīng)用曾雕。修改 compileSdkVersion 不會(huì)改變運(yùn)行時(shí)的行為。當(dāng)你修改了 compileSdkVersion 的時(shí)候助被,可能會(huì)出現(xiàn)新的編譯警告剖张、編譯錯(cuò)誤,但新的 compileSdkVersion 不會(huì)被包含到 APK 中:它純粹只是在編譯的時(shí)候使用揩环。

targetSdkVersion

targetSdkVersion 是 Android 提供向前兼容的主要依據(jù)修械,在應(yīng)用的 targetSdkVersion 沒有更新之前系統(tǒng)不會(huì)應(yīng)用最新的行為變化。這允許你在適應(yīng)新的行為變化之前就可以使用新的 API (因?yàn)槟阋呀?jīng)更新了 compileSdkVersion 不是嗎检盼?)肯污。

3、Runtime Permissions,動(dòng)態(tài)權(quán)限管理

3蹦渣、動(dòng)態(tài)權(quán)限管理

Runtime Permissions哄芜,即,應(yīng)用在運(yùn)行的時(shí)候柬唯,獲得權(quán)限认臊,而不是在安裝的時(shí)候得到權(quán)限,但是這個(gè)行為锄奢,取決于系統(tǒng)的版本和targetSdkVersion:

  • 設(shè)備的版本號(hào)<=android 5.1 或者 targetSdkVersion<=22 失晴,是在安裝的時(shí)候得到權(quán)限
  • 如果>=6.0 同時(shí) targetSdkVersion>=23,運(yùn)行的時(shí)候獲得權(quán)限

但是拘央,即便targetSdkVersion<=22涂屁,只要設(shè)備的版本號(hào)>=android 6.0,用戶依然可以撤銷授權(quán)(設(shè)置--應(yīng)用)灰伟。

Google將權(quán)限分為兩類:

Normal Permissions拆又,這類權(quán)限一般不涉及用戶隱私,是不需要用戶進(jìn)行授權(quán)的栏账,比如手機(jī)震動(dòng)帖族、訪問網(wǎng)絡(luò)等:

ACCESS_NETWORK_STATE、ACCESS_WIFI_STATE挡爵、CHANGE_NETWORK_STATE竖般、CHANGE_WIFI_STATE、INTERNET茶鹃、MODIFY_AUDIO_SETTINGS生巡、RECEIVE_BOOT_COMPLETED(開機(jī)廣播)批钠、VIBRATE缆八、WAKE_LOCK

Dangerous Permissions恨溜,一般是涉及到用戶隱私的垃杖,需要用戶進(jìn)行授權(quán)男杈,比如讀取sdcard、訪問通訊錄调俘、電話伶棒、日歷、攝像頭彩库、位置肤无、錄音、SMS等:

WRITE_EXTERNAL_STORAGE骇钦、CAMERA宛渐、READ_PHONE_STATE、ACCESS_COARSE_LOCATION

幾個(gè)方法:

  • ContextCompat.checkSelfPermission(context,permission)
    主要用于檢測(cè)某個(gè)權(quán)限是否已經(jīng)被授予,方法返回值為PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED窥翩。當(dāng)返回DENIED就需要進(jìn)行申請(qǐng)授權(quán)了业岁。

  • ActivityCompat.requestPermissions(context,permissions,PERMISSION_REQUEST_CODE)
    請(qǐng)求權(quán)限

  • onRequestPermissionsResult
    回調(diào)

  • shouldShowRequestPermissionRationale()

如果用戶拒絕某授權(quán)。下一次彈框寇蚊,用戶會(huì)有一個(gè)“不再提醒”的選項(xiàng)的來防止app以后繼續(xù)請(qǐng)求授權(quán)笔时。
  如果這個(gè)選項(xiàng)在拒絕授權(quán)前被用戶勾選了,下次為這個(gè)權(quán)限請(qǐng)求requestPermissions時(shí)仗岸,對(duì)話框就不彈出來了允耿,系統(tǒng)會(huì)直接回調(diào)onRequestPermissionsResult函數(shù),回調(diào)結(jié)果為最后一次用戶的選擇扒怖。所以為了應(yīng)對(duì)這種情況较锡,系統(tǒng)提供了一個(gè)shouldShowRequestPermissionRationale()函數(shù),這個(gè)函數(shù)的作用是幫助開發(fā)者找到需要向用戶額外解釋權(quán)限的情況姚垃,這個(gè)函數(shù):
  ** 應(yīng)用安裝后第一次訪問念链,直接返回false;第一次請(qǐng)求權(quán)限時(shí)积糯,用戶拒絕了掂墓,下一次shouldShowRequestPermissionRationale()返回 true,這時(shí)候可以顯示一些為什么需要這個(gè)權(quán)限的說明看成;第二次請(qǐng)求權(quán)限時(shí)君编,用戶拒絕了,并選擇了“不再提醒”的選項(xiàng)時(shí):shouldShowRequestPermissionRationale()返回 false川慌;設(shè)備的系統(tǒng)設(shè)置中禁止當(dāng)前應(yīng)用獲取這個(gè)權(quán)限的授權(quán)吃嘿,shouldShowRequestPermissionRationale()返回false **;

注意:第二次請(qǐng)求權(quán)限時(shí)梦重,才會(huì)有“不再提醒”的選項(xiàng)兑燥,如果用戶一直拒絕,并沒有選擇“不再提醒”的選項(xiàng)琴拧,下次請(qǐng)求權(quán)限時(shí)降瞳,會(huì)繼續(xù)有“不再提醒”的選項(xiàng),并且shouldShowRequestPermissionRationale()也會(huì)一直返回true蚓胸。

代碼:

public class PermissionsChecker {    
   private Context context;    
   public PermissionsChecker(Context context) {        
        this.context = context.getApplicationContext();    
   }    
  public boolean lacksPermissions(String...permissions){
        Log.i("permission","length:"+permissions.length); 
       for(String permission:permissions){
            if(lacksPermission(permission)){
                return  true;
            }
        }
        return false;
    }
    private boolean lacksPermission(String permission) {
        return ContextCompat.checkSelfPermission(context,permission)== PackageManager.PERMISSION_DENIED;
    }

/** * 是否授予權(quán)限 */
public boolean verifyPermissions(int[] grantResults) {
    // At least one result must be checked.
    if (grantResults.length < 1) {
        return false;
    }
    // Verify that each required permission has been granted, otherwise return false.
    for (int result : grantResults) {
        if (result != PackageManager.PERMISSION_GRANTED) {
            return false;
        } 
   }
    return true;
}
}


private PermissionsChecker permissionsChecker;
private boolean flag;private String[] permissions=new String[]{        Manifest.permission.WRITE_EXTERNAL_STORAGE,
        Manifest.permission.CAMERA,
        Manifest.permission.ACCESS_COARSE_LOCATION,
        Manifest.permission.READ_PHONE_STATE};
private static final int PERMISSION_REQUEST_CODE = 1;
private static final int SEETING_PERMISSION_REQUEST_CODE = 2;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    permissionsChecker=new PermissionsChecker(this);
    if(savedInstanceState==null){
        flag=true;
    }else{
        flag=savedInstanceState.getBoolean("flag");
    }
}

//在權(quán)限被回收之后挣饥,activity會(huì)被重啟
@Override public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
    super.onSaveInstanceState(outState, outPersistentState);
    outState.putBoolean("flag", flag);
}
@Override protected void onResume() {
    super.onResume();
    if(permissionsChecker.lacksPermissions(permissions)){
        requestMainPermissions();
    }
}
private void requestMainPermissions() {
    if(flag){
        flag=false;
        ActivityCompat.requestPermissions(MainActivity.this, permissions, PERMISSION_REQUEST_CODE);
    }
}
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if(requestCode==PERMISSION_REQUEST_CODE && permissionsChecker.verifyPermissions(grantResults)){
    }
else {
        AlertDialog.Builder builder=new AlertDialog.Builder(this);
        builder.setCancelable(false);
        builder.setOnKeyListener(new DialogInterface.OnKeyListener() {
            @Override
            public boolean onKey(DialogInterface dialogInterface, int i, KeyEvent keyEvent) {
                if (keyEvent.getAction() == KeyEvent.KEYCODE_BACK) {
                    finish();
                }
                return false;
            }
        }
);
        builder.setTitle("提示");
        builder.setMessage("缺少必要權(quán)限");
        builder.setNegativeButton("退出", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                finish();
            }
        });
        builder.setPositiveButton("設(shè)置", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                intent.setData(Uri.parse("package:" + getPackageName()));
                startActivityForResult(intent, SEETING_PERMISSION_REQUEST_CODE);
            }
        });
        builder.show();
    }
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(requestCode==SEETING_PERMISSION_REQUEST_CODE){
        if(permissionsChecker.lacksPermissions(permissions)){
            finish();
        }
    }
}

1

代碼:
https://github.com/my-sunshine/MyPermission

參考:
如何選擇 compileSdkVersion, minSdkVersion 和 targetSdkVersion
Android 6.0: 動(dòng)態(tài)權(quán)限管理的解決方案
http://www.2cto.com/kf/201512/455888.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市沛膳,隨后出現(xiàn)的幾起案子扔枫,更是在濱河造成了極大的恐慌,老刑警劉巖锹安,帶你破解...
    沈念sama閱讀 221,406評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件短荐,死亡現(xiàn)場(chǎng)離奇詭異倚舀,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)搓侄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門瞄桨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人讶踪,你說我怎么就攤上這事芯侥。” “怎么了乳讥?”我有些...
    開封第一講書人閱讀 167,815評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵柱查,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我云石,道長(zhǎng)唉工,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,537評(píng)論 1 296
  • 正文 為了忘掉前任汹忠,我火速辦了婚禮淋硝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘宽菜。我一直安慰自己谣膳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評(píng)論 6 397
  • 文/花漫 我一把揭開白布铅乡。 她就那樣靜靜地躺著继谚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪阵幸。 梳的紋絲不亂的頭發(fā)上花履,一...
    開封第一講書人閱讀 52,184評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音挚赊,去河邊找鬼诡壁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛荠割,可吹牛的內(nèi)容都是我干的妹卿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼涨共,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了宠漩?” 一聲冷哼從身側(cè)響起举反,我...
    開封第一講書人閱讀 39,668評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎扒吁,沒想到半個(gè)月后火鼻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體室囊,經(jīng)...
    沈念sama閱讀 46,212評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評(píng)論 3 340
  • 正文 我和宋清朗相戀三年魁索,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了融撞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,438評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡粗蔚,死狀恐怖尝偎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鹏控,我是刑警寧澤致扯,帶...
    沈念sama閱讀 36,128評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站当辐,受9級(jí)特大地震影響抖僵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缘揪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評(píng)論 3 333
  • 文/蒙蒙 一耍群、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧找筝,春花似錦蹈垢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至陆赋,卻和暖如春沐祷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背攒岛。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工赖临, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人灾锯。 一個(gè)月前我還...
    沈念sama閱讀 48,827評(píng)論 3 376
  • 正文 我出身青樓兢榨,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親顺饮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子吵聪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評(píng)論 2 359

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

  • 一、引言 隨著Android6.0發(fā)布兼雄,系統(tǒng)增加了一些新的特性和功能吟逝。這次的發(fā)布介紹了一種新的權(quán)限機(jī)制。用戶可以在...
    宇是我閱讀 7,464評(píng)論 7 41
  • 安卓平臺(tái)權(quán)限一直有被流氓應(yīng)用隨便利用詬病, android M的發(fā)布徹底解決了這一問題,取而代之的是赦肋,app不得不...
    常興E站閱讀 1,647評(píng)論 0 5
  • 只有一杯少得可憐的愛块攒,我一向如此吝嗇励稳。對(duì)愛情,一向來說囱井,不貪婪就是不夠愛驹尼,不貪婪是個(gè)犯罪行為。 這杯愛里有70%的...
    夢(mèng)蝶Ariel閱讀 367評(píng)論 0 2
  • 今天這么乖啊……
    鬼畫胡子閱讀 302評(píng)論 0 0
  • 顧念北緊緊地圈抱住懷里的慕南枝庞呕,姑娘帶著微微醉意新翎,雙眼朦朧。 只聽得細(xì)小的聲音千扶,糯糯軟軟的:“念北哥哥.........
    貓咪吖閱讀 475評(píng)論 1 6