亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android運(yùn)行時(shí)權(quán)限終極方案(PermissionX)

 更新時(shí)間:2020年05月19日 11:21:15   作者:guolin  
這篇文章主要介紹了Android運(yùn)行時(shí)權(quán)限終極方案(PermissionX),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

各位小伙伴們大家早上好,不知道你的《第三行代碼》已經(jīng)讀到哪里了?

有些朋友的閱讀速度真是令人印象深刻,我記得在《第三行代碼》剛剛發(fā)售一周不到的時(shí)間里,竟然就有人已經(jīng)讀到第9章了(因?yàn)楣娞?hào)后臺(tái)有人回復(fù)第9章里隱藏的關(guān)鍵字)?,F(xiàn)在,《第三行代碼》已經(jīng)出版一個(gè)月有余了,相信已經(jīng)有不少朋友將全本書(shū)都看完了。

全書(shū)都看完的朋友一定知道,《第三行代碼》的最后一章是帶著大家一起開(kāi)發(fā)了一個(gè)開(kāi)源庫(kù):PermissionX。這一章的主旨是為了讓你了解一個(gè)開(kāi)源庫(kù)整體的開(kāi)發(fā)與發(fā)布過(guò)程,為了更好地演示這個(gè)過(guò)程,我想到了去寫(xiě)PermissionX這樣一個(gè)庫(kù)。

不過(guò),書(shū)中PermissionX庫(kù)的整體功能還是比較簡(jiǎn)單的,因?yàn)檫@一章的重點(diǎn)不在于如何將開(kāi)源庫(kù)做得完善與強(qiáng)大,而是強(qiáng)調(diào)的一個(gè)開(kāi)發(fā)與發(fā)布的過(guò)程。

但是后來(lái),我覺(jué)得PermissionX確實(shí)可以做成一個(gè)真正用于簡(jiǎn)化Android運(yùn)行時(shí)權(quán)限處理的庫(kù),它所存在的意義應(yīng)該不僅限于書(shū)中的教學(xué)目的,而是可以真的應(yīng)用到實(shí)際的項(xiàng)目當(dāng)中,幫助大家解決處理運(yùn)行時(shí)權(quán)限的痛點(diǎn)。

所以,后期我又對(duì)PermissionX進(jìn)行了諸多功能拓展,現(xiàn)在已經(jīng)達(dá)到對(duì)外發(fā)布的標(biāo)準(zhǔn)了,那么今天正式向大家宣布:PermissionX已經(jīng)上線!

源碼庫(kù)地址是:https://github.com/guolindev/PermissionX

痛點(diǎn)在哪里?

沒(méi)有人愿意編寫(xiě)處理Android運(yùn)行時(shí)權(quán)限的代碼,因?yàn)樗娴奶爆嵙恕?/p>

這是一項(xiàng)沒(méi)有什么技術(shù)含量,但是你又不得不去處理的工作,因?yàn)椴惶幚硭绦蚓蜁?huì)崩潰。但如果處理起來(lái)比較簡(jiǎn)單也就算了,可事實(shí)上,Android提供給我們的運(yùn)行時(shí)權(quán)限API并不友好。

以一個(gè)撥打電話的功能為例,因?yàn)镃ALL_PHONE權(quán)限是危險(xiǎn)權(quán)限,所以在我們除了要在AndroidManifest.xml中聲明權(quán)限之外,還要在執(zhí)行撥打電話操作之前進(jìn)行運(yùn)行時(shí)權(quán)限處理才行。

權(quán)限聲明如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.permissionx.app">

  <uses-permission android:name="android.permission.CALL_PHONE" />
	...
	
</manifest>

然后,編寫(xiě)如下代碼來(lái)進(jìn)行運(yùn)行時(shí)權(quán)限處理:

class MainActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    makeCallBtn.setOnClickListener {
      if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
        call()
      } else {
        ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CALL_PHONE), 1)
      }
    }
  }

  override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    when (requestCode) {
      1 -> {
        if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
          call()
        } else {
          Toast.makeText(this, "You denied CALL_PHONE permission", Toast.LENGTH_SHORT).show()
        }
      }
    }
  }

  private fun call() {
    try {
      val intent = Intent(Intent.ACTION_CALL)
      intent.data = Uri.parse("tel:10086")
      startActivity(intent)
    } catch (e: SecurityException) {
      e.printStackTrace()
    }
  }

}

這段代碼中真有正意義的功能邏輯就是call()方法中的內(nèi)容,可是如果直接調(diào)用call()方法是無(wú)法實(shí)現(xiàn)撥打電話功能的,因?yàn)槲覀冞€沒(méi)有申請(qǐng)CALL_PHONE權(quán)限。

那么整段代碼其他的部分就都是在處理CALL_PHONE權(quán)限申請(qǐng)??梢钥吹?,這里需要先判斷用戶(hù)是否已授權(quán)我們撥打電話的權(quán)限,如果沒(méi)有的話則要進(jìn)行權(quán)限申請(qǐng),然后還要在onRequestPermissionsResult()回調(diào)中處理權(quán)限申請(qǐng)的結(jié)果,最后才能去執(zhí)行撥打電話的操作。

你可能覺(jué)得,這也不算是很繁瑣呀,代碼量并不是很多。那是因?yàn)?,目前我們還只是處理了運(yùn)行時(shí)權(quán)限最簡(jiǎn)單的場(chǎng)景,而實(shí)際的項(xiàng)目環(huán)境中有著更加復(fù)雜的場(chǎng)景在等著我們。

比如說(shuō),你的App可能并不只是單單申請(qǐng)一個(gè)權(quán)限,而是需要同時(shí)申請(qǐng)多個(gè)權(quán)限。雖然ActivityCompat.requestPermissions()方法允許一次性傳入多個(gè)權(quán)限名,但是你在onRequestPermissionsResult()回調(diào)中就需要判斷哪些權(quán)限被允許了,哪些權(quán)限被拒絕了,被拒絕的權(quán)限是否影響到應(yīng)用程序的核心功能,以及是否要再次申請(qǐng)權(quán)限。

而一旦牽扯到再次申請(qǐng)權(quán)限,就引出了一個(gè)更加復(fù)雜的問(wèn)題。你申請(qǐng)的權(quán)限被用戶(hù)拒絕過(guò)了一次,那么再次申請(qǐng)將很有可能再次被拒絕。為此,Android提供了一個(gè)shouldShowRequestPermissionRationale()方法,用于判斷是否需要向用戶(hù)解釋申請(qǐng)這個(gè)權(quán)限的原因,一旦shouldShowRequestPermissionRationale()方法返回true,那么我們最好彈出一個(gè)對(duì)話框來(lái)向用戶(hù)闡明為什么我們是需要這個(gè)權(quán)限的,這樣可以增加用戶(hù)同意授權(quán)的幾率。

是不是已經(jīng)覺(jué)得很復(fù)雜了?不過(guò)還沒(méi)完,Android系統(tǒng)還提供了一個(gè)“拒絕,不要再詢(xún)問(wèn)”的選項(xiàng),如下圖所示:

只要用戶(hù)選擇了這個(gè)選項(xiàng),那么我們以后每次執(zhí)行權(quán)限申請(qǐng)的代碼都將會(huì)直接被拒絕。

可是如果我的某項(xiàng)功能就是必須要依賴(lài)這個(gè)權(quán)限才行呢?沒(méi)有辦法,你只能提示用戶(hù)去應(yīng)用程序設(shè)置當(dāng)中手動(dòng)打開(kāi)權(quán)限,程序方面已無(wú)法進(jìn)行操作。

可以看出,如果想要在項(xiàng)目中對(duì)運(yùn)行時(shí)權(quán)限做出非常全面的處理,是一件相當(dāng)復(fù)雜的事情。事實(shí)上,大部分的項(xiàng)目都沒(méi)有將權(quán)限申請(qǐng)這塊處理得十分恰當(dāng),這也是我編寫(xiě)PermissionX的理由。

PermissionX的實(shí)現(xiàn)原理

在開(kāi)始介紹PermissionX的具體用法之前,我們先來(lái)討論一下它的實(shí)現(xiàn)原理。

其實(shí)之前并不是沒(méi)有人嘗試過(guò)對(duì)運(yùn)行時(shí)權(quán)限處理進(jìn)行封裝,我之前在做直播公開(kāi)課的時(shí)候也向大家演示過(guò)一種運(yùn)行時(shí)權(quán)限API的封裝過(guò)程。

但是,想要對(duì)運(yùn)行時(shí)權(quán)限的API進(jìn)行封裝并不是一件容易的事,因?yàn)檫@個(gè)操作是有特定的上下文依賴(lài)的,一般需要在Activity中接收onRequestPermissionsResult()方法的回調(diào)才行,所以不能簡(jiǎn)單地將整個(gè)操作封裝到一個(gè)獨(dú)立的類(lèi)中。

為此,也衍生出了一系列特殊的封裝方案,比如將運(yùn)行時(shí)權(quán)限的操作封裝到BaseActivity中,或者提供一個(gè)透明的Activity來(lái)處理運(yùn)行時(shí)權(quán)限等。

不過(guò)上述兩種方案都不夠輕量,因?yàn)楦淖傾ctivity的繼承結(jié)構(gòu)這可是大事情,而提供一個(gè)透明的Activty則需要在AndroidManifest.xml中進(jìn)行額外的聲明。

現(xiàn)在,業(yè)內(nèi)普遍比較認(rèn)可使用另外一種小技巧來(lái)進(jìn)行實(shí)現(xiàn)。是什么小技巧呢?回想一下,之前所有申請(qǐng)運(yùn)行時(shí)權(quán)限的操作都是在Activity中進(jìn)行的,事實(shí)上,Android在Fragment中也提供了一份相同的API,使得我們?cè)贔ragment中也能申請(qǐng)運(yùn)行時(shí)權(quán)限。

但不同的是,F(xiàn)ragment并不像Activity那樣必須有界面,我們完全可以向Activity中添加一個(gè)隱藏的Fragment,然后在這個(gè)隱藏的Fragment中對(duì)運(yùn)行時(shí)權(quán)限的API進(jìn)行封裝。這是一種非常輕量級(jí)的做法,不用擔(dān)心隱藏Fragment會(huì)對(duì)Activity的性能造成什么影響。

這就是PermissionX的實(shí)現(xiàn)原理了,書(shū)中其實(shí)也已經(jīng)介紹過(guò)了這部分內(nèi)容。但是,在其實(shí)現(xiàn)原理的基礎(chǔ)之上,后期我又增加了很多新功能,讓PermissionX變得更加強(qiáng)大和好用,下面我們就來(lái)學(xué)習(xí)一下PermissionX的具體用法。

基本用法

要使用PermissionX之前,首先需要將其引入到項(xiàng)目當(dāng)中,如下所示:

dependencies {
	...
	implementation 'com.permissionx.guolindev:permissionx:1.1.1'
} 

我在寫(xiě)本篇文章時(shí)PermissionX的最新版本是1.1.1,想要查看它的當(dāng)前最新版本,請(qǐng)?jiān)L問(wèn)PermissionX的主頁(yè):https://github.com/guolindev/PermissionX

PermissionX的目的是為了讓運(yùn)行時(shí)權(quán)限處理盡可能的容易,因此怎么讓API變得簡(jiǎn)單好用就是我優(yōu)先要考慮的問(wèn)題。

比如同樣實(shí)現(xiàn)撥打電話的功能,使用PermissionX只需要這樣寫(xiě):

class MainActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    makeCallBtn.setOnClickListener {
      PermissionX.init(this)
        .permissions(Manifest.permission.CALL_PHONE)
        .request { allGranted, grantedList, deniedList ->
          if (allGranted) {
            call()
          } else {
            Toast.makeText(this, "您拒絕了撥打電話權(quán)限", Toast.LENGTH_SHORT).show()
          }
        }
    }
  }

  ...

}

是的,PermissionX的基本用法就這么簡(jiǎn)單。首先調(diào)用init()方法來(lái)進(jìn)行初始化,并在初始化的時(shí)候傳入一個(gè)FragmentActivity參數(shù)。由于AppCompatActivity是FragmentActivity的子類(lèi),所以只要你的Activity是繼承自AppCompatActivity的,那么直接傳入this就可以了。

接下來(lái)調(diào)用permissions()方法傳入你要申請(qǐng)的權(quán)限名,這里傳入CALL_PHONE權(quán)限。你也可以在permissions()方法中傳入任意多個(gè)權(quán)限名,中間用逗號(hào)隔開(kāi)即可。

最后調(diào)用request()方法來(lái)執(zhí)行權(quán)限申請(qǐng),并在Lambda表達(dá)式中處理申請(qǐng)結(jié)果。可以看到,Lambda表達(dá)式中有3個(gè)參數(shù):allGranted表示是否所有申請(qǐng)的權(quán)限都已被授權(quán),grantedList用于記錄所有已被授權(quán)的權(quán)限,deniedList用于記錄所有被拒絕的權(quán)限。

因?yàn)槲覀冎簧暾?qǐng)了一個(gè)CALL_PHONE權(quán)限,因此這里直接判斷:如果allGranted為true,那么就調(diào)用call()方法,否則彈出一個(gè)Toast提示。

運(yùn)行結(jié)果如下:

怎么樣?對(duì)比之前的寫(xiě)法,是不是覺(jué)得運(yùn)行時(shí)權(quán)限處理沒(méi)那么繁瑣了?

核心用法

然而我們目前還只是處理了最普通的場(chǎng)景,剛才提到的,假如用戶(hù)拒絕了某個(gè)權(quán)限,在下次申請(qǐng)之前,我們最好彈出一個(gè)對(duì)話框來(lái)向用戶(hù)解釋申請(qǐng)這個(gè)權(quán)限的原因,這個(gè)又該怎么實(shí)現(xiàn)呢?

別擔(dān)心,PermissionX對(duì)這些情況進(jìn)行了充分的考慮。

onExplainRequestReason()方法可以用于監(jiān)聽(tīng)那些被用戶(hù)拒絕,而又可以再次去申請(qǐng)的權(quán)限。從方法名上也可以看出來(lái)了,應(yīng)該在這個(gè)方法中解釋申請(qǐng)這些權(quán)限的原因。

而我們只需要將onExplainRequestReason()方法串接到request()方法之前即可,如下所示:

PermissionX.init(this)
  .permissions(Manifest.permission.CAMERA, Manifest.permission.READ_CONTACTS, Manifest.permission.CALL_PHONE)
  .onExplainRequestReason { deniedList ->
  }
  .request { allGranted, grantedList, deniedList ->
    if (allGranted) {
      Toast.makeText(this, "所有申請(qǐng)的權(quán)限都已通過(guò)", Toast.LENGTH_SHORT).show()
    } else {
      Toast.makeText(this, "您拒絕了如下權(quán)限:$deniedList", Toast.LENGTH_SHORT).show()
    }
  }

這種情況下,所有被用戶(hù)拒絕的權(quán)限會(huì)優(yōu)先進(jìn)入onExplainRequestReason()方法進(jìn)行處理,拒絕的權(quán)限都記錄在deniedList參數(shù)當(dāng)中。接下來(lái),我們只需要在這個(gè)方法中調(diào)用showRequestReasonDialog()方法,即可彈出解釋權(quán)限申請(qǐng)?jiān)虻膶?duì)話框,如下所示:

PermissionX.init(this)
  .permissions(Manifest.permission.CAMERA, Manifest.permission.READ_CONTACTS, Manifest.permission.CALL_PHONE)
  .onExplainRequestReason { deniedList ->
    showRequestReasonDialog(deniedList, "即將重新申請(qǐng)的權(quán)限是程序必須依賴(lài)的權(quán)限", "我已明白", "取消")
  }
  .request { allGranted, grantedList, deniedList ->
    if (allGranted) {
      Toast.makeText(this, "所有申請(qǐng)的權(quán)限都已通過(guò)", Toast.LENGTH_SHORT).show()
    } else {
      Toast.makeText(this, "您拒絕了如下權(quán)限:$deniedList", Toast.LENGTH_SHORT).show()
    }
  }

showRequestReasonDialog()方法接受4個(gè)參數(shù):第一個(gè)參數(shù)是要重新申請(qǐng)的權(quán)限列表,這里直接將deniedList參數(shù)傳入。第二個(gè)參數(shù)則是要向用戶(hù)解釋的原因,我只是隨便寫(xiě)了一句話,這個(gè)參數(shù)描述的越詳細(xì)越好。第三個(gè)參數(shù)是對(duì)話框上確定按鈕的文字,點(diǎn)擊該按鈕后將會(huì)重新執(zhí)行權(quán)限申請(qǐng)操作。第四個(gè)參數(shù)是一個(gè)可選參數(shù),如果不傳的話相當(dāng)于用戶(hù)必須同意申請(qǐng)的這些權(quán)限,否則對(duì)話框無(wú)法關(guān)閉,而如果傳入的話,對(duì)話框上會(huì)有一個(gè)取消按鈕,點(diǎn)擊取消后不會(huì)重新進(jìn)行權(quán)限申請(qǐng),而是會(huì)把當(dāng)前的申請(qǐng)結(jié)果回調(diào)到request()方法當(dāng)中。

另外始終要記得將所有申請(qǐng)的權(quán)限都在AndroidManifest.xml中進(jìn)行聲明:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.permissionx.app">

  <uses-permission android:name="android.permission.READ_CONTACTS" />
  <uses-permission android:name="android.permission.CAMERA" />
  <uses-permission android:name="android.permission.CALL_PHONE" />
	...
	
</manifest>

重新運(yùn)行一下程序,效果如下圖所示:

目前解釋權(quán)限申請(qǐng)?jiān)驅(qū)υ捒虻臉邮綍簳r(shí)還無(wú)法自定義,下個(gè)版本當(dāng)中,我會(huì)加入自定義對(duì)話框樣式的功能。

當(dāng)然,我們也可以指定要對(duì)哪些權(quán)限重新申請(qǐng),比如上述申請(qǐng)的3個(gè)權(quán)限中,我認(rèn)為CAMERA權(quán)限是必不可少的,而其他兩個(gè)權(quán)限則可有可無(wú),那么在重新申請(qǐng)的時(shí)候也可以只申請(qǐng)CAMERA權(quán)限:

PermissionX.init(this)
  .permissions(Manifest.permission.CAMERA, Manifest.permission.READ_CONTACTS, Manifest.permission.ACCESS_FINE_LOCATION)
  .onExplainRequestReason { deniedList ->
    val filteredList = deniedList.filter {
      it == Manifest.permission.CAMERA
    }
    showRequestReasonDialog(filteredList, "攝像機(jī)權(quán)限是程序必須依賴(lài)的權(quán)限", "我已明白", "取消")
  }
  .request { allGranted, grantedList, deniedList ->
    if (allGranted) {
      Toast.makeText(this, "所有申請(qǐng)的權(quán)限都已通過(guò)", Toast.LENGTH_SHORT).show()
    } else {
      Toast.makeText(this, "您拒絕了如下權(quán)限:$deniedList", Toast.LENGTH_SHORT).show()
    }
  }

這樣當(dāng)再次申請(qǐng)權(quán)限的時(shí)候就只會(huì)申請(qǐng)CAMERA權(quán)限,剩下的兩個(gè)權(quán)限最終會(huì)被傳入到request()方法的deniedList參數(shù)當(dāng)中。

解決了向用戶(hù)解釋權(quán)限申請(qǐng)?jiān)虻膯?wèn)題,接下來(lái)還有一個(gè)頭疼的問(wèn)題要解決:如果用戶(hù)不理會(huì)我們的解釋?zhuān)匀粓?zhí)意拒絕權(quán)限申請(qǐng),并且還選擇了拒絕且不再詢(xún)問(wèn)的選項(xiàng),這該怎么辦?通常這種情況下,程序?qū)用嬉呀?jīng)無(wú)法再次做出權(quán)限申請(qǐng),唯一能做的就是提示用戶(hù)到應(yīng)用程序設(shè)置當(dāng)中手動(dòng)打開(kāi)權(quán)限。

那么PermissionX是如何處理這種情況的呢?我相信絕對(duì)會(huì)給你帶來(lái)驚喜。PermissionX中還提供了一個(gè)onForwardToSettings()方法,專(zhuān)門(mén)用于監(jiān)聽(tīng)那些被用戶(hù)永久拒絕的權(quán)限。另外從方法名上就可以看出,我們可以在這里提醒用戶(hù)手動(dòng)去應(yīng)用程序設(shè)置當(dāng)中打開(kāi)權(quán)限。代碼如下所示:

PermissionX.init(this)
  .permissions(Manifest.permission.CAMERA, Manifest.permission.READ_CONTACTS, Manifest.permission.CALL_PHONE)
  .onExplainRequestReason { deniedList ->
    showRequestReasonDialog(deniedList, "即將重新申請(qǐng)的權(quán)限是程序必須依賴(lài)的權(quán)限", "我已明白", "取消")
  }
  .onForwardToSettings { deniedList ->
    showForwardToSettingsDialog(deniedList, "您需要去應(yīng)用程序設(shè)置當(dāng)中手動(dòng)開(kāi)啟權(quán)限", "我已明白", "取消")
  }
  .request { allGranted, grantedList, deniedList ->
    if (allGranted) {
      Toast.makeText(this, "所有申請(qǐng)的權(quán)限都已通過(guò)", Toast.LENGTH_SHORT).show()
    } else {
      Toast.makeText(this, "您拒絕了如下權(quán)限:$deniedList", Toast.LENGTH_SHORT).show()
    }
  }

可以看到,這里又串接了一個(gè)onForwardToSettings()方法,所有被用戶(hù)選擇了拒絕且不再詢(xún)問(wèn)的權(quán)限都會(huì)進(jìn)行到這個(gè)方法中處理,拒絕的權(quán)限都記錄在deniedList參數(shù)當(dāng)中。

接下來(lái),你并不需要自己彈出一個(gè)Toast或是對(duì)話框來(lái)提醒用戶(hù)手動(dòng)去應(yīng)用程序設(shè)置當(dāng)中打開(kāi)權(quán)限,而是直接調(diào)用showForwardToSettingsDialog()方法即可。類(lèi)似地,showForwardToSettingsDialog()方法也接收4個(gè)參數(shù),每個(gè)參數(shù)的作用和剛才的showRequestReasonDialog()方法完全一致,我這里就不再重復(fù)解釋了。

showForwardToSettingsDialog()方法將會(huì)彈出一個(gè)對(duì)話框,當(dāng)用戶(hù)點(diǎn)擊對(duì)話框上的我已明白按鈕時(shí),將會(huì)自動(dòng)跳轉(zhuǎn)到當(dāng)前應(yīng)用程序的設(shè)置界面,從而不需要用戶(hù)自己慢慢進(jìn)入設(shè)置當(dāng)中尋找當(dāng)前應(yīng)用了。另外,當(dāng)用戶(hù)從設(shè)置中返回時(shí),PermissionX將會(huì)自動(dòng)重新請(qǐng)求相應(yīng)的權(quán)限,并將最終的授權(quán)結(jié)果回調(diào)到request()方法當(dāng)中。效果如下圖所示:

同樣,下個(gè)版本當(dāng)中,我也會(huì)加入自定義這個(gè)對(duì)話框樣式的功能。

更多用法

PermissionX最主要的功能大概就是這些,不過(guò)我在使用一些App的時(shí)候發(fā)現(xiàn),有些App喜歡在第一次請(qǐng)求權(quán)限之前就先彈出一個(gè)對(duì)話框向用戶(hù)解釋自己需要哪些權(quán)限,然后才會(huì)進(jìn)行權(quán)限申請(qǐng)。這種做法是比較提倡的,因?yàn)橛脩?hù)同意授權(quán)的概率會(huì)更高。

那么PermissionX中要如何實(shí)現(xiàn)這樣的功能呢?

其實(shí)非常簡(jiǎn)單,PermissionX還提供了一個(gè)explainReasonBeforeRequest()方法,只需要將它也串接到request()方法之前就可以了,代碼如下所示:

PermissionX.init(this)
  .permissions(Manifest.permission.CAMERA, Manifest.permission.READ_CONTACTS, Manifest.permission.CALL_PHONE)
	.explainReasonBeforeRequest()
  .onExplainRequestReason { deniedList ->
    showRequestReasonDialog(deniedList, "即將申請(qǐng)的權(quán)限是程序必須依賴(lài)的權(quán)限", "我已明白")
  }
  .onForwardToSettings { deniedList ->
    showForwardToSettingsDialog(deniedList, "您需要去應(yīng)用程序設(shè)置當(dāng)中手動(dòng)開(kāi)啟權(quán)限", "我已明白")
  }
  .request { allGranted, grantedList, deniedList ->
    if (allGranted) {
      Toast.makeText(this, "所有申請(qǐng)的權(quán)限都已通過(guò)", Toast.LENGTH_SHORT).show()
    } else {
      Toast.makeText(this, "您拒絕了如下權(quán)限:$deniedList", Toast.LENGTH_SHORT).show()
    }
  }

這樣,當(dāng)每次請(qǐng)求權(quán)限時(shí),會(huì)優(yōu)先進(jìn)入onExplainRequestReason()方法,彈出解釋權(quán)限申請(qǐng)?jiān)虻膶?duì)話框,用戶(hù)點(diǎn)擊我已明白按鈕之后才會(huì)執(zhí)行權(quán)限申請(qǐng)。效果如下圖所示:

不過(guò),你在使用explainReasonBeforeRequest()方法時(shí),其實(shí)還有一些關(guān)鍵的點(diǎn)需要注意。

第一,單獨(dú)使用explainReasonBeforeRequest()方法是無(wú)效的,必須配合onExplainRequestReason()方法一起使用才能起作用。這個(gè)很好理解,因?yàn)闆](méi)有配置onExplainRequestReason()方法,我們?cè)趺聪蛴脩?hù)解釋權(quán)限申請(qǐng)?jiān)蚰兀?/p>

第二,在使用explainReasonBeforeRequest()方法時(shí),如果onExplainRequestReason()方法中編寫(xiě)了權(quán)限過(guò)濾的邏輯,最終的運(yùn)行結(jié)果可能和你期望的會(huì)不一致。這一點(diǎn)可能會(huì)稍微有點(diǎn)難理解,我用一個(gè)具體的示例來(lái)解釋一下。

觀察如下代碼:

PermissionX.init(this)
  .permissions(Manifest.permission.CAMERA, Manifest.permission.READ_CONTACTS, Manifest.permission.CALL_PHONE)
	.explainReasonBeforeRequest()
  .onExplainRequestReason { deniedList ->
    val filteredList = deniedList.filter {
      it == Manifest.permission.CAMERA
    }
    showRequestReasonDialog(filteredList, "攝像機(jī)權(quán)限是程序必須依賴(lài)的權(quán)限", "我已明白")
  }
  ...

這里在onExplainRequestReason()方法中編寫(xiě)了剛才用到的權(quán)限過(guò)濾邏輯,當(dāng)有多個(gè)權(quán)限被拒絕時(shí),我們只重新申請(qǐng)CAMERA權(quán)限。

在沒(méi)有加入explainReasonBeforeRequest()方法時(shí),一切都可以按照我們所預(yù)期的那樣正常運(yùn)行。但如果加上了explainReasonBeforeRequest()方法,在執(zhí)行權(quán)限請(qǐng)求之前會(huì)先進(jìn)入onExplainRequestReason()方法,而這里將除了CAMERA之外的其他權(quán)限都過(guò)濾掉了,因此實(shí)際上PermissionX只會(huì)請(qǐng)求CAMERA這一個(gè)權(quán)限,剩下的權(quán)限將完全不會(huì)嘗試去請(qǐng)求,而是直接作為被拒絕的權(quán)限回調(diào)到最終的request()方法當(dāng)中。

效果如下圖所示:

針對(duì)于這種情況,PermissionX在onExplainRequestReason()方法中提供了一個(gè)額外的beforeRequest參數(shù),用于標(biāo)識(shí)當(dāng)前上下文是在權(quán)限請(qǐng)求之前還是之后,借助這個(gè)參數(shù)在onExplainRequestReason()方法中執(zhí)行不同的邏輯,即可很好地解決這個(gè)問(wèn)題,示例代碼如下:

PermissionX.init(this)
  .permissions(Manifest.permission.CAMERA, Manifest.permission.READ_CONTACTS, Manifest.permission.CALL_PHONE)
	.explainReasonBeforeRequest()
  .onExplainRequestReason { deniedList, beforeRequest ->
    if (beforeRequest) {
      showRequestReasonDialog(deniedList, "為了保證程序正常工作,請(qǐng)您同意以下權(quán)限申請(qǐng)", "我已明白")
    } else {
      val filteredList = deniedList.filter {
        it == Manifest.permission.CAMERA
      }
      showRequestReasonDialog(filteredList, "攝像機(jī)權(quán)限是程序必須依賴(lài)的權(quán)限", "我已明白")
    }
  }
  ...

可以看到,當(dāng)beforeRequest為true時(shí),說(shuō)明此時(shí)還未執(zhí)行權(quán)限申請(qǐng),那么我們將完整的deniedList傳入showRequestReasonDialog()方法當(dāng)中。

而當(dāng)beforeRequest為false時(shí),說(shuō)明某些權(quán)限被用戶(hù)拒絕了,此時(shí)我們只重新申請(qǐng)CAMERA權(quán)限,因?yàn)樗潜夭豢缮俚模渌麢?quán)限則可有可無(wú)。

最終運(yùn)行效果如下:

Permission-Support

這個(gè)庫(kù)的名字叫PermissionX,因此不用多說(shuō),它肯定是與AndroidX兼容的。以防還有部分朋友不清楚AndroidX是什么的,這里有一篇我之前寫(xiě)的科普文章 總是聽(tīng)到有人說(shuō)AndroidX,到底什么是AndroidX

但是,我相信現(xiàn)在仍然存在很多項(xiàng)目沒(méi)有使用AndroidX,而是在繼續(xù)使用著之前的Android Support Library。為此,我又專(zhuān)門(mén)提供了一份面向Android Support Library的版本:Permission-Support。

在用法層面,兩個(gè)版本沒(méi)有任何區(qū)別,本文以上討論的所有內(nèi)容在Permission-Support上都適用。只是在引用庫(kù)的時(shí)候,如果你準(zhǔn)備使用Permission-Support,請(qǐng)使用以下依賴(lài)庫(kù)地址:

dependencies {
	...
	implementation 'com.permissionx.guolindev:permission-support:1.1.1'
} 

不過(guò),Android Support Library注定將會(huì)在不久的將來(lái)被Google完全淘汰,因此Permission-Support我也不會(huì)維護(hù)太久的時(shí)間,只是暫時(shí)過(guò)渡一下。而PermissionX我是準(zhǔn)備長(zhǎng)期維護(hù)下去的,并會(huì)持續(xù)增加更多好用的新功能。

后記

最后,一定也會(huì)有朋友想要詢(xún)問(wèn),Java語(yǔ)言的項(xiàng)目能不能使用PermissionX呢?

其實(shí)早在最開(kāi)始的時(shí)候,我是打算將PermissionX設(shè)計(jì)成Kotlin和Java都可以通用的一個(gè)庫(kù)。但是寫(xiě)著寫(xiě)著發(fā)現(xiàn),如果想要兼容Java語(yǔ)言,需要放棄很多Kotlin的語(yǔ)法特性,這樣PermissionX用起來(lái)就不再是那么簡(jiǎn)潔了,最終只好選擇了放棄Java語(yǔ)言的支持。

不過(guò)等PermissionX整體功能穩(wěn)定下來(lái)之后,我可能會(huì)專(zhuān)門(mén)再編寫(xiě)一個(gè)Java版的PermissionX。語(yǔ)法層面肯定要比Kotlin版的復(fù)雜不少,但是一定比你自己去處理運(yùn)行時(shí)權(quán)限簡(jiǎn)單得多。

新庫(kù)剛剛發(fā)布,可能還存在很多我自己沒(méi)能測(cè)出來(lái)的bug,也請(qǐng)大家?guī)兔Χ喽鄿y(cè)試,共同將這個(gè)庫(kù)變得更加完善。

再次貼上PermissionX的開(kāi)源庫(kù)地址,歡迎大家star和fork。

https://github.com/guolindev/PermissionX

到此這篇關(guān)于Android運(yùn)行時(shí)權(quán)限終極方案(PermissionX)的文章就介紹到這了,更多相關(guān)Android 運(yùn)行時(shí)權(quán)限內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論