android studio插件開發

之前寫過一篇java項目的自動化刪除lint到的無用資源,由於使用起來每次都得運行java項目,雖然也不麻煩,但是android studio支持插件開發,於是就想通過插件開發達到一鍵永逸,使用起來也很方便;正好同事也感興趣所以一起研究插件開發,這也是我們android開發人員進階的一項重要技能。畢竟有很多重複性工作,可以使用插件這玩意代替人工,效率也會大大的提高。。。比如前邊有幾篇用java寫的都可以使用插件去開發。畢竟這個插件是支持java編程的所以移植過來很方便。這裏挑了一個比較簡單的之前用Java寫的自動化刪除lint到的無用資源改用插件開發。隨後會附上demo。

     由於插件是用java開發的,所以支持所有java的api,插件的UI用java中的swing,如果不是特別瞭解swing的可以去看看,我相信做android的瞭解swing的組件也不難。這裏寫的比較簡單沒用到UI,但是同事有一個demo插件寫的還不錯該有的基本都會有了,隨後也附上一起學習學習。


在學習之前需要安裝好intelliJ IEDA:這裏推薦自己收藏的資料,按照如下基本成功的安裝好IDEA以及如何創建一個插件項目和android studio所需要的插件安裝包


intelli idea插件開發 下載 community免費版

1jdk版本--> jdk1.8及以上

  安裝jdk及配置環境變量

2、IntelliJIDEA工具--> 地址:http://www.jetbrains.com/idea/

  安裝ide及配置SdkJavaAndroidIntelliJ IDEA

  http://idea.lanyus.com/  ide     註冊碼獲取

3、配置:

  安裝的ide本身就是sdk

4、文檔

http://www.doc88.com/p-7758859318883.html  中文文檔
http://www.jetbrains.org/intellij/sdk/docs/basics/architectural_overview/virtual_file.html 官方開發以及API文檔

注意:這裏的SDK不是android中的SDK,其實不用關心,我們只需要配置JDK1.8以上的就行,不用配置什麼SDK,因爲ide本身就是sdk


注意:根據自己的操作系統選擇下載community這款就行

環境搭建好了:接下來推薦一個博客寫的和創建一個插件項目、如何安裝插件、以及一些demo

http://blog.csdn.net/lmj623565791/article/details/51548272

http://www.open-open.com/lib/view/open1477035480914.html

http://blog.csdn.net/liuloua/article/details/51917362


相信通過上邊的詳細閱讀基本瞭解如何開發一個插件了,接下來就是對API的瞭解以及使用,主要是那幾個關鍵類表示的是什麼以及如何使用,這裏建議通過閱讀官方的開發文檔

http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started.html

不過官方的開發文檔並沒有他們的用法,也很不全,看的是雲裏霧裏的,只是講解了一下API,所以這裏上傳了自己總結的關鍵API以及同事寫的比較全的Demo以及文檔

http://download.csdn.net/detail/zhongwn/9688623

http://download.csdn.net/detail/zhongwn/9688626


那我們一起來看看代碼的實現吧:這裏同樣假設你已經拿到了lint-results.xml:獲取的網址如下:

http://blog.csdn.net/imesong/article/details/49187695


這裏創建一個action

/**
 * Created by zhongwr on 2016/11/19.
 */
public class DeleteMainAction extends AnAction {

    /**
     * 代表當前項目
     */
    Project project;

    @Override
    public void actionPerformed(AnActionEvent e) {
        project = e.getData(PlatformDataKeys.PROJECT);
        //獲取當前項目的根路徑
        Constant.PROJECT_ROOT_DIR = project.getBasePath();
        Log.pln("PROJECT_ROOT_DIR = " + Constant.PROJECT_ROOT_DIR);
//        e.getData(PlatformDataKeys.EDITOR).getDocument();//讀取的是當前文檔
        //解析指定目錄的lint文件
        List<String> resList = XmlPraser.parseUnusedResXml(Constant.LINT_RESULT_XML_PATH);
        Log.pln("List = " + resList.toString());
        //獲取指定文件的document
        Document lintResultXmlDoc = ConfigUtils.getConfigDocument(Constant.LINT_RESULT_XML_PATH);
        PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(lintResultXmlDoc);

        // 獲取當前編輯器中的文件
//        Project project = e.getData(PlatformDataKeys.PROJECT);
//        Editor editor = e.getData(PlatformDataKeys.EDITOR);
//        PsiFile file = PsiUtilBase.getPsiFileInEditor(editor, project);
        // 獲取當前類
//        PsiClass targetClass = getTargetClass(editor, file);
        //獲取元素操作的工廠類,通過這個工廠可根據字符串創建方法、屬性、類  element指的就是方法、屬性、類等
//        PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
//        new DeleteUnuseResAction(project,psiFile).execute();
        new DeleteUnuseResAction(project, psiFile).executeAction();

    }

package util;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by zhongwr on 2016/11/19.
 */
public class XmlPraser {
    /**
     * 例子:
     * <student id="5" group="5">
     * <name>小明</name>
     * <sex></sex>
     * <age>18</age>
     * <email>[email protected]</email>
     * <birthday>1987-06-08</birthday>
     * <memo>好學生</memo>
     * </student>
     * <p>
     * switch (eventType) {
     * //文檔開始
     * case XmlPullParser.START_DOCUMENT:
     * list=new ArrayList<Student>();
     * break;
     * //開始節點
     * case XmlPullParser.START_TAG:
     * //判斷如果其實節點爲student
     * if("student".equals(nodeName)){
     * //實例化student對象
     * student=new Student();
     * //設置Id屬性
     * student.setId(Integer.parseInt(xmlPullParser.getAttributeValue(0)));
     * //設置Group屬性
     * student.setGroup(Integer.parseInt(xmlPullParser.getAttributeValue(1)));
     * }else if("name".equals(nodeName)){
     * //設置name
     * student.setName(xmlPullParser.nextText());
     * <p>
     * }
     * break;
     * //結束節點
     * case XmlPullParser.END_TAG:
     * if("student".equals(nodeName)){
     * list.add(student);
     * student=null;
     * }
     * break;
     * default:
     * break;
     * }
     * eventType=xmlPullParser.next();
     * }
     * <p>
     * <p>
     * pull解析lintxml--獲取要刪除資源的路徑 如:d:/results.xml
     *
     * @return
     * @author zhongwr
     */
    public static List<String> parseUnusedResXml(String filePath) {

        List<String> pathLists = new ArrayList<String>();
        FileInputStream fis = null;
        InputStream is = null;
        File file = new File(Constant.PROJECT_ROOT_DIR + filePath);
        if(!file.exists()){
            Log.pln("請確保lint-result.xml文件存在");
            return pathLists;
        }
        try {
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            // 獲取XmlPullParser實例
            XmlPullParser pullParser = factory.newPullParser();
            File resFile = new File(Constant.PROJECT_ROOT_DIR + filePath);
            if (!resFile.isFile()) {
                System.out.println("file is not exist");
                return pathLists;
            }
            Log.pln("parseUnusedResXml = " + Constant.PROJECT_ROOT_DIR + filePath);
            fis = new FileInputStream(file);
            is = new BufferedInputStream(fis);
            pullParser.setInput(is, "UTF-8");
            // 開始
            int eventType = pullParser.getEventType();
            boolean isUnusedPath = false;
            while (eventType != XmlPullParser.END_DOCUMENT) {// 文檔沒讀取完
                String nodeName = pullParser.getName();
                switch (eventType) {
                    // 文檔開始
                    case XmlPullParser.START_DOCUMENT:
                        pathLists = new ArrayList<String>();
                        break;

                    // 開始節點
                    case XmlPullParser.START_TAG:

                        // System.out.println( "START_TAG Node = " + nodeName);
                        // 過濾要刪除的未使用的資源
                        if ("issue".equals(nodeName) && "UnusedResources".equals(pullParser.getAttributeValue(0))) {
                            isUnusedPath = true;
                            System.out.println("UnusedResources Node = " + nodeName);

                        } else if ("location".equals(nodeName) && isUnusedPath) {
                            System.out.println("location Node = " + nodeName);
                            String path = pullParser.getAttributeValue(0);
                            System.out.println("path = " + path);
                            // 刪除指定目錄下的資源,防止刪除lib下的資源 以及 過濾掉不需要刪除的目錄
//                           String rootDir =  Constant.PROJECT_ROOT_DIR.replace("\\","\\\\");
                            //測試使用:
                           String rootDir =  "D:\\soft\\studio_project\\DeleteUnusedRes";
                            Log.pln("rootDir " + rootDir);
                            Log.pln("path tru ? " + (path.startsWith( rootDir+ "\\app")));
                            if (path.startsWith(rootDir + "\\app")
                                    && !path.contains("res\\values")) {
                                pathLists.add(path);
                            }
                        }
                        break;
                    // 結束節點
                    case XmlPullParser.END_TAG:
                        if ("issue".equals(nodeName)) {
                            isUnusedPath = false;
                        }
                        // System.out.println( "END_TAG Node = " + nodeName);
                        break;
                    default:
                        break;
                }
                // 手動的觸發下一個事件
                eventType = pullParser.next();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        System.out.println("pathLists = " + pathLists.size());
        return pathLists;
    }
}

通過project獲取當前項目,可以獲取到當前項目的根目錄,得到根目錄之後就可以隨意操作目錄的文件了,得到根目錄之後,配置lint-results.xml的路徑就可以解析lint的文件從而得到了無用資源(drawable、layout)的絕對路徑,然後執行DeleteUnuseResAction刪除這些資源

/**
 * 刪除無用資源管理
 * Created by zhongwr on 2016/6/17.
 */
public class DeleteUnuseResAction extends WriteCommandAction.Simple {

    public DeleteUnuseResAction(Project project, PsiFile... files) {
        super(project, files);
    }

    @Override
    protected void run() throws Throwable {
        Log.pln("===run===run==");
    }


    /**
     * 刪除無用資源
     */
    public void executeAction() {
        final List<String> pathList = XmlPraser.parseUnusedResXml(Constant.LINT_RESULT_XML_PATH);
        int size = pathList.size();
        Log.pln("size = "+size);
        String filePath = null;
        for (int i = 0; i < size; i++) {
            filePath = pathList.get(i);
            try {
                FileUtil.copyFile(filePath,
                        Constant.BACKUP_PATH + filePath.substring(filePath.lastIndexOf("\\") + 1, filePath.length()));
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                FileUtil.deletFile(filePath);
            }
        }
    }
}
這個action是繼承自
WriteCommandAction.Simple

這個類會實現一個run()方法,它是一個工作線程,所有耗時的操作都可以放在這裏執行,感興趣的可以看看源碼實現,其實它也是封裝了子線程的調用,可以查看我上傳的API文檔說明,那裏有源碼的子線程調用;

但是這裏我嘗試了刪除文件沒放在run()方法中執行也是可以的,但是最好放置在子線程中刪除吧;

這裏刪除資源之前我們先將其備份,默認備份在d:/dele_res_backup/目錄下,可以在配置文件自行修改;備份完後就可以安安心心將資源文件刪除了。

package util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;

/**
 * Created by Administrator on 2016/11/19.
 */
public class FileUtil {
    /**
     * 刪除 未使用的冗餘資源(圖片 xml佈局)
     * <p>
     * false 顯示資源列表
     * <p>
     * true 顯示資源列表 並刪除資源
     *
     * @throws Exception
     */
    public static void deletFile(String filePath) {
        File file = new File(filePath);
        System.out.println("file.isFile() = " + file.isFile());
        if (file.isFile() && file.exists()) { // 判斷文件是否存在
            try {
                file.delete();
                System.out.println("刪除成功");
            } catch (Exception e) {
            }
        }
    }

    /**
     * 複製單個文件:將刪除的文件備份
     *
     * @param oldPath
     *            String 原文件路徑 如:c:/fqf.txt
     * @param newPath
     *            String 複製後路徑 如:f:/fqf.txt
     * @return boolean
     * @author zhongwr
     */
    public static void copyFile(String oldPath, String newPath) {
        try {
//       System.out.println("oldPath = " + oldPath + " \\n newPath = " + newPath);
            int byteread = 0;
            File newfile = new File(newPath);
            if (new File(newPath).exists()) {
                System.out.println("文件已存在 : " + newPath);
                return;
            }
            if (!newfile.getParentFile().exists()) {
                boolean isOk = newfile.getParentFile().mkdirs();
                System.out.println("isOk = " + isOk);
            }
            File oldfile = new File(oldPath);
            if (oldfile.exists()) { // 文件存在時
                InputStream inStream = new FileInputStream(oldPath); // 讀入原文件
                FileOutputStream fs = new FileOutputStream(newPath);
                byte[] buffer = new byte[inStream.available()];
                while ((byteread = inStream.read(buffer)) != -1) {
                    fs.write(buffer, 0, byteread);
                }
                inStream.close();
                System.out.println("複製文件成功  = " + oldPath);
            } else {
                System.out.println("文件不存在 ");
            }
        } catch (Exception e) {
            System.out.println("複製單個文件操作出錯");
            e.printStackTrace();
        }
    }
}
有些註釋是我故意寫上去備註的,方便理解,也可作爲參考使用吧。

通過這個例子會發現所有的代碼感覺都好熟悉有木有,其實都是java代碼,就連解析xml都可以是xpull解析,所以我們做安卓的很容易上手,難點就是理解Project、Document、PSiFile等等新概念這些建議邊用邊差查文檔吧,畢竟我們不是專門開發插件的,隨用隨查也是一項重要的開發技能咯。到這基本完事了。例子可直接使用也可作爲參考,demo地址如下:

http://download.csdn.net/detail/zhongwn/9688658



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章