前言
本文已經(jīng)收錄到我的 Github 個(gè)人博客,歡迎大佬們光臨寒舍:
本篇文章需要已經(jīng)具備的知識(shí):
-
Git與Github的基本使用 -
Kotlin語(yǔ)法基礎(chǔ) -
Android開發(fā)基礎(chǔ)
學(xué)習(xí)清單:
- 如何封裝自己的權(quán)限框架
- 將開源庫(kù)發(fā)布到
JitPack倉(cāng)庫(kù)的一整套流程
一.為什么要封裝這套框架
我們?cè)谌粘i_發(fā)中,經(jīng)常需要用到申請(qǐng)運(yùn)行時(shí)權(quán)限的知識(shí),于是,經(jīng)常就寫了下面的一大串代碼
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
...
//申請(qǐng) CALL_PHONE 權(quán)限
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()
}
}
}
}
麻鴨,頭疼,這么多代碼,不僅寫著難受,看著更是頭疼

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

很顯然,上面用到了自己封裝的框架,有可能你會(huì)一臉不屑:『這算啥?Github 上一堆權(quán)限申請(qǐng)框架,他們寫的這個(gè)簡(jiǎn)潔又漂亮,功能又多又全,超帥的』
我想說(shuō):『是的,你說(shuō)的對(duì),雖然 Github 上有這么多,跑得又快又棒的輪子,但是,別人做的菜總歸沒有自己的香鴨!我們可以通過(guò)自己封裝一個(gè)簡(jiǎn)單的權(quán)限申請(qǐng)框架開始,學(xué)習(xí)發(fā)布開源庫(kù)到 Jitpack / Jcenter 的一整套流程,從而激發(fā)自己的學(xué)習(xí)興趣,以后自己也多多造輪子(xia zhe teng)!成為 Android 界的輪子哥』
先為大佬送上筆者已經(jīng)封裝好的輪子:https://github.com/LoveLifeEveryday/XPermissions


二.入坑之路
2.1 創(chuàng)建 Android 項(xiàng)目
新建一個(gè)空的 Android 項(xiàng)目

2.2 創(chuàng)建 Github 項(xiàng)目

然后,把該項(xiàng)目
clone到一個(gè)上面已經(jīng)創(chuàng)建的Android項(xiàng)目的位置將克隆下來(lái)的所有文件全部復(fù)制到上一層目錄(注意:復(fù)制的時(shí)候不要忘記復(fù)制
.git文件)將克隆的
XPermission目錄刪除執(zhí)行一系列的
git add .git commit -m "First commit"git push origin master操作
2.3 實(shí)現(xiàn) XPermission
- 對(duì)著最頂層的
XPermission,新建一個(gè)module,選擇Android Library

看到 library 就行,如下

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

2.3.2 創(chuàng)建 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 前面加個(gè)星號(hào)的意思是:將數(shù)組轉(zhuǎn)化為可變長(zhǎng)度參數(shù)傳遞過(guò)去
fragment.requestNow(callback, *permission)
}
}
相信代碼大家都看得懂,所以筆者就不寫很多注釋了(其實(shí)是因?yàn)閼?.)
2.4 測(cè)試
在
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')
}
然后進(jìn)行你喜歡的權(quán)限申請(qǐng)
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)
}
}
如果可以的話,恭喜你,你已經(jīng)成功一大步了
2.5 發(fā)布到 JitPack
2.5.1 JitPack 簡(jiǎn)介
JitPack 是一個(gè)網(wǎng)站,它允許你把 git 托管的 java 或 android 項(xiàng)目(貌似目前僅支持github和碼云),輕松發(fā)布到 jitpack 的 maven 倉(cāng)庫(kù)上,它所有內(nèi)容都通過(guò)內(nèi)容分發(fā)網(wǎng)絡(luò)(CDN)使用加密 https 連接獲取
2.5.2 為什么用 JitPack
優(yōu)點(diǎn):打包比較簡(jiǎn)單,省時(shí)間,背靠 Github 這座大山
缺點(diǎn):每次導(dǎo)入庫(kù)的時(shí)候,都要先在根的 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 賬號(hào)名,我的是:LoveLifeEveryday
group='com.github.LoveLifeEveryday'
android {
...
}
- 同步一下

- 在命令行中輸入
gradlew install,從而構(gòu)建你的library到你的本地maven倉(cāng)庫(kù)

等待
BUILD SUCCESSFUL,若BUILD FAIL,說(shuō)明構(gòu)建失敗,這時(shí)候你就要按照失敗提示去排錯(cuò),排錯(cuò)完后在執(zhí)行一遍gradlew install命令,直到出現(xiàn)BUILD SUCCESS
- 把代碼提交到本地
git倉(cāng)庫(kù)
git add .和git commit -m “XX”
- 在本地
git倉(cāng)庫(kù)打tag
git tag -a 1.0.0 -m "第一版"
git push origin 1.0.0
- 打開你的
libary的github界面,點(diǎn)擊release,如下:

- 點(diǎn)擊
Draft a new release,新建一個(gè)release,如下:

- 然后填信息,如下:

- 填好信息后,點(diǎn)擊
publich release,如下:

- 用
GitHub賬號(hào)登陸、注冊(cè)jitpack - 登陸后,在地址欄中輸入你的
library的github項(xiàng)目地址,然后點(diǎn)擊Look Up,如下:

- 然后點(diǎn)擊
Get it,它會(huì)滾到下面去,你要滾回上面去,先等一會(huì),等jitpack那里構(gòu)建完,會(huì)出現(xiàn)一個(gè)綠色的log,則構(gòu)建成功,如下:

然后你就可以愉快的在項(xiàng)目中按照它的提示引用你的開源庫(kù)

- 點(diǎn)擊那個(gè)
jitpack,把它的鏈接復(fù)制到你的Readme中去,如下:

2.6 嘗試使用你的框架
當(dāng)然是在 app\build.gradle 中
//引用自己的開源庫(kù)
implementation 'com.github.LoveLifeEveryday:XPermissions:1.0.0'
然后嘗試使用吧

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

三.我在使用中遇到的問題
3.1 在模擬器上 Call 權(quán)限申請(qǐng)無(wú)反應(yīng)
- 發(fā)生情景:在逍遙模擬器上測(cè)試
Call權(quán)限
至于我為什么要使用逍遙模擬器,這又是另一個(gè)故事了
- 解決:真機(jī)測(cè)試正常申請(qǐng)權(quán)限,于是百度了一波,發(fā)現(xiàn)很多模擬器沒有
Call這個(gè)權(quán)限(such as 夜神模擬器),我覺得原裝的模擬器應(yīng)該是可以正常運(yùn)行的 - 結(jié)論:模擬器的鍋
3.2 上傳到 Jcenter 時(shí) Failed
- 發(fā)生情景:執(zhí)行上傳命令的時(shí)候,運(yùn)行到最后發(fā)生錯(cuò)誤
- 錯(cuò)誤:
* What went wrong:
Execution failed for task ':utils:bintrayUpload'.
> org.apache.http.NoHttpResponseException: The target server failed to respond
- 過(guò)程:
Google&&Baidu - 結(jié)論:網(wǎng)絡(luò)問題
- 結(jié)果:嘗試了普通網(wǎng)絡(luò)和 Ke Xue 上網(wǎng),還是無(wú)法解決,轉(zhuǎn)為使用
JitPack
如果想了解,怎么上傳到
Jcenter的話,可以看下這篇文章:AS上傳Library到JCenter 教程+踩坑記錄

如果文章對(duì)您有一點(diǎn)幫助的話,希望您能點(diǎn)一下贊,您的點(diǎn)贊,是我前進(jìn)的動(dòng)力
本文參考鏈接:
- 『Android 第一行代碼 - 第三版』
- AS上傳Library到JCenter 教程+踩坑記錄
- 如何讓你的 GitHub 項(xiàng)目表面上更專業(yè)
- 快速發(fā)布開源庫(kù)到j(luò)itpack