帶你封裝自己的『許可權管理』框架
前言
本文已經收錄到我的 Github 個人部落格,歡迎大佬們光臨寒舍:
本篇文章需要已經具備的知識:
-
Git
與Github
的基本使用 -
Kotlin
語法基礎 -
Android
開發基礎
學習清單:
-
如何封裝自己的許可權框架 -
將開源庫發布到 JitPack
倉庫的一整套流程
一.為什麼要封裝這套框架
我們在日常開發中,經常需要用到申請運行時許可權的知識,於是,經常就寫了下面的一大串程式碼
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
...
//申請 CALL_PHONE 許可權
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.CALL_PHONE
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CALL_PHONE), 1)
} else {
call()
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out 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 the permission", Toast.LENGTH_SHORT).show()
}
}
}
}
麻鴨,頭疼,這麼多程式碼,不僅寫著難受,看著更是頭疼

這時候,如果這個世界簡單點,純粹點,就好了
XPermission.request(
this,
Manifest.permission.CALL_PHONE
) { allGranted, deniedList ->
if (allGranted) {
call()
} else {
Toast.makeText(this, "You denied $deniedList", Toast.LENGTH_SHORT).show()
}
}
是不是感覺世界又友好了很多呢?這段程式碼比之前的程式碼量少了很多不說,邏輯更是清晰了很多鴨!

很顯然,上面用到了自己封裝的框架,有可能你會一臉不屑:『這算啥?Github
上一堆許可權申請框架,他們寫的這個簡潔又漂亮,功能又多又全,超帥的』
我想說:『是的,你說的對,雖然 Github
上有這麼多,跑得又快又棒的輪子,但是,別人做的菜總歸沒有自己的香鴨!我們可以通過自己封裝一個簡單的許可權申請框架開始,學習發布開源庫到 Jitpack
/ Jcenter
的一整套流程,從而激發自己的學習興趣,以後自己也多多造輪子(xia zhe teng)!成為 Android
界的輪子哥』
先為大佬送上筆者已經封裝好的輪子://github.com/LoveLifeEveryday/XPermissions


二.入坑之路
2.1 創建 Android
項目
新建一個空的 Android
項目

2.2 創建 Github
項目

然後,把該項目
clone
到一個上面已經創建的Android
項目的位置將克隆下來的所有文件全部複製到上一層目錄(注意:複製的時候不要忘記複製
.git
文件)將克隆的
XPermission
目錄刪除執行一系列的
git add .
git commit -m "First commit"
git push origin master
操作
2.3 實現 XPermission
-
對著最頂層的 XPermission
,新建一個module
,選擇Android Library

看到 library
就行,如下

然後,我們思考下,運行時許可權的實現思路,有以下三種:
-
將運行時許可權的操作封裝到 BaseActivity
中 -
提供一個透明的 Activity
來處理 -
提供一個隱藏的 Fragment
來處理
本文,將根據最後一個思路進行實現
2.3.1 創建 InvisibleFragment
//給 (Boolean, List<String>) -> Unit 指定一個別名
typealias PermissionCallback = (Boolean, List<String>) -> Unit
class InvisibleFragment : Fragment() {
//定義一個 callback 作為運行時許可權申請結果的回調通知方式
private var callback: PermissionCallback? = null
//定義申請許可權的方法,vararg 表示可變長度的 permissions 參數列表
fun requestNow(cb: PermissionCallback, vararg permission: String) {
callback = cb
requestPermissions(permission, 1)
}
/**
* 請求返回結果
* @param requestCode Int 請求碼
* @param permissions Array<String> 許可權
* @param grantResults IntArray 請求結果
*/
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
if (requestCode == 1) {
// deniedList 用來記錄被用戶拒絕的許可權
val deniedList = ArrayList<String>()
for ((index, result) in grantResults.withIndex()) {
if (result != PackageManager.PERMISSION_GRANTED) {
deniedList.add(permissions[index])
}
}
// allGranted 用來標識是否所有申請的許可權都已經授權
val allGranted = deniedList.isEmpty()
//對申請許可權的結果進行回調
callback?.let { it(allGranted, deniedList) }
}
}
}
-
首先,我們定義一個 callback
作為運行時許可權申請結果的回調通知方式 -
然後,定義一個 requestNow
方法 -
最後重寫 onRequestPermissionsResult
方法

2.3.2 創建 XPermission
object XPermission {
private const val TAG = "InvisibleFragment"
fun request(
activity: FragmentActivity,
vararg permission: String,
callback: PermissionCallback
) {
val fragmentManager = activity.supportFragmentManager
val existedFragment = fragmentManager.findFragmentByTag(TAG)
val fragment = if (existedFragment != null) {
existedFragment as InvisibleFragment
} else {
val invisibleFragment = InvisibleFragment()
fragmentManager.beginTransaction().add(invisibleFragment, TAG).commitNow()
invisibleFragment
}
//這裡在 permission 前面加個星號的意思是:將數組轉化為可變長度參數傳遞過去
fragment.requestNow(callback, *permission)
}
}
相信程式碼大家都看得懂,所以筆者就不寫很多注釋了(其實是因為懶..)
2.4 測試
在
app\build.gradle
中引入library
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
//添加這行就行
implementation project(':library')
}
然後進行你喜歡的許可權申請
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
makeCallBtn.setOnClickListener {
XPermission.request(this, Manifest.permission.CALL_PHONE) { allGranted, deniedList ->
if (allGranted) {
call()
} else {
Toast.makeText(this, "You Denied $deniedList", Toast.LENGTH_SHORT).show()
}
}
}
}
private fun call() {
val intent = Intent(Intent.ACTION_CALL)
intent.data = Uri.parse("tel:10086")
startActivity(intent)
}
}
如果可以的話,恭喜你,你已經成功一大步了
2.5 發布到 JitPack
2.5.1 JitPack
簡介
JitPack 是一個網站,它允許你把 git
託管的 java
或 android
項目(貌似目前僅支援github
和碼雲),輕鬆發布到 jitpack
的 maven
倉庫上,它所有內容都通過內容分發網路(CDN
)使用加密 https
連接獲取
2.5.2 為什麼用 JitPack
優點:打包比較簡單,省時間,背靠 Github
這座大山
缺點:每次導入庫的時候,都要先在根的 build.gradle
文件中添加 maven

2.5.3 步驟
-
在根的 build.gradle
中添加maven
插件
buildscript {
ext.kotlin_version = '1.3.71'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
//添加 maven 插件
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
-
在 library
目錄的build.gradle
下apply
插件和添加group
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
//添加下面兩行
apply plugin: 'com.github.dcendents.android-maven'
//這裡 LoveLifeEveryday 改為你的 github 帳號名,我的是:LoveLifeEveryday
group='com.github.LoveLifeEveryday'
android {
...
}
-
同步一下

-
在命令行中輸入 gradlew install
,從而構建你的library
到你的本地maven
倉庫

等待
BUILD SUCCESSFUL,
若BUILD FAIL
,說明構建失敗,這時候你就要按照失敗提示去排錯,排錯完後在執行一遍gradlew install
命令,直到出現BUILD SUCCESS
-
把程式碼提交到本地 git
倉庫
git add .
和git commit -m 「XX」
-
在本地 git
倉庫打tag
git tag -a 1.0.0 -m "第一版"
git push origin 1.0.0
-
打開你的 libary
的github
介面,點擊release
,如下:

-
點擊 Draft a new release
,新建一個release
,如下:

-
然後填資訊,如下:

-
填好資訊後,點擊 publich release
,如下:

-
用 GitHub
帳號登陸、註冊jitpack
-
登陸後,在地址欄中輸入你的 library
的github
項目地址,然後點擊Look Up
,如下:

-
然後點擊 Get it
,它會滾到下面去,你要滾回上面去,先等一會,等jitpack
那裡構建完,會出現一個綠色的log
,則構建成功,如下:

然後你就可以愉快的在項目中按照它的提示引用你的開源庫

-
點擊那個 jitpack
,把它的鏈接複製到你的Readme
中去,如下:

2.6 嘗試使用你的框架
當然是在 app\build.gradle
中
//引用自己的開源庫
implementation 'com.github.LoveLifeEveryday:XPermissions:1.0.0'
然後嘗試使用吧

2.7 美化你的項目
一個優秀的開源項目,readme
一定不會差
魯迅說:『雖然這些工作不會讓你的項目變得牛逼,但會讓你的項目變得漂亮,方便了其他人去了解你這個項目』
詳細的美化操作,可以參考這篇文章:如何讓你的 GitHub 項目表面上更專業

三.我在使用中遇到的問題
3.1 在模擬器上 Call
許可權申請無反應
-
發生情景:在逍遙模擬器上測試 Call
許可權
至於我為什麼要使用逍遙模擬器,這又是另一個故事了
-
解決:真機測試正常申請許可權,於是百度了一波,發現很多模擬器沒有 Call
這個許可權(such as 夜神模擬器),我覺得原裝的模擬器應該是可以正常運行的 -
結論:模擬器的鍋
3.2 上傳到 Jcenter
時 Failed
-
發生情景:執行上傳命令的時候,運行到最後發生錯誤 -
錯誤:
* What went wrong:
Execution failed for task ':utils:bintrayUpload'.
> org.apache.http.NoHttpResponseException: The target server failed to respond
-
過程: Google
&&Baidu
-
結論:網路問題 -
結果:嘗試了普通網路和 Ke Xue 上網,還是無法解決,轉為使用 JitPack
如果想了解,怎麼上傳到
Jcenter
的話,可以看下這篇文章:AS上傳Library到JCenter 教程+踩坑記錄

如果文章對您有一點幫助的話,希望您能點一下贊,您的點贊,是我前進的動力
本文參考鏈接:
-
『Android 第一行程式碼 – 第三版』 -
AS上傳Library到JCenter 教程+踩坑記錄 -
如何讓你的 GitHub 項目表面上更專業 -
快速發布開源庫到jitpack
本文使用 mdnice 排版