Android 6.0 動(dòng)態(tài)權(quán)限Permission相關(guān)

Andriod 6.0 動(dòng)態(tài)權(quán)限Permission相關(guān)

推薦博文:

隨著Android 6.0發(fā)布以及普及,我們開發(fā)者所要應(yīng)對(duì)的主要就是新版本SDK帶來的一些變化谤牡,首先關(guān)注的就是權(quán)限機(jī)制的變化锋爪。對(duì)于6.0的幾個(gè)主要的變化丙曙,查看查看官網(wǎng)的這篇文章http://developer.android.com/intl/zh-cn/about/versions/marshmallow/android-6.0-changes.html,其中當(dāng)然包含Runtime Permissions几缭。

相關(guān)知識(shí)點(diǎn)

新的權(quán)限機(jī)制更好的保護(hù)了用戶的隱私河泳,Google將權(quán)限分為兩類,一類是Normal Permissions年栓,這類權(quán)限一般不涉及用戶隱私,是不需要用戶進(jìn)行授權(quán)的薄霜,比如手機(jī)震動(dòng)某抓、訪問網(wǎng)絡(luò)等;另一類是Dangerous Permission惰瓜,一般是涉及到用戶隱私的否副,需要用戶進(jìn)行授權(quán),比如讀取sdcard崎坊、訪問通訊錄等备禀。

targetSdkVersion和minSdkVersion相關(guān)的區(qū)別

關(guān)于taretSdkVersion和minSdkVersion的區(qū)分相信很多人都不是太清楚,在這里推薦Android Min SDK Version vs. Target SDK Version,對(duì)相關(guān)的設(shè)置有點(diǎn)說明奈揍。這兩者相當(dāng)于一個(gè)區(qū)間曲尸,你可以用到targetSDK中最新的API和最酷的新功能,但你又不得不向下兼容到minSDK男翰,保證這個(gè)區(qū)間內(nèi)的設(shè)備都可以正常的運(yùn)行你的app另患。換句話說,你想使用Android剛剛推出的新特性蛾绎,但這對(duì)于你的app又不是必須的昆箕,你就可以將targetSDK設(shè)置為你想使用新特性的SDK版本,minSDK設(shè)置成低版本保證所有人都可以使用你的app租冠。

  • minSdkVersion與maxSdkVersion比較容易理解鹏倘,就是在安裝程序的時(shí)候,如果目標(biāo)設(shè)備的API版本小于minSdkVersion顽爹, 或者大于maxSdkVersion纤泵,程序?qū)o法安裝。一般來說沒有必要設(shè)置maxSdkVersion话原。

  • targetSdkVersion相對(duì)復(fù)雜一些夕吻,如果設(shè)置了此屬性诲锹,那么在程序執(zhí)行時(shí),如果目標(biāo)設(shè)備的API版本正好等于此數(shù)值涉馅, 他會(huì)告訴Android平臺(tái):此程序在此版本已經(jīng)經(jīng)過充分測(cè)归园,沒有問題。不必為此程序開啟兼容性檢查判斷的工作了稚矿。 也就是說庸诱,如果targetSdkVersion與目標(biāo)設(shè)備的API版本相同時(shí),運(yùn)行效率可能會(huì)高一些晤揣。 但是桥爽,這個(gè)設(shè)置僅僅是一個(gè)聲明、一個(gè)通知昧识,不會(huì)有太實(shí)質(zhì)的作用钠四, 比如說,使用了targetSdkVersion這個(gè)SDK版本中的一個(gè)特性跪楞,但是這個(gè)特性在低版本中是不支持的 缀去,那么在低版本的API設(shè)備上運(yùn)行程序時(shí),可能會(huì)報(bào)錯(cuò):Java.lang.VerifyError甸祭。也就是說缕碎,此屬性不會(huì)幫你解決兼容性的測(cè)試問題。 你至少需要在minSdkVersion這個(gè)版本上將程序完整的跑一遍來確定兼容性是沒有問題的池户。(這個(gè)問題確實(shí)讓人頭疼)

  • project.properties中的target是指在編譯的時(shí)候使用哪個(gè)版本的API進(jìn)行編譯咏雌。 綜上,上面的四個(gè)值其實(shí)是作用于不同的時(shí)期:
    target API level是在編譯的時(shí)候起作用校焦,用于指定使用哪個(gè)API版本(SDK版本)進(jìn)行編譯赊抖。 minSdkVersion和maxSdkVersion是在程序安裝的時(shí)候起作用, 用于指定哪些版本的設(shè)備可以安裝此應(yīng)用斟湃。 targetSdkVersion是在程序運(yùn)行的時(shí)候起作用熏迹,用于提高指定版本的設(shè)備上程序運(yùn)行體驗(yàn)。

  • 知道注意的是:最好將project.properties中target與AndroidManifest中minSdkVersion的APILevel設(shè)置為同一個(gè)等級(jí)凝赛,達(dá)到編譯兼容最小版本的效果注暗,如果前者比后者等級(jí)高的話,可能出現(xiàn)的一個(gè)問題就是墓猎,在開發(fā)過程中捆昏,一些高等級(jí)的api能夠通過編譯,但是當(dāng)實(shí)際運(yùn)行在低系統(tǒng)的真機(jī)或者模擬器上時(shí)毙沾,將會(huì)出現(xiàn)一些NoSuchMethodError的錯(cuò)誤骗卜,導(dǎo)致程序奔潰。例如:
    android.view.view.setBackgroundDrawable(Drawable background)方法在APILeven為16時(shí)廢棄(deprecated)掉了,改用setBackground(Drawable background)寇仓,開發(fā)中如果工程project.properties中的target編譯版本大于或等于16举户,而在AndroidManifest中設(shè)置的minSdkVersion為小于16,則會(huì)出現(xiàn)能夠通過開發(fā)工具的編譯遍烦,但運(yùn)行在4.1系統(tǒng)一下的機(jī)器中時(shí)俭嘁,會(huì)出現(xiàn)NoSuchMethodError的錯(cuò)誤!

  • 當(dāng)你的應(yīng)用targetSdkVersion小于23的時(shí)候服猪,就算你運(yùn)行在Android6.0系統(tǒng)上供填,它也會(huì)默認(rèn)采用以前的權(quán)限管理機(jī)制,也就是一刀切罢猪。當(dāng)你的targetSdkVersion大于等于23的時(shí)候且在Andorid6.0(M)系統(tǒng)上近她,它才會(huì)采用新的這套權(quán)限管理機(jī)制。
    所以如果你想逃開這個(gè)“麻煩”膳帕,只要把targetSdkVersion的版本設(shè)置為低于23就可以了粘捎,不過不建議采用這種方案,該來的總是要來的危彩,隨著國產(chǎn)手機(jī)ROM的更新晌端,比如小米,華為恬砂,魅族等也開始有部分機(jī)型進(jìn)行了系統(tǒng)升級(jí),所以這是種趨勢(shì)蓬痒。
    說了這么多泻骤,那么來看下怎么進(jìn)行Android6.0(M)的權(quán)限管理適配吧,其實(shí)很簡單梧奢,只需要記住下面幾個(gè)API方法就可以:(API23之后提供)
  • int checkSelfPermission(String permission) 用來檢測(cè)應(yīng)用是否已經(jīng)具有權(quán)限
  • void requestPermissions(String[] permissions, int requestCode) 進(jìn)行請(qǐng)求單個(gè)或多個(gè)權(quán)限
  • void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 請(qǐng)求權(quán)限結(jié)果回調(diào)

參考博文鏈接:http://www.reibang.com/p/a37f4827079a

下面來段代碼示例(為了向下兼容狱掂,這里我采用了v4包下的ContextCompat和ActivityCompat):

 View.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //判斷當(dāng)前系統(tǒng)是否高于或等于6.0
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                //當(dāng)前系統(tǒng)大于等于6.0
                if (ContextCompat.checkSelfPermission(MineInforActivity.this,Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
                    //具有拍照權(quán)限,直接調(diào)用相機(jī)
                    //具體調(diào)用代碼
                } else {
                    //不具有拍照權(quán)限亲轨,需要進(jìn)行權(quán)限申請(qǐng)
                    ActivityCompat.requestPermissions(MineInforActivity.this,new String[]{Manifest.permission.CAMERA}, REQUEST_PERMISSION_CAMERA_CODE);
                }
            } else {
                //當(dāng)前系統(tǒng)小于6.0趋惨,直接調(diào)用拍照

            }
        }
    });

如果用戶勾選了不再提醒,然后把你拒絕了惦蚊,那你的應(yīng)用就GG了器虾,其實(shí)這里還有一個(gè)API方法:

    if(!shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)){
                        //如果用戶勾選了不再提醒,則返回false
                        //給予用戶提醒蹦锋,比如Toast或者對(duì)話框兆沙,讓用戶去系統(tǒng)設(shè)置-應(yīng)用管理里把相關(guān)權(quán)限打開    
                    }

ContextCompat.checkSelfPermission無效的問題

在做項(xiàng)目中發(fā)現(xiàn),我在使用ContextCompat.checkSelfPermission時(shí)莉掂,無論如何開關(guān)權(quán)限返回值都是PackageManager.PERMISSION_GRANTED葛圃,而使用PackageManager.checkPermission()的時(shí)候返回值又始終都是PackageManager.PERMISSION_DENIED;
經(jīng)過查詢相關(guān)文檔及博客發(fā)現(xiàn):

**If your application is targeting an API level before 23 (Android M) then both:ContextCompat.CheckSelfPermission and Context.checkSelfPermission doesn't work and always returns 0 (PERMISSION_GRANTED). Even if you run the application on Android 6.0 (API 23).
**
在targetSdkVersion小于23(Android M)的時(shí)候,ContextCompat.CheckSelfPermission 和Context.checkSelfPermission方法都不能正常工作并且始終返0(PERMISSION_GRANTED),即使你的應(yīng)用運(yùn)行在Android6.0(API 23)的設(shè)備上库正。

解決辦法:

As I said in the 1st point, if you targeting an API level before 23 on Android 6.0 then ContextCompat.CheckSelfPermission and Context.checkSelfPermission doesn't work. Fortunately you can use PermissionChecker.checkSelfPermission to check run-time permissions.
使用permissionChecker.checkSelfPermission,來檢測(cè)權(quán)限是否被授予曲楚。

關(guān)于權(quán)限android.permission.READ_PHONE_STATE和 WRITE_EXTERNAL_STORAGE/READ_EXTERNAL_STORAGE問題

參考博文
以上兩個(gè)權(quán)限對(duì)應(yīng)用運(yùn)行時(shí)影響最大,其中READ_PHONE_STATE用來獲取deviceID褥符,即IMEI號(hào)碼龙誊。這是很多統(tǒng)計(jì)依賴計(jì)算設(shè)備唯一ID的參考。如果新的權(quán)限導(dǎo)致讀取不到属瓣,避免導(dǎo)致統(tǒng)計(jì)的異常载迄。建議在完全支持運(yùn)行時(shí)權(quán)限之前,將對(duì)應(yīng)的值寫入到App本地?cái)?shù)據(jù)中抡蛙,對(duì)于新安裝的护昧,可以采取其他策略減少對(duì)統(tǒng)計(jì)的影響。

WRITE_EXTERNAL_STORAGE/READ_EXTERNAL_STORAGE這兩個(gè)權(quán)限和外置存儲(chǔ)(即sdcard)有關(guān)粗截,對(duì)于下載相關(guān)的應(yīng)用這一點(diǎn)還是比較重要的惋耙,我們應(yīng)該盡可能的說明和引導(dǎo)用戶授予該權(quán)限。

關(guān)于權(quán)限android.permission.READ_PHONE_STATE熊昌,系統(tǒng)會(huì)彈出一個(gè)對(duì)話框提醒撤銷的危害绽榛,如果用戶執(zhí)意撤銷,會(huì)帶來如下的反應(yīng)
如果你的程序正在運(yùn)行婿屹,則會(huì)被殺掉灭美。
當(dāng)你的應(yīng)用再次運(yùn)行時(shí),可能出現(xiàn)崩潰
為什么會(huì)可能崩潰的昂利,比如下面這段代碼

    TelephonyManager telephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
    String deviceId = telephonyManager.getDeviceId();
    if (deviceId.equals(mLastDeviceId)) {//This may cause NPE
      //do something
    }

如果用戶撤消了獲取DeviceId的權(quán)限届腐,那么再次運(yùn)行時(shí),deviceId就是null,如果程序后續(xù)處理不當(dāng)蜂奸,就會(huì)出現(xiàn)崩潰犁苏。
目前我在項(xiàng)目中做了版本控制,當(dāng)版本較低是還是用老的方法扩所,做了向下兼容围详。

service與動(dòng)態(tài)權(quán)限管理兼容問題

requestPermission()can only be called from an Activity and not a Service (unlike checkPermission() that only requires PackageManager). So you need to do some extra work to get around that; you do need to provide an Activity in your app and, for example, your Service can check for permissions it needs and if they have not been granted yet, it can create a notification and that can inform user with a descriptive short message as to why there is a notification and what needs to happen when they click on the notification, etc.

requestPermission()需要用戶提供Activity,在service里使用存在問題祖屏,可以在service里面先執(zhí)行checkPermission方法助赞,判斷是否授予權(quán)限,鄙人技術(shù)有限沒能解決這個(gè)問題赐劣,不過有技術(shù)大牛實(shí)現(xiàn)相關(guān)問題GitHub傳送門有興趣的可以下載先來研究研究源碼,這里我先做下相關(guān)備注以便后面學(xué)習(xí)嫉拐。

部分手機(jī)兼容存在禁止權(quán)限卻始終返回PERMISSION_GRANTED

這種情況部分手機(jī)解決方案跟上述ContextCompat.checkSelfPermission無效問題類似,先設(shè)置targetSdkVersion>=23魁兼,再設(shè)置ContextCompat.checkSelfPermission()改為permissionChecker.checkSelfPermission()方法婉徘,來檢測(cè)是否授予權(quán)限漠嵌。
但是針對(duì)魅族、小米手機(jī)盖呼,還是存在無效問題儒鹿,這就需要對(duì)應(yīng)的處理,還去大佬指教……

BaseActivity部分核心代碼

1几晤、先檢測(cè)權(quán)限是否授予

 * 檢測(cè)所有的權(quán)限是否都已授權(quán)
 * 
 * @param permissions
 * @return
 */
private boolean checkPermissions(String[] permissions) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
        return true;
    }
    for (String permission : permissions) {
        if (PermissionChecker.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
            return false;
        }
    }
    return true;

}

2约炎、獲取權(quán)限集合中權(quán)限列表

 * 獲取權(quán)限集中需要申請(qǐng)權(quán)限的列表
 * 
 * @param permissions
 * @return
 */
private List<String> getDeniedPermissions(String[] permissions) {
    List<String> needRequestPermissionList = new ArrayList<>();
    for (String permission : permissions) {
        if (PermissionChecker.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED
                || ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) {
            needRequestPermissionList.add(permission);
        }
    }
    return needRequestPermissionList;
}

3、請(qǐng)求所需權(quán)限

 * 請(qǐng)求權(quán)限
 * 
 * @param permissions
 *            請(qǐng)求的權(quán)限
 * @param requestCode
 *            請(qǐng)求權(quán)限的請(qǐng)求碼
 */
public void requestPermission(String[] permissions, int requestCode) {
    this.REQUEST_CODE_PERMISSION = requestCode;
    if (checkPermissions(permissions)) {
        permissionSuccess(REQUEST_CODE_PERMISSION);
    } else {
        List<String> needPermissions = getDeniedPermissions(permissions);
        ActivityCompat.requestPermissions(this, needPermissions.toArray(new String[needPermissions.size()]),
                REQUEST_CODE_PERMISSION);
    }
}

4蟹瘾、確定所有權(quán)限是否都已授權(quán)

 * 確認(rèn)所有的權(quán)限是否都已授權(quán)
 * 
 * @param grantResults
 * @return
 */
private boolean verifyPermissions(int[] grantResults) {
    for (int grantResult : grantResults) {
        if (grantResult != PackageManager.PERMISSION_GRANTED) {
            return false;
        }
    }
    return true;
}

5圾浅、系統(tǒng)請(qǐng)求權(quán)限回調(diào)執(zhí)行對(duì)應(yīng)的操作

 * 系統(tǒng)請(qǐng)求權(quán)限回調(diào)
 * 
 * @param requestCode
 * @param permissions
 * @param grantResults
 */
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
        @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_CODE_PERMISSION) {
        if (verifyPermissions(grantResults)) {
            permissionSuccess(REQUEST_CODE_PERMISSION);
        } else {
            permissionFail(REQUEST_CODE_PERMISSION);
        }
    }
}

6、子類繼承實(shí)現(xiàn)方法執(zhí)行相應(yīng)的操作

 * 獲取權(quán)限成功
 * 
 * @param requestCode
 */
public void permissionSuccess(int requestCode) {
    Log.e("TAG", "獲取權(quán)限成功=" + requestCode);

}

/**
 * 權(quán)限獲取失敗
 * 
 * @param requestCode
 */
public void permissionFail(int requestCode) {
    Log.e("TAG", "獲取權(quán)限失敗=" + requestCode);
}

歡迎各位大佬給予寶貴意見……

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末憾朴,一起剝皮案震驚了整個(gè)濱河市狸捕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌众雷,老刑警劉巖灸拍,帶你破解...
    沈念sama閱讀 206,013評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異砾省,居然都是意外死亡鸡岗,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門编兄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來轩性,“玉大人,你說我怎么就攤上這事狠鸳∨谝蹋” “怎么了?”我有些...
    開封第一講書人閱讀 152,370評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵碰煌,是天一觀的道長。 經(jīng)常有香客問我绅作,道長芦圾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,168評(píng)論 1 278
  • 正文 為了忘掉前任俄认,我火速辦了婚禮个少,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘眯杏。我一直安慰自己夜焦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,153評(píng)論 5 371
  • 文/花漫 我一把揭開白布岂贩。 她就那樣靜靜地躺著茫经,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上卸伞,一...
    開封第一講書人閱讀 48,954評(píng)論 1 283
  • 那天抹镊,我揣著相機(jī)與錄音,去河邊找鬼荤傲。 笑死垮耳,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的遂黍。 我是一名探鬼主播终佛,決...
    沈念sama閱讀 38,271評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼雾家!你這毒婦竟也來了铃彰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,916評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后恕汇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體评汰,經(jīng)...
    沈念sama閱讀 43,382評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,877評(píng)論 2 323
  • 正文 我和宋清朗相戀三年捡偏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,989評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡霜浴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蓝纲,到底是詐尸還是另有隱情阴孟,我是刑警寧澤,帶...
    沈念sama閱讀 33,624評(píng)論 4 322
  • 正文 年R本政府宣布税迷,位于F島的核電站永丝,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏箭养。R本人自食惡果不足惜慕嚷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,209評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望毕泌。 院中可真熱鬧喝检,春花似錦、人聲如沸撼泛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽愿题。三九已至损俭,卻和暖如春蛙奖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背撩炊。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評(píng)論 1 260
  • 我被黑心中介騙來泰國打工外永, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拧咳。 一個(gè)月前我還...
    沈念sama閱讀 45,401評(píng)論 2 352
  • 正文 我出身青樓伯顶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親骆膝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子祭衩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,700評(píng)論 2 345

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