Android 6.0終於是千呼萬喚始出來了,除了動畫更加酷炫之外,也有很多吊炸天的功能,比如新增加的權限管理,看完它,作爲一個程序猿,第一感覺就是,真是日了狗了!
在android 6以前,程序在manifest聲明的權限,在用戶安裝程序的時候,就已經展示出來並全部賦予權限了,這種權限管理的特徵就是隻在安裝時顯示一次,管你同不同意,只要你安裝了,就代表你同意了。因此我敢吃鍵盤來賭你安裝的時候從來沒有看那些權限列表。
雖然對用戶來說很坑爹,但是對程序猿來說就是福利,因爲自android 6之後,部分權限已經不允許這樣了,除了以前正常的編碼之後,還需要添加對Dangerous permissions的權限請求,google怕你偷取用戶數據幹一些見不得人的事情,可謂是用心良苦啊
附上Dangerous permissions及它所屬權限組預覽表
雖然上面的權限需要另外在代碼中請求授權,但是同一組的權限是相通的,比如用戶給了你READ_CLAENDAR權限,你就獲得了CALENDAR權限組下的所有權限。
在此之前,說一說android6.0在設置的應用界面增加了一個權限管理功能,打開如下
說明了我們已經可以手動管理一些危險的權限了,想想當我們關閉這個權限的時候,當我執行到需要這個權限的代碼的時候回怎麼樣,有兩種情況
- 崩潰
- 返回值爲null或者0。
爲什麼會有兩種情況,在target SDK22(android 5.1)以前,根本就沒有這個權限管理啊,要是你關了權限直接導致程序奔潰了,那不是gg了,所以官方爲了廣大用戶着想,當你設置了target SDK爲22以下時,直接返回空(但是如果你沒在程序中考慮到返回空值的情況而不處理的話還是有可能會導致崩潰的)
直接的情況自然就是target SDK23以上了,簡單暴力,使我們不得不在代碼裏添加請求權限
權限功能是在android6.0纔有,請時刻想象你的手機運行棉花糖系統
在6.0手機上運行程序時,如果你是定義target SDK22以下的時候,記得這些權限默認是開啓的,只有你手賤關閉的時候纔會返回空;而在target SDK23以上的時候,默認是關閉的,如果你不處理,就直接程序崩潰
綜上所述,不管你設置target SDK是多少,只要想在android6.0下運行程序,最好都要添加權限請求。
廢話了那麼多,終於要說正事了,到底是如何在代碼裏管理權限的呢,請大家洗耳恭聽。
在本次示例中,我做了一個圖片選擇器,用到了READ_EXTERNAL_STORAGE,屬於STORAGE權限組,要想在6.0下運行,我要在代碼中對READ_EXTERNAL_STORAGE或者WRITE_EXTERNAL_STORAGE授權。
因爲權限管理類在23纔有,所以我用了support v4包
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//初始化所有圖片文件目錄</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">initDirectory</span>() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> checkSelfPermission = ContextCompat.checkSelfPermission(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>, Manifest.permission.WRITE_EXTERNAL_STORAGE); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>(checkSelfPermission != PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>,<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_STORAGE_PERMISSION); }<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>{ <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//有權限了,獲取圖片數據</span> getCameraData(); } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul>
上面代碼很簡單,首先先查詢指定的權限有沒有被授權,如果有則直接獲取數據,如果沒有的話,調用requestPermissions請求獲取權限,上面的權限參數是String數組,說明可以同時請求多個授權。
執行之後,系統啓動個dialog詢問用戶.
既然請求了,那應該就有回調。我們需要在該類下重寫onRequestPermissionsResult方法
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onRequestPermissionsResult</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> requestCode, String[] permissions, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>[] grantResults) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">switch</span> (requestCode){ <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> REQUEST_STORAGE_PERMISSION: <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>(grantResults[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>] == PackageManager.PERMISSION_GRANTED){ <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//獲取數據</span> getCameraData(); }<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>{ Toast.makeText(PhotoPickActivity.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"獲取權限失敗"</span>,Toast.LENGTH_SHORT).show(); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onRequestPermissionsResult(requestCode, permissions, grantResults); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li></ul>
用戶運行和拒絕的界面都如下所示,只要有請求權限,就算拒絕了也不會崩潰,而是返回null或0.這麼簡單就不說了吧
一般來說,當用戶覺得這個權限請求很莫名其妙的時候(比如顯示圖片的時候突然請求獲取location權限),通常會不給授權。於是google提供了ActivityCompat.shouldShowRequestPermissionRationale來給哪些莫名其妙的權限提供一個解釋自身清白的機會。
當shouldShowRequestPermissionRationale方法返回true,就是建議你給他一個解釋的時候了。一般第一次彈出請求權限框的時候(即沒有”不在詢問”選項框的時候)返回false.
當第一次拒絕之後,系統覺得你拒絕它是有原因的,因此接下來的的彈出框(即有”不在詢問”選項框的時候)都返回true
勾選不在詢問或者授權成功情況下都返回false
接下來就看看他是怎麼使用的
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">initDirectory</span>() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> checkSelfPermission = ContextCompat.checkSelfPermission(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>, Manifest.permission.WRITE_EXTERNAL_STORAGE); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>(checkSelfPermission != PackageManager.PERMISSION_GRANTED){ <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (ActivityCompat.shouldShowRequestPermissionRationale(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>, Manifest.permission.READ_EXTERNAL_STORAGE)) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> AlertDialog.Builder(PhotoPickActivity.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>) .setMessage(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"你需要啓動權限WRITE_EXTERNAL_STORAGE來獲取圖片"</span>) .setPositiveButton(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"OK"</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> DialogInterface.OnClickListener() { <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onClick</span>(DialogInterface dialog, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> which) { ActivityCompat.requestPermissions(PhotoPickActivity.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_STORAGE_PERMISSION); } }) .setNegativeButton(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Cancel"</span>,<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) .create() .show(); }<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>{ ActivityCompat.requestPermissions(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>,<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_STORAGE_PERMISSION); } }<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>{ getCameraData(); } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li></ul>
基本的分析寫完了,最後在總結一下
- 當你還沒寫好這些權限請求時,千萬不要把target sdk設置爲23以上,不然用戶就悲劇了,總不能叫他去設置裏面開啓權限吧
- 養成支持權限的習慣,說不定用戶哪天就關了那個重要的權限而忘了,你就可以貼心提醒它了