帶你封裝本身的『權限管理』框架

前言

本文已經收錄到個人 Github 我的博客,歡迎大佬們光臨寒舍:java

個人 Github 博客android

本篇文章須要已經具有的知識:

  • GitGithub 的基本使用
  • Kotlin 語法基礎
  • Android 開發基礎

學習清單:

  • 如何封裝本身的權限框架
  • 將開源庫發佈到 JitPack 倉庫的一整套流程

一.爲何要封裝這套框架

咱們在平常開發中,常常須要用到申請運行時權限的知識,因而,常常就寫了下面的一大串代碼nginx

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()
            }
        }
    }
}
複製代碼

麻鴨,頭疼,這麼多代碼,不只寫着難受,看着更是頭疼git

頭疼

這時候,若是這個世界簡單點,純粹點,就行了github

XPermission.request(
    this,
    Manifest.permission.CALL_PHONE
) { allGranted, deniedList ->
    if (allGranted) {
        call()
    } else {
        Toast.makeText(this, "You denied $deniedList", Toast.LENGTH_SHORT).show()
    }
}
複製代碼

是否是感受世界又友好了不少呢?這段代碼比以前的代碼量少了不少不說,邏輯更是清晰了不少鴨!apache

滾來了

很顯然,上面用到了本身封裝的框架,有可能你會一臉不屑:『這算啥?Github 上一堆權限申請框架,他們寫的這個簡潔又漂亮,功能又多又全,超帥的』segmentfault

我想說:『是的,你說的對,雖然 Github 上有這麼多,跑得又快又棒的輪子,可是,別人作的菜總歸沒有本身的香鴨!咱們能夠經過本身封裝一個簡單的權限申請框架開始,學習發佈開源庫到 Jitpack / Jcenter 的一整套流程,從而激發本身的學習興趣,之後本身也多多造輪子(xia zhe teng)!成爲 Android 界的輪子哥』數組

先爲大佬送上筆者已經封裝好的輪子:github.com/LoveLifeEve…bash

XPermission

上車

二.入坑之路

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

  1. 對着最頂層的 XPermission ,新建一個 module ,選擇 Android Library

新建 module

看到 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 託管的 javaandroid 項目(貌似目前僅支持github和碼雲),輕鬆發佈到 jitpackmaven 倉庫上,它全部內容都經過內容分發網絡(CDN)使用加密 https 鏈接獲取

2.5.2 爲何用 JitPack

優勢:打包比較簡單,省時間,背靠 Github 這座大山

缺點:每次導入庫的時候,都要先在根的 build.gradle 文件中添加 maven

添加 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.gradleapply 插件和添加 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 倉庫

gradlew install

等待 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
複製代碼
  • 打開你的 libarygithub 界面,點擊 release,以下:

release

  • 點擊 Draft a new release,新建一個 release,以下:

image-20200424182958983

  • 而後填信息,以下:

填信息

  • 填好信息後,點擊publich release,以下:

  • GitHub 帳號登錄、註冊 jitpack
  • 登錄後,在地址欄中輸入你的 librarygithub 項目地址,而後點擊 Look Up ,以下:

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

而後你就能夠愉快的在項目中按照它的提示引用你的開源庫

  • 點擊那個 jitpack ,把它的連接複製到你的 Readme 中去,以下:

jitpack

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 上傳到 JcenterFailed

  • 發生情景:執行上傳命令的時候,運行到最後發生錯誤
  • 錯誤:
* 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 教程+踩坑記錄

bug 退散


若是文章對您有一點幫助的話,但願您能點一下贊,您的點贊,是我前進的動力

本文參考連接:

相關文章
相關標籤/搜索