Android6.0权限插件开发(一键自动生成权限申请相关代码)

Android6.0权限申请, 应该说是挺墨迹的一个事, 现在网上有一些权限申请的第三方, 用起来也很方便, 但是有一点, 这些第三方集成进你的项目中无疑会造成apk的体积增大, 这就好比大名鼎鼎的ButterKnife插件, 用起来很爽, 但是有人可能觉得ButterKnife会或多或少的影响性能, 对于这个问题我觉得是见仁见智的问题. 如果怕影响性能, 大可以用一些其他的插件, 比如用FindViewByMe插件,这个插件只是自动生成findviewbyid的相关代码来代替手写而已, 所以, 我们也可以自己写一个自动生成权限申请的代码的插件, 这样即不需要引入第三方,又不需要每次都手写, 可谓一劳永逸.下面就一起来开发一个简单版的权限申请代码自动生成插件

注: 因为篇幅原因, 本文只介绍插件开发的思路和过程, 如果有想要源码和jar包的请关注文章末尾公众号, 发送"permission_plugin源码"和"permission_plugin插件包"即可获得下载链接

(一)设计

严格来说, 动态权限申请, 应该是在应用要获取某个权限的时候再申请, 但是很多市面上面的app, 包括一些比较知名的app都是在首页的时候就会弹出权限申请, 因此, 本插件也设计成在某一个界面申请动态权限.

(二) 问题

整个插件开发过程其实就是解决这几个问题:

1 插件怎么知道开发者要在哪个界面(Activity)进行权限申请?
2 插件怎么知道属于动态申请的权限有哪些?
3 插件怎么知道在哪个位置生成什么代码?

(三)具体实现

下面我们就从新建项目开始一步步解决这几个问题, 进而实现整个插件.

1 新建项目

AndroidStudio基于IntelliJ平台,因此,开发AndroidStudio插件其本质只是开发IntelliJ平台的插件, 因此, 我们要在IDEA中开发插件, 新建项目见下图:


Screenshot from 2019-05-30 10-04-40.png

将Project选择为IntelliJ Platform Plugin, 然后next, 见下图


Screenshot from 2019-05-30 10-05-18.png

书写项目名称和选择项目本地保存路径, 见下图
Screenshot from 2019-05-30 10-06-28.png

点击Finish后, 打开项目, 见下图:


Screenshot from 2019-05-30 10-11-16.png
2 开发代码
(1)
在src中新建一个包, 这里叫com.android_M.permission.plugin, 再在包中新建一个类, 这里叫PermissionPlugin, 再让PermissionPlugin这个类继承AnAction, 重写actionPerformed(AnActionEvent anActionEvent)方法
public class PermissionPlugin extends AnAction {
      @Override
      public void actionPerformed(AnActionEvent anActionEvent) {

      }
}

当开发者在studio中想一键生成代码的时候, 会点击插件触发一个动作事件,IntelliJ会回调AnAction类的actionPerformed函数。因此我们只需重写actionPerformed函数即可。

(2)
首先,获取project, 再获取editor, 获取当前的java文件,当前类名,判断是否是activity子类
Project project = anActionEvent.getData(PlatformDataKeys.PROJECT);
Editor editor = anActionEvent.getData(PlatformDataKeys.EDITOR);
PsiFile currentEditorFile = PsiUtilBase.getPsiFileInEditor(editor, project);
String currentEditorFileName = currentEditorFile.getName();

studio中如果打开的是MainActivity, 并且鼠标光标在类的范围里面,那么此时currentEditorFile就是MainActivity.java文件,currentEditorFileName就是:"MainActivity.java"这个字符串,这里用来判断当前打开的类是不是activity的子类。(其实这么判断很不严谨,这里是为了方便),到这里,就解决了第一个问题,即用户打开了哪个activity,并且鼠标光圈在类范围内,那么就在这个类里面生成代码

(3)
获取AndroidManifest.xml文件,找到所有注册的权限,过滤掉普通权限,把剩下的危险权限添加进集合中。
// 获取AndroidManifest.xml文件,注意,获取的结果是数组,是因为当有多个module时是有多个AndroidManifest.xml的
PsiFile[] psiFiles = FilenameIndex.getFilesByName(project, "AndroidManifest.xml", GlobalSearchScope.allScope(project));
for(int i = 0; i < psiFiles.length; i++) {
    // 此时的xmlFile就是每一个AndroidManifest.xml文件
    XmlFile xmlFile = (XmlFile) psiFiles[i];
}

然后开始根据标签tag解析XmlFile文件,获取到以及注册的所有权限

xmlFile.accept(new XmlRecursiveElementVisitor() {
            @Override
            public void visitElement(PsiElement element) {
                super.visitElement(element);
                // 解析Xml标签
                if (element instanceof XmlTag) {
                    XmlTag tag = (XmlTag) element;
                    // 获取Tag的名字(TextView)或者自定义
                    String tagName = tag.getName();
                    if(tagName != null && !tagName.equals("") && tagName.equals("uses-permission")) {
                        // 获取permission name属性
                        XmlAttribute permission_name = tag.getAttribute("android:name", null);
                        if(permission_name == null) {
                            return;
                        }
                        // 获取具体的权限值
                        String permissionValue = permission_name.getValue();
                        // 把获取到的所有权限添加进集合
                        permissionList.add(permissionValue);
                    }
                }
            }
        });

到这里,获取到了所有的权限,经过过滤筛选后(所有的危险权限都是固定的,过滤筛选部分代码略过)至此,解决了第二给问题,即都需要申请哪些动态权限

(4)
自动生成代码的部分必须写在线程中。首先判断这个类中是否有onCreate方法,如果没有,就自动生成,如果有,就在里面添加一个调用方法checkPermissions(listPermission)
WriteCommandAction.runWriteCommandAction(anActionEvent.getProject(), new Runnable() {
            @Override
            public void run() {
                // 获取当前类,有几个onCreate方法,如果没有, 就进行自动生成onCreate方法
                if(psiClass.findMethodsByName("onCreate", false).length == 0) {
                    // 想要自动生成的代码,是以字符串的形式作为参数,创建PsiMethod对象,再由psiClass对象add即可(详见下面贴的buildMethodOnCreate()方法实现)
                    String methodOnCreate = util.buildMethodOnCreate();
                    PsiMethod psiMethodOnCreate = elementFactory.createMethodFromText(methodOnCreate, psiClass);
                    psiClass.add(psiMethodOnCreate);
                }
                // 如果有onCreate方法,就自动生成checkPermissions方法,并在onCreate方法里面调用
                if(psiClass.findMethodsByName("onCreate", false).length = 1) {
                    // 获取onCreate方法,在里面调用checkPermissions方法
                    PsiMethod onCreateMethod = psiClass.findMethodsByName("onCreate", false)[0];
                    onCreateMethod.getBody().add(elementFactory.createStatementFromText("checkPermissions(listPermission);", psiClass));
                }

                // 写变量,(用来存放所有需要申请的危险权限)
                String variable = "private ArrayList<String> requestPermissionList = new ArrayList<String>();";
                PsiField fieldFromText = elementFactory.createFieldFromText(variable, psiClass);
                psiClass.add(fieldFromText);

                // 写变量,申请权限后回调的requestCode
                String requestCode = "private final int REQUEST_PERMISSION_CODE = 0;";
                PsiField fieldFromCode = elementFactory.createFieldFromText(requestCode, psiClass);
                psiClass.add(fieldFromCode);

                // 写变量,(从AndroidManifest.xml文件读取来的所有的危险权限)
                String variableArr = "private String[] listPermission = new String[]{" + sb.toString() + "};";
                PsiField fieldFromArr = elementFactory.createFieldFromText(variableArr, psiClass);
                psiClass.add(fieldFromArr);

                // 写方法,(检查权限)
                String methodText = util.buildMethodText(className);
                PsiMethod psiMethod = elementFactory.createMethodFromText(methodText, psiClass);
                psiClass.add(psiMethod);

                // 写申请权限的requestPermission方法
                PsiMethod psiMethodRequestPermission = elementFactory.createMethodFromText(util.buildMethodRequestPermission(), psiClass);
                psiClass.add(psiMethodRequestPermission);

                // 写申请权限后的回调方法
                PsiMethod psiMethodRequestPermissionResult = elementFactory.createMethodFromText(util.buildMethodRequestPermissionResult(className), psiClass);
                psiClass.add(psiMethodRequestPermissionResult);
            }
        });

具体生成代码的部分(只以自动生成onCreate方法为例):

public static String buildMethodOnCreate() {
        StringBuilder method = new StringBuilder();
        method.append("@Override protected void onCreate(android.os.Bundle savedInstanceState) {\n");
        method.append("super.onCreate(savedInstanceState);\n");
        method.append("}");
        return method.toString();
    }
(5)
配置插件文件, 我们需要通过配置文件, 告诉用户, 插件的名称, 功能, 版本, 以及怎么触发等信息

打开项目中resources下面的plugin.xml文件, 见下图:


Screenshot from 2019-05-31 10-02-44.png

找到<id>标签, 填写上插件id
找到<name>标签,填写上插件名称
找到<version>标签, 填写上版本号
找到<description>标签, 填写上插件的简介
找到<change-notes>标签, 填写上版本变更
找到<actions>标签, 再新建一个<action>标签, 分别填写class, id, text, description, 重点是<add-to-group>标签, 见下图


Screenshot from 2019-05-31 10-13-08.png

<add-to-group>标签表示在studio中, 点击code这个菜单后, 在第一个位置显示插件, 点击就可触发了
(6)
至此, 插件开发完毕,接下来是打成jar包

点击IDEA的build菜单, 选中Prepare Plugin Module...选项进行打包, 完成后, 会在项目的.iml文件下方生成一个.jar文件, 这个就是插件包了


Screenshot from 2019-05-31 09-56-36.png
(7)
应用

打开android studio,点击file --> settings --> 左侧点击plugin, 选择下面的install plugin from disk, 选择要应用的插件包, 然后restart即可.


Screenshot from 2019-05-31 11-12-25.png

重启studio后在AndroidManifest.xml中添加几个普通权限和几个普通危险权限, 随便打开一个后缀是Activity的类, 鼠标放在类的内部, 点击studio中的Code菜单, 点击插件名称就自动生成了权限相关代码. 效果见下面动图. 至此, 全部完成


aaa.gif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352