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免费版

1 jdk 版本 --> 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>xiaoming@163.com</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



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值