Android6.0權(quán)限插件開(kāi)發(fā)(一鍵自動(dòng)生成權(quán)限申請(qǐng)相關(guān)代碼)

Android6.0權(quán)限申請(qǐng), 應(yīng)該說(shuō)是挺墨跡的一個(gè)事, 現(xiàn)在網(wǎng)上有一些權(quán)限申請(qǐng)的第三方, 用起來(lái)也很方便, 但是有一點(diǎn), 這些第三方集成進(jìn)你的項(xiàng)目中無(wú)疑會(huì)造成apk的體積增大, 這就好比大名鼎鼎的ButterKnife插件, 用起來(lái)很爽, 但是有人可能覺(jué)得ButterKnife會(huì)或多或少的影響性能, 對(duì)于這個(gè)問(wèn)題我覺(jué)得是見(jiàn)仁見(jiàn)智的問(wèn)題. 如果怕影響性能, 大可以用一些其他的插件, 比如用FindViewByMe插件,這個(gè)插件只是自動(dòng)生成findviewbyid的相關(guān)代碼來(lái)代替手寫(xiě)而已, 所以, 我們也可以自己寫(xiě)一個(gè)自動(dòng)生成權(quán)限申請(qǐng)的代碼的插件, 這樣即不需要引入第三方,又不需要每次都手寫(xiě), 可謂一勞永逸.下面就一起來(lái)開(kāi)發(fā)一個(gè)簡(jiǎn)單版的權(quán)限申請(qǐng)代碼自動(dòng)生成插件

注: 因?yàn)槠? 本文只介紹插件開(kāi)發(fā)的思路和過(guò)程, 如果有想要源碼和jar包的請(qǐng)關(guān)注文章末尾公眾號(hào), 發(fā)送"permission_plugin源碼"和"permission_plugin插件包"即可獲得下載鏈接

(一)設(shè)計(jì)

嚴(yán)格來(lái)說(shuō), 動(dòng)態(tài)權(quán)限申請(qǐng), 應(yīng)該是在應(yīng)用要獲取某個(gè)權(quán)限的時(shí)候再申請(qǐng), 但是很多市面上面的app, 包括一些比較知名的app都是在首頁(yè)的時(shí)候就會(huì)彈出權(quán)限申請(qǐng), 因此, 本插件也設(shè)計(jì)成在某一個(gè)界面申請(qǐng)動(dòng)態(tài)權(quán)限.

(二) 問(wèn)題

整個(gè)插件開(kāi)發(fā)過(guò)程其實(shí)就是解決這幾個(gè)問(wèn)題:

1 插件怎么知道開(kāi)發(fā)者要在哪個(gè)界面(Activity)進(jìn)行權(quán)限申請(qǐng)?
2 插件怎么知道屬于動(dòng)態(tài)申請(qǐng)的權(quán)限有哪些?
3 插件怎么知道在哪個(gè)位置生成什么代碼?

(三)具體實(shí)現(xiàn)

下面我們就從新建項(xiàng)目開(kāi)始一步步解決這幾個(gè)問(wèn)題, 進(jìn)而實(shí)現(xiàn)整個(gè)插件.

1 新建項(xiàng)目

AndroidStudio基于IntelliJ平臺(tái),因此,開(kāi)發(fā)AndroidStudio插件其本質(zhì)只是開(kāi)發(fā)IntelliJ平臺(tái)的插件, 因此, 我們要在IDEA中開(kāi)發(fā)插件, 新建項(xiàng)目見(jiàn)下圖:


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

將Project選擇為IntelliJ Platform Plugin, 然后next, 見(jiàn)下圖


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

書(shū)寫(xiě)項(xiàng)目名稱(chēng)和選擇項(xiàng)目本地保存路徑, 見(jiàn)下圖
Screenshot from 2019-05-30 10-06-28.png

點(diǎn)擊Finish后, 打開(kāi)項(xiàng)目, 見(jiàn)下圖:


Screenshot from 2019-05-30 10-11-16.png
2 開(kāi)發(fā)代碼
(1)
在src中新建一個(gè)包, 這里叫com.android_M.permission.plugin, 再在包中新建一個(gè)類(lèi), 這里叫PermissionPlugin, 再讓PermissionPlugin這個(gè)類(lèi)繼承AnAction, 重寫(xiě)actionPerformed(AnActionEvent anActionEvent)方法
public class PermissionPlugin extends AnAction {
      @Override
      public void actionPerformed(AnActionEvent anActionEvent) {

      }
}

當(dāng)開(kāi)發(fā)者在studio中想一鍵生成代碼的時(shí)候, 會(huì)點(diǎn)擊插件觸發(fā)一個(gè)動(dòng)作事件,IntelliJ會(huì)回調(diào)AnAction類(lèi)的actionPerformed函數(shù)。因此我們只需重寫(xiě)actionPerformed函數(shù)即可。

(2)
首先,獲取project, 再獲取editor, 獲取當(dāng)前的java文件,當(dāng)前類(lèi)名,判斷是否是activity子類(lèi)
Project project = anActionEvent.getData(PlatformDataKeys.PROJECT);
Editor editor = anActionEvent.getData(PlatformDataKeys.EDITOR);
PsiFile currentEditorFile = PsiUtilBase.getPsiFileInEditor(editor, project);
String currentEditorFileName = currentEditorFile.getName();

studio中如果打開(kāi)的是MainActivity, 并且鼠標(biāo)光標(biāo)在類(lèi)的范圍里面,那么此時(shí)currentEditorFile就是MainActivity.java文件,currentEditorFileName就是:"MainActivity.java"這個(gè)字符串,這里用來(lái)判斷當(dāng)前打開(kāi)的類(lèi)是不是activity的子類(lèi)。(其實(shí)這么判斷很不嚴(yán)謹(jǐn),這里是為了方便),到這里,就解決了第一個(gè)問(wèn)題,即用戶(hù)打開(kāi)了哪個(gè)activity,并且鼠標(biāo)光圈在類(lèi)范圍內(nèi),那么就在這個(gè)類(lèi)里面生成代碼

(3)
獲取AndroidManifest.xml文件,找到所有注冊(cè)的權(quán)限,過(guò)濾掉普通權(quán)限,把剩下的危險(xiǎn)權(quán)限添加進(jìn)集合中。
// 獲取AndroidManifest.xml文件,注意,獲取的結(jié)果是數(shù)組,是因?yàn)楫?dāng)有多個(gè)module時(shí)是有多個(gè)AndroidManifest.xml的
PsiFile[] psiFiles = FilenameIndex.getFilesByName(project, "AndroidManifest.xml", GlobalSearchScope.allScope(project));
for(int i = 0; i < psiFiles.length; i++) {
    // 此時(shí)的xmlFile就是每一個(gè)AndroidManifest.xml文件
    XmlFile xmlFile = (XmlFile) psiFiles[i];
}

然后開(kāi)始根據(jù)標(biāo)簽tag解析XmlFile文件,獲取到以及注冊(cè)的所有權(quán)限

xmlFile.accept(new XmlRecursiveElementVisitor() {
            @Override
            public void visitElement(PsiElement element) {
                super.visitElement(element);
                // 解析Xml標(biāo)簽
                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;
                        }
                        // 獲取具體的權(quán)限值
                        String permissionValue = permission_name.getValue();
                        // 把獲取到的所有權(quán)限添加進(jìn)集合
                        permissionList.add(permissionValue);
                    }
                }
            }
        });

到這里,獲取到了所有的權(quán)限,經(jīng)過(guò)過(guò)濾篩選后(所有的危險(xiǎn)權(quán)限都是固定的,過(guò)濾篩選部分代碼略過(guò))至此,解決了第二給問(wèn)題,即都需要申請(qǐng)哪些動(dòng)態(tài)權(quán)限

(4)
自動(dòng)生成代碼的部分必須寫(xiě)在線程中。首先判斷這個(gè)類(lèi)中是否有onCreate方法,如果沒(méi)有,就自動(dòng)生成,如果有,就在里面添加一個(gè)調(diào)用方法checkPermissions(listPermission)
WriteCommandAction.runWriteCommandAction(anActionEvent.getProject(), new Runnable() {
            @Override
            public void run() {
                // 獲取當(dāng)前類(lèi),有幾個(gè)onCreate方法,如果沒(méi)有, 就進(jìn)行自動(dòng)生成onCreate方法
                if(psiClass.findMethodsByName("onCreate", false).length == 0) {
                    // 想要自動(dòng)生成的代碼,是以字符串的形式作為參數(shù),創(chuàng)建PsiMethod對(duì)象,再由psiClass對(duì)象add即可(詳見(jiàn)下面貼的buildMethodOnCreate()方法實(shí)現(xiàn))
                    String methodOnCreate = util.buildMethodOnCreate();
                    PsiMethod psiMethodOnCreate = elementFactory.createMethodFromText(methodOnCreate, psiClass);
                    psiClass.add(psiMethodOnCreate);
                }
                // 如果有onCreate方法,就自動(dòng)生成checkPermissions方法,并在onCreate方法里面調(diào)用
                if(psiClass.findMethodsByName("onCreate", false).length = 1) {
                    // 獲取onCreate方法,在里面調(diào)用checkPermissions方法
                    PsiMethod onCreateMethod = psiClass.findMethodsByName("onCreate", false)[0];
                    onCreateMethod.getBody().add(elementFactory.createStatementFromText("checkPermissions(listPermission);", psiClass));
                }

                // 寫(xiě)變量,(用來(lái)存放所有需要申請(qǐng)的危險(xiǎn)權(quán)限)
                String variable = "private ArrayList<String> requestPermissionList = new ArrayList<String>();";
                PsiField fieldFromText = elementFactory.createFieldFromText(variable, psiClass);
                psiClass.add(fieldFromText);

                // 寫(xiě)變量,申請(qǐng)權(quán)限后回調(diào)的requestCode
                String requestCode = "private final int REQUEST_PERMISSION_CODE = 0;";
                PsiField fieldFromCode = elementFactory.createFieldFromText(requestCode, psiClass);
                psiClass.add(fieldFromCode);

                // 寫(xiě)變量,(從AndroidManifest.xml文件讀取來(lái)的所有的危險(xiǎn)權(quán)限)
                String variableArr = "private String[] listPermission = new String[]{" + sb.toString() + "};";
                PsiField fieldFromArr = elementFactory.createFieldFromText(variableArr, psiClass);
                psiClass.add(fieldFromArr);

                // 寫(xiě)方法,(檢查權(quán)限)
                String methodText = util.buildMethodText(className);
                PsiMethod psiMethod = elementFactory.createMethodFromText(methodText, psiClass);
                psiClass.add(psiMethod);

                // 寫(xiě)申請(qǐng)權(quán)限的requestPermission方法
                PsiMethod psiMethodRequestPermission = elementFactory.createMethodFromText(util.buildMethodRequestPermission(), psiClass);
                psiClass.add(psiMethodRequestPermission);

                // 寫(xiě)申請(qǐng)權(quán)限后的回調(diào)方法
                PsiMethod psiMethodRequestPermissionResult = elementFactory.createMethodFromText(util.buildMethodRequestPermissionResult(className), psiClass);
                psiClass.add(psiMethodRequestPermissionResult);
            }
        });

具體生成代碼的部分(只以自動(dòng)生成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)
配置插件文件, 我們需要通過(guò)配置文件, 告訴用戶(hù), 插件的名稱(chēng), 功能, 版本, 以及怎么觸發(fā)等信息

打開(kāi)項(xiàng)目中resources下面的plugin.xml文件, 見(jiàn)下圖:


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

找到<id>標(biāo)簽, 填寫(xiě)上插件id
找到<name>標(biāo)簽,填寫(xiě)上插件名稱(chēng)
找到<version>標(biāo)簽, 填寫(xiě)上版本號(hào)
找到<description>標(biāo)簽, 填寫(xiě)上插件的簡(jiǎn)介
找到<change-notes>標(biāo)簽, 填寫(xiě)上版本變更
找到<actions>標(biāo)簽, 再新建一個(gè)<action>標(biāo)簽, 分別填寫(xiě)class, id, text, description, 重點(diǎn)是<add-to-group>標(biāo)簽, 見(jiàn)下圖


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

<add-to-group>標(biāo)簽表示在studio中, 點(diǎn)擊code這個(gè)菜單后, 在第一個(gè)位置顯示插件, 點(diǎn)擊就可觸發(fā)了
(6)
至此, 插件開(kāi)發(fā)完畢,接下來(lái)是打成jar包

點(diǎn)擊IDEA的build菜單, 選中Prepare Plugin Module...選項(xiàng)進(jìn)行打包, 完成后, 會(huì)在項(xiàng)目的.iml文件下方生成一個(gè).jar文件, 這個(gè)就是插件包了


Screenshot from 2019-05-31 09-56-36.png
(7)
應(yīng)用

打開(kāi)android studio,點(diǎn)擊file --> settings --> 左側(cè)點(diǎn)擊plugin, 選擇下面的install plugin from disk, 選擇要應(yīng)用的插件包, 然后restart即可.


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

重啟studio后在AndroidManifest.xml中添加幾個(gè)普通權(quán)限和幾個(gè)普通危險(xiǎn)權(quán)限, 隨便打開(kāi)一個(gè)后綴是Activity的類(lèi), 鼠標(biāo)放在類(lèi)的內(nèi)部, 點(diǎn)擊studio中的Code菜單, 點(diǎn)擊插件名稱(chēng)就自動(dòng)生成了權(quán)限相關(guān)代碼. 效果見(jiàn)下面動(dòng)圖. 至此, 全部完成


aaa.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容