Android Eclipse 导入 AS Gradle AAR 库手冊

序言

这是一篇半技术类文章。众所周知如今Google主推Android Studio开发工具。而Eclipse已经被闲置一阵子了,可是Eclipse项目却还有非常多没有迁移到AS中。而如今一些新的库都是採用AS Gradle打包并公布到公共仓库中。而这些库Eclipse 并不能直接使用。在本篇文章中将解说怎样导入一个或者多个库到Eclipse中使用。

目的

Eclipse基本算是荒废的状态了。这并非说Eclipse就不好。毕竟都是Eclipse过来的;仅仅能说一个个是新欢一个旧情人了。

旧情人肯定是有她特殊的韵味的,所以如今并非所有的都迁移到AS了,还有非常大一部分处于Eclipse上的项目。

这里就有问题了,很多的开发人员在AS上的库一般来说都不是打包为Jar包。而是直接生成AAR库,随后公布到公共仓库中,AS的使用者呢就使用一句简单的代码就能使用这个库了,其下载-载入-编译-执行都用Gradle搞定了。而Eclipse就不乐意了~~

Eclipse 就面临着尴尬的局面想要用新的库,可是却没法用~~
在本篇文章中。将会解说怎样在Eclipse中使用一个公共仓库中的库。

Gradle 公布流程

仓库简单介绍

在AS中公布库都是採用Gradle公布到相应的公共仓库中进行使用,仓库有非常多。你也能够自己使用server搭建仓库;而公共仓库中有两个特别OK的:

在最初google框架中默认使用的仓库是:mavencentral 。可是由于mavencentral 仓库的公布流程较为严格并且操作上不够人性化所以后来改为了:jcenter。

所以推荐使用jcenter,当然假设你在mavencentral中公布了你的库,那么这个库在公布成功后的24小时内会被同步到jcenter中,而jcenter中公布的库也能够通过一键式操作公布到mavencentral仓库中。

由于有上面的仓库差别,所以假设你使用的是AS将会在配置中看见诸如这种配置:

repositories {
    jcenter()
}
repositories {
    jcenter {
        url "http://jcenter.bintray.com/"
    }
}
repositories {
    mavenLocal()
}

这就是在告诉你的项目中使用的库的来源。AS将会更加库的标识去寻找须要的文件,当然两个仓库能够同一时候写入到项目配置中。

公布到仓库

在我的文章中有一篇关于公布到mavencentral仓库的文章: [Publish AAR To Maven] 使用 Gradle 公布 AAR 到 Maven 仓库

上面所介绍的是公布到mavencentral仓库的流程,而jcenter的流程也基本相似,仅仅只是把文件加密的过程放到了server上去完毕。

总体来说:

  1. 首先我们须要有一份库的代码
  2. 编译源代码得到Jar, R, JNI, xml等源代码文件。

  3. 打包所有文件为一个aar的文件
  4. 签名
  5. 公布到仓库
  6. 校验并公布

怎样使用

而使用这个库,则也非常easy下载相应版本号的AAR文件,加入到项目中,并编译进项目。
从这里就能够看出一点,实际的android使用中就是使用的aar文件,仅仅只是此时的aar文件会被解包编译。

在AS的项目配置中我们加上这样一句:

dependencies {
    compile 'net.qiujuer.genius:ui:1.3.0'
}

在编译后,我们查看一下项目的Build文件夹:
这里写图片描写叙述

在这里我们能够看见实际的使用中的确如我们所想的一般。

当然这里之所以会出现“res“包,是由于“genius-ui“包中默认引用了“genius-res“包,所以当我们使用“UI“包的时候就会同步下载“res“包。具体能够见开源项目:Genius-Android

OK, 一个库的简单流程我们说好了;那么来看看怎样在Eclipse中使用。

问题

从上面的库的流程中能够看出一个AAR库中能够包含:jar, aidl, assets, res, R 文件等数据。而Eclipse中默认引用库的时候仅仅能引用jar文件。

假设我们仅仅是把jar文件放到eclipse中的话将会无法正常使用相应的资源文件。那么是不是仅仅要同一时候把资源文件放到eclipse中也就攻克了这个问题?并非的!

众所周知,一个资源文件在编译的时候会在R文件里生成一个int 值与之相相应。而实际代码中所有使用这个int值取代;那么上面加入的资源文件所有会生成一份新的值与之相应。那么在库的jar中依旧无法找到与之相应的资源,终于将会导致APP崩溃。

此时有一个办法能够解决,那就是固化资源相应的INT值,让改资源在编译的时候每次都生成我们指定的INT值。

在这里我们使用一个android中特有的文件:“public.xml

public.xml: 用于固化资源所相应的INT值。

实施

下载

首先我们须要准备一份相应的aar文件,在这里我使用 genius-android 库的 UI 包来完毕该操作。

下载点有:

我们进入mavencentral,并搜索 genius ,找到 ui 包:

这里写图片描写叙述

由于UI包中引用了res包。所以我们同一时候下载ui和res包。

解包

更改后缀为zip,并解压:

这里写图片描写叙述

  • AndroidManifest.xml :含有权限-服务声明-广播注冊-activity声明等信息。直接将当中的数据复制到自己的项目中就OK。
  • aidl:这个中一般都是独立服务的东西。在项目中依照相应的地方建立好文件就好。
  • assets:这个中通常是字体。html等数据
  • classes.jar 这个就是我们须要直接使用的jar包了
  • R.txt 这个就是资源相应的int值了,我们须要做的主要工作就是把其转换为 public.xml 文件
  • Res 文件夹中的内容能够原封不动的复制到项目的res文件夹中。

创建项目

为了避免该项目与自己的项目相冲突。我们首先须要建立项目:

这里写图片描写叙述

在这里起名为:GeniusUI,并指定包名为:”net.qiujuer.genius.ui”
为什么呢?由于上面解压后的AndroidManifest.xml中有指定包名:

这里写图片描写叙述

假设这里未指定包名,那么就算后面我们写好了 public.xml文件,其尽管能固化INT值,可是却没法相应到我们的包名上。换句话说我们的 jar 文件里依旧无法找到相应的资源文件。

进入项目属性(Properties)-Android-勾选上(Is Library) 使其成为库项目。

这里写图片描写叙述

请记住在这里我们并不为RES包单独创建项目;由于UI库中引用了RES包中的资源。那么在UI库生成的时候将会又一次计算一份新的Res库的资源INT值。


在这里之所以要下载Res包,仅仅是仅仅须要当中的资源文件而已。

同一时候打开 Res包中的 R文件(左边)与UI包中的R文件(右边):

这里写图片描写叙述

能够看出UI包中同样含有颜色相应的INT值。可是其值与原Res包中并不同样。

复制文件

我们依照图片所看到的把现有的文件所有拷贝进项目中:

这里写图片描写叙述

此时编译一次项目。一般来说仅仅要文件所有拷贝正确了,那么是不应该报错了,并且此时应该生成了一份R文件,仅仅只是此时的R文件与原来库中的值并不全然同样(有可能有部分同样,这也就导致有部分资源能够正常使用)。

对照

此时我们对照项目生成的R文件与库中的R文件:

这里写图片描写叙述

能够发现不尽同样。当然你的也也许全然同样。比方:

这里写图片描写叙述

这意味着什么?意味着这个项目中的资源能够全然使用。可是什么情况下会不同?

我们在 Res 项目中的 values.xml 文件里加上一个颜色:

<color name="a">#ffecb3</color>

这句话将会带来什么?带来兴许的ID所有相应失败的问题:

这里写图片描写叙述

这也就是我说的资源无法正常相应的问题。
这个同样和不同样都是随机的,也许原来的库刚刚巧合了那么也就同样了。

Public.xml 固化

此时我们在 项目的 values 文件夹中创建 public.xml 文件,并加上:

<?

xml version="1.0" encoding="utf-8"?> <resources> <public id="0x7f020000" name="amber_100" type="color" /> <public id="0x7f020001" name="amber_200" type="color" /> <public id="0x7f020002" name="amber_300" type="color" /> <public id="0x7f020003" name="amber_400" type="color" /> </resources>

编译项目,此时我们再次查看 R 文件:

这里写图片描写叙述

在这里能够看出我们写了的部分已经OK了,从第5个開始又出现故障了,所以啊,我们须要把所有的R相应的INT值都固化到Public中。

好了,如今知道Public文件的魅力了。可是想要固化一整个文件并非那么的如意;我一打开UI库的R文件的时候是崩溃的。

这里写图片描写叙述

我擦,500多汗~~
坑爹呢不是。。
并且大概看了一下有颜色-数字-style-TM 还有 styleable 这是什么> 鬼?假设你写过自己定义控件那么应该知道这个是:自己定义属性。

可是,可是这个怎么固化?500多行。等弄好菜都凉了。

代码化

我们是程序猿,我们都非常懒,所以我们用程序来解决。
在这里我们写一个从 R.txt 转换到 public.xml 的小程序就好。

格式

首先来看看R.txt 文件里转换到 XML 时相应的格式。

  • 颜色、数字、数组、ID、String、drawable能够直接转换
int color black 0x7f04000e
int dimen font_10 0x7f050000
int array loading 0x7f030000
int id all 0x7f080009
int string g_font_file 0x7f060000
int drawable background 0x7f020000
<public type="color" name="black" id="0x7f04000e" />
<public type="dimen" name="font_10" id="0x7f050000" />
<public type="array" name="loading" id="0x7f030000" />
<public type="id" name="all" id="0x7f080009" />
<public type="string" name="g_font_file" id="0x7f060000" />
<public type="drawable" name="background" id="0x7f020000" />
  • style 须要替换“_“为“.“
int style Genius_Widget_BalloonMarker 0x7f070000
<public type="style" name="Genius.Widget.BalloonMarker" id="0x7f070000" />
  • attr属性须要抛弃styleable部分
int attr gAllowTrackClickToDrag 0x7f010022
...
int[] styleable AbsSeekBar { 0x7f01000c, 0x7f010019, 0x7f01001a, 0x7f01001b, 0x7f01001c, 0x7f01001d, 0x7f01001e, 0x7f01001f, 0x7f010020, 0x7f010021, 0x7f010022, 0x7f010023, 0x7f010024, 0x7f010025, 0x7f010026, 0x7f010027, 0x7f010028, 0x7f010029, 0x7f01002a, 0x7f01002b, 0x7f01002c }
int styleable AbsSeekBar_gAllowTrackClickToDrag 10

要得到 gAllowTrackClickToDrag 所代表的INT值,两种办法,直接从第一句中转换得到。


第二个办法,读取到 styleable AbsSeekBar 时存储后面的数组,然后读取到 int styleable AbsSeekBar_gAllowTrackClickToDrag 10 时,依照 10 这个下标去取得值 0x7f010022

<public type="attr" name="gAllowTrackClickToDrag" id="0x7f010022" />

代码

实体类

    static class PublicLine implements Comparable<PublicLine> {
        public String type;
        public String name;
        public String id;

        public PublicLine() {
        }

        public PublicLine(String type, String name, String id) {
            this.type = type.trim();
            this.name = name.trim();
            this.id = id.trim();
        }

        public String getKey() {
            return type + "_" + name;
        }

        @Override
        public String toString() {
            return "<public type=\"" + type + "\" name=\"" + name + "\" id=\"" + id + "\" />";
        }

        @Override
        public int compareTo(PublicLine o) {
            int i = this.type.compareTo(o.type);
            if (i == 0)
                return this.name.compareTo(o.name);
            else
                return i;
        }
    }
  • 实现 compareTo 是用于后面排序,避免输出的内容乱糟糟的。
  • “getKey()“ 方法是为了存储到Map中的主键值而准备。避免反复。
  • “toString()“这是为了输出日志而准备。

总体流程

    public static void main(String[] args) throws IOException {
        File in = new File("R.txt");
        File out = new File("public.xml");

        if (!in.exists()) {
            throw new NullPointerException("R.txt is not null.");
        }

        try {
            out.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }

        System.out.println(in.getAbsolutePath());
        System.out.println(out.getAbsolutePath());

        InputStreamReader read = new InputStreamReader(new FileInputStream(in));
        BufferedReader bufferedReader = new BufferedReader(read);

        OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(out));
        BufferedWriter bufferedWriter = new BufferedWriter(writer);

        Map<String, PublicLine> xml = new HashMap<>();
        buildXml(bufferedReader, xml);

        List<PublicLine> lines = new ArrayList<>();
        lines.addAll(xml.values());

        Collections.sort(lines);

        saveFile(lines, bufferedWriter);

        close(bufferedReader);
        close(bufferedWriter);

        System.out.println("End.");
    }
  1. 整个流程非常easy,直接使用当前文件夹生成两个文件。R.txt 为输入源,public.xml 则是输出文件。该文件每次执行都又一次生成新文件。

  2. 然后输出一次两个文件的文件夹信息到控制台。

  3. 然后初始化Buffer,在这里经过2层的分装,终于得到的是:BufferedReader 与 BufferedWriter,才有这两个的目的主要是为了一行行的读取,终于也一行行的输出。

  4. 然后声明一个 Map 变量。用于存储读取并转换为实体的集合。之所以採用Map是为了实现避免反复内容的出现。

  5. 再后面我们又声明了一个 List 变量。并把Map的值存储到List中,这一步主要是为了排序整个集合;Map本身是无序的,并且假设直接对Map排序就相当复杂,所以这里我们填充到 List 后再进行排序。

  6. 然后则是输出到Public文件里,然后关闭流的操作。

读取集合

    public static void buildXml(BufferedReader reader, Map<String, PublicLine> xml) {
        while (true) {
            String line;
            try {
                line = reader.readLine();
                if (line == null || line.trim().length() == 0)
                    return;
            } catch (IOException e) {
                e.printStackTrace();
                continue;
            }

            if (line.contains("styleable")) {
                // skip styleable array
                continue;
            } else {
                // convert other xml
                String[] split = line.split(" ");
                if (split.length == 0)
                    continue;

                String type = split[1];
                String name = split[2];
                String id = split[3];
                if (type.contains("style"))
                    name = name.replace("_", ".");
                saveToMap(xml, new PublicLine(type, name, id));
            }
        }
    }
  1. 循环读取每一行,当读取到某行空的情况下则退出循环
  2. 在这里我们对attr的转换採用过滤的方式。所以凡事具有“styleable“ 标示的行都能够直接抛弃掉。
  3. 正常情况下。我们读取一行,并按空格划分开;然后分别读取到 type 、name、id 的值。

  4. 假设当前类型为 style 类型。那么我们的名称须要把 “_” 替换为 “.”
  5. 把当前识别到的信息存储到 Map 中。

假设这里对 attr 的识别採取的是读取 styleable 数组中的信息。然后再按索引找寻相应INT值的话,代码就应该是这样:

    public static void buildXml(BufferedReader reader, Map<String, PublicLine> xml) {
        while (true) {
            String line;
            try {
                line = reader.readLine();
                if (line == null || line.trim().length() == 0)
                    return;
            } catch (IOException e) {
                e.printStackTrace();
                continue;
            }

            String[] split = line.split(" ");
            if (split.length == 0)
                continue;

            if (line.contains("int[]")) {
                // convert attr xml
                String name = split[2].trim();
                line = line.substring(line.indexOf("{") + 1, line.lastIndexOf("}"));
                System.out.println(line);

                String[] ids = line.split(",");
                if (ids.length > 0) {
                    readStyleableXml(reader, xml, ids, name);
                }
            } else {
                // convert other xml
                String type = split[1];
                String name = split[2];
                String id = split[3];
                if (type.contains("style"))
                    name = name.replace("_", ".");
                saveToMap(xml, new PublicLine(type, name, id));
            }
        }
    }

    @SuppressWarnings("unused")
    public static void readStyleableXml(BufferedReader reader, Map<String, PublicLine> xml, String[] ids, String name) {
        for (String id : ids) {
            String line;
            try {
                line = reader.readLine();
                if (line == null)
                    continue;
            } catch (IOException e) {
                e.printStackTrace();
                continue;
            }
            String[] split = line.split(" ");

            String lName = split[2].substring(split[2].indexOf(name) + name.length() + 1);
            String lId = ids[Integer.parseInt(split[3].trim())];
            saveToMap(xml, new PublicLine("attr", lName, lId));
        }
    }

在这里我们就须要多一个方法,在这种方法中我们依照当前控件的数组长度进行循环读取其后相应数组长度的行,然后再解析并存储。

存储

    public static void saveToMap(Map<String, PublicLine> xml, PublicLine line) {
        try {
            xml.putIfAbsent(line.getKey(), line);
            System.out.println(">>>: " + line.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

存储方法相对简单,我们採用 Map 的 “putIfAbsent“ 方法。该方法有一个作用,就是会推断当前 KEY 是否存在,假设不存在则存储。

存储


    public static void saveFile(List<PublicLine> lines, BufferedWriter writer) throws IOException {
        // write head
        writer.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
        writer.append("\n");
        writer.append("<resources>");
        writer.append("\n");

        for (PublicLine line : lines) {
            try {
                writer.append(" ");
                writer.append(line.toString());
                writer.append("\n");
                writer.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        // write footer
        writer.append("</resources>");
        writer.flush();
    }

存储过程分为存储头部。存储集合数据。以及存储底部来完毕。

关闭流

    public static void close(Closeable closeable) {
        try {
            closeable.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

关闭流的方法就更加简单了,无非就是单独出来了而已。

效果

此时我们把R文件放到根文件夹,执行一次代码,控制台将会输出:

这里写图片描写叙述

此时已经生成了一份 public 文件。与R文件相应看一下:

这里写图片描写叙述

能够看出效果是相当不错。把public文件复制到库中直接投入使用。

预览

为了測试是否成功,我依照开源库 Genius-Android 中的 sample 项目代码在 Eclipse 中完毕了一份界面布局。

这里写图片描写叙述

所有控件与资源都能正常使用。

最后

在这里我把代码生成了一份 jar 文件,你能够下载该文件,并把 R.txt 文件放到同一文件夹,执行该 jar 文件既可得到 public 文件。

同一时候我把所有的源文件与代码都上传到了 GtiHub 的 BeFoot 项目中;以后的所有样例也都会更新到该项目中。

假设有哪里不正确,或者说的不够清楚还请多多不吝赐教。

========================================================
作者:qiujuer
博客:blog.csdn.net/qiujuer
站点:www.qiujuer.net
开源库:github.com/qiujuer/Genius-Android
开源库:github.com/qiujuer/Blink
转载请注明出处:http://blog.csdn.net/qiujuer/article/details/50084345
—— 学之开源,用于开源;刚開始学习的人的心态,与君共勉!

========================================================

posted @ 2017-07-20 08:22  wzzkaifa  阅读(611)  评论(0编辑  收藏  举报