Android6.0权限申请, 应该说是挺墨迹的一个事, 现在网上有一些权限申请的第三方, 用起来也很方便, 但是有一点, 这些第三方集成进你的项目中无疑会造成apk的体积增大, 这就好比大名鼎鼎的ButterKnife插件, 用起来很爽, 但是有人可能觉得ButterKnife会或多或少的影响性能, 对于这个问题我觉得是见仁见智的问题. 如果怕影响性能, 大可以用一些其他的插件, 比如用FindViewByMe插件,这个插件只是自动生成findviewbyid的相关代码来代替手写而已, 所以, 我们也可以自己写一个自动生成权限申请的代码的插件, 这样即不需要引入第三方,又不需要每次都手写, 可谓一劳永逸.下面就一起来开发一个简单版的权限申请代码自动生成插件
注: 因为篇幅原因, 本文只介绍插件开发的思路和过程, 如果有想要源码和jar包的请关注文章末尾公众号, 发送"permission_plugin源码"和"permission_plugin插件包"即可获得下载链接
(一)设计
严格来说, 动态权限申请, 应该是在应用要获取某个权限的时候再申请, 但是很多市面上面的app, 包括一些比较知名的app都是在首页的时候就会弹出权限申请, 因此, 本插件也设计成在某一个界面申请动态权限.
(二) 问题
整个插件开发过程其实就是解决这几个问题:
1 插件怎么知道开发者要在哪个界面(Activity)进行权限申请?
2 插件怎么知道属于动态申请的权限有哪些?
3 插件怎么知道在哪个位置生成什么代码?
(三)具体实现
下面我们就从新建项目开始一步步解决这几个问题, 进而实现整个插件.
1 新建项目
AndroidStudio基于IntelliJ平台,因此,开发AndroidStudio插件其本质只是开发IntelliJ平台的插件, 因此, 我们要在IDEA中开发插件, 新建项目见下图:
将Project选择为IntelliJ Platform Plugin, 然后next, 见下图
书写项目名称和选择项目本地保存路径, 见下图
点击Finish后, 打开项目, 见下图:
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文件, 见下图:
找到<id>标签, 填写上插件id
找到<name>标签,填写上插件名称
找到<version>标签, 填写上版本号
找到<description>标签, 填写上插件的简介
找到<change-notes>标签, 填写上版本变更
找到<actions>标签, 再新建一个<action>标签, 分别填写class, id, text, description, 重点是<add-to-group>标签, 见下图
<add-to-group>标签表示在studio中, 点击code这个菜单后, 在第一个位置显示插件, 点击就可触发了
(6)
至此, 插件开发完毕,接下来是打成jar包
点击IDEA的build菜单, 选中Prepare Plugin Module...选项进行打包, 完成后, 会在项目的.iml文件下方生成一个.jar文件, 这个就是插件包了
(7)
应用
打开android studio,点击file --> settings --> 左侧点击plugin, 选择下面的install plugin from disk, 选择要应用的插件包, 然后restart即可.
重启studio后在AndroidManifest.xml中添加几个普通权限和几个普通危险权限, 随便打开一个后缀是Activity的类, 鼠标放在类的内部, 点击studio中的Code菜单, 点击插件名称就自动生成了权限相关代码. 效果见下面动图. 至此, 全部完成