带你封装自己的『权限管理』框架

前言

本文已经收录到我的 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
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
新建 module

看到 library 就行,如下

新建 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: PermissionCallbackvararg 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
添加 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
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
release
  • 点击 Draft a new release,新建一个 release,如下:
image-20200424182958983
image-20200424182958983
  • 然后填信息,如下:
填信息
填信息
  • 填好信息后,点击publich release,如下:
  • GitHub 账号登陆、注册 jitpack
  • 登陆后,在地址栏中输入你的 librarygithub 项目地址,然后点击 Look Up ,如下:
  • 然后点击 Get it,它会滚到下面去,你要滚回上面去,先等一会,等 jitpack 那里构建完,会出现一个绿色的 log,则构建成功,如下:

然后你就可以愉快的在项目中按照它的提示引用你的开源库

  • 点击那个 jitpack ,把它的链接复制到你的 Readme 中去,如下:
jitpack
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 退散
bug 退散

如果文章对您有一点帮助的话,希望您能点一下赞,您的点赞,是我前进的动力

本文参考链接:

本文使用 mdnice 排版

posted @ 2020-04-25 11:14  许朋友爱玩  阅读(1531)  评论(1编辑  收藏  举报