1. 程式人生 > >帶你封裝自己的『許可權管理』框架

帶你封裝自己的『許可權管理』框架

前言

本文已經收錄到我的 Github 個人部落格,歡迎大佬們光臨寒舍:

我的 Github 部落格

本篇文章需要已經具備的知識:

  • GitGithub 的基本使用
  • 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 界的輪子哥』

先為大佬送上筆者已經封裝好的輪子:https://github.com/LoveLifeEveryday/XPermissions

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 一定不會差