android(或java中)怎样在自己的jar包里集成第三方类库并且可用(不会报错:java.lang.NoClassDefFoundError....)

        估计不少coder friends 在开发android或java项目时都有过自己打jar包给别人使用的经历,这其中有一些人可能跟我一样在打jar包时会遇到一个问题:在打jar包的整个过程中一切正常,如果这个jar里的源代码都是自己所写(没有单独引用第三方开源库),那么不出意外的话这个jar就能正常在apk中使用,但是如果我的jar里又引用了其他的jar(或者直接在maven,gradle等构建工具中集成开源库),那么在apk中使用我们的jar包时可能会报错:"java.lang.NoClassDefFoundError....",这个异常一般指向的是你引用的第三方开源库。这时你很奇怪:这个类怎么可能没定义?这个第三方的开源库明明就在啊?而且我在代码里还能正常使用,写代码时编辑器(比如intellij idea或android studio等)还能自动提示补全呢。当你确定了报错中指向的类确实存在时,这时你就可以考虑报这个错的原因可能是因为打jar包存在问题了。如果你只关心我怎么解决这个问题而不关心我分析出现问题的过程,那么请直接跳到下面"解决过程"的部分。

也可以先看看这个,可能更好点(https://blog.csdn.net/Builder_Taoge/article/details/81183871).

分析过程

这一部分写了一大堆可能感觉是废话的内容,因为真正解决问题的步骤还是下面"解决过程"那一部分。不过这是我遇到这个问题的分析过程,之所以写出来是想给各位一个思路,因为我的这篇文章里说的方式也可能解决不了你的问题,假如确实没有解决你的问题,也许你可以通过我这个分析过程得到一些启发,从而自己找到了适合的解决方式,这才是我写这段的目的。下面开始叙述我的分析过程:

       显然,如果这个类确实有,那么还报这个错的话基本上可以断定是这个类文件根本没有被打包进你的jar包,所以程序找不到这个类。你可能感觉有点啰嗦了,会说:我当然知道这个第三方的开源库没有打进我的jar包,因为解压我的jar包后只能看到我自己写的源文件编译成的class文件[捂脸.png],关键是为什么以及怎么解决啊?
好吧,惭愧的是我现在也没完全确定是不是我想的这样,只是在猜测阶段,不过问题确实是通过这种方式解决了。因为我对gradle,maven这类的工具用的不是很熟,只知道肯定是哪里配置出问题了,比如文件的路径配置,构建脚本(.gradle或者pom.xml等)的写法等。OK,既然用工具不行,那我就手动来吧。下面以我自己的jar包集成okhttp为例说明我是怎么解决的,如果您有更靠谱简单的方式真心希望能告知一下。

图1.

如图1,最开始我在build.gradle中通过两种方式集成okhttp相关(一共两个)的开源库:

①直接集成,交给gradle去下载。

②通过自己下载jar包然后放在我项目的libs目录下的方式。

注意我没有一起用这两种方式,是分别尝试的,这里只是为了方便说明过程才放在一起的,省略几张配图。

我用gradle相关的命令构建自己的jar包OK,但在apk中使用我的jar时就遇到如题的异常,接下来我就使用了各种方式:把gradle中的"implementation"关键字换为"api"关键字,不行;再换为compileOnly等等(它们之间的区别请看:https://stackoverflow.com/questions/47365119/gradle-dependency-configuration-implementation-vs-api-vs-runtimeonly-vs-compil)还不行,又换成compile,provided等仍然不行,结果都是我自己的jar包都能正常创建,但在apk中使用时都报okhttp中的某个类没定义的异常。(注意:这里的compile和前面的implementation意思一样,provided对应前面的"api",这是gradle旧版本中的写法,现在过时了并且会在2018年底彻底移除。)
我没办法了,心想既然不能通过gradle工具将第三方的jar集成在我自己的jar中,我只有试试手动的方式了。

解决过程

思路:既然报错说okhttp中某个类没找到(或没定义),那么就是没有被合并到我的jar包中,所以我将它合并到我的jar包中就完了:

①把你自己的jar文件和okhttp相关的两个jar文件都放在同一个文件夹下,在当前目录下打开命令行,然后用jar -xvf 命令挨个解压(下面图2,3,4):

图2.

图3.

图4(解压).


②解压后把用到的这几个jar包都删除了,剩下解压后的文件目录:

图5

.
③通过打包命令进行合并打包:

jar -cvfm <你的jar包名>  [解压后的清单文件]  <要被打成jar包的class文件,可以是包含它们的目录>:

图6.


④将生成的新jar包解压缩一下,查看一下新的jar包里是不是包含了你自己的jar和第三方jar的目录结构,如果操作正常,那么这就完事了,你可以再试试这个新的jar包是否已经可用了。

一些说明:


jar工具和java以及javac一样都是jdk自带的工具,只要你配置了java_home的环境变量,就可以像java或者javac一样,在任何地方使用,不用我再多说。
图中解压jar包以及打jar包的一些命令参数可以通过jar -h帮助命令自行查看,如图7

图7.

比如-v是打印执行过程中的输出日志,-f是自定义jar包的名字等。对于-C(大写C,和小写c不一样)稍微说一下:如果你想在任意目录下执行打包命令,那么-C命令可以帮你"重定向'一下,-C <目录名> 的格式就是你要在哪个目录下执行你的jar命令。相当于先执行"cd 目录"再在这个目录下执行去掉-C后的命令(https://www.cnblogs.com/wangshuo1/p/5697746.html)。比如执行jar -cvf new.jar -C /demo就相当于两步:
<1>打开demo目录:cd /demo
<2>在demo目录下执行 jar -cvf new.jar命令。
最基础的打包命令像图7最下面给的示例那样:
jar -cvf <你的jar包名字> <需要被打成jar包的所有class文件,用空格隔开所有class文件> 。

但实际上我们的所有class文件并不是都在同一个目录下,如果挨个把完成路径都列出来,那会是很麻烦的事。这个问题使用通配符的方式轻易解决:对,就是你看到的图6中打包命令最后那个实心的点号".",它就是代表当前目录,用它来表示当前目录下的所有class文件,jar工具会自动递归它下面的所有目录找class文件,你再也不用担心自己写所有class文件目录的问题了。
你会看到图6打包的命令中有".\META-INF\MANIFEST.MF"这一串文字,它是干嘛的?是必须的吗?其实,你下载的第三方开源库的jar包中解压后一般都有这个文件,这是打jar包时生成的一个清单文件,用来描述jar包的一些信息,如果你在打jar包时加上了-m参数,它就会自动创建这个文件了,不加这个参数就没有这个清单文件,不创建这个清单文件应该没太大影响。但是如果你要合并的多个jar中有这个文件的话,你最好合并的时候把这个清单文件用上,相应的打包命令中也要加上-m参数,就是图6中的这个样式,图6中的这个写法是这个清单文件的路径而已。如果所有jar包中都没有清单文件,那么不要加-m参数,只写jar -cvf .就OK了(别忘点号),甚至-v也不用,如果你不想看打包过程中的输出信息的话。

最后一个重点来了

一般我们在github上找的开源库没有jar包的形式啊?只告诉我用gradle的话就直接在gradle文件中 compile "...",用maven的话就<dependency>...</dependency>进行配置,这又怎么办?

遇到这个问题,首先说,一般GitHub上知名的开源库都会在自己的官网提供jar包,你会在页面某个地方看到它提供的官网网址,进去以后找找有没有jar包的格式,或者在GitHub上它直接就给出下载jar包的连接,比如下图。

图8.

如果还是没有怎么办?
呃~~那我们就把这个开源项目down下来自己打个jar包呗。至于怎么将开源库的源代码打打成jar包,我本来不想写了,篇幅太长了,再说我也是网上找的,各位自己去网上搜一下就OK了,不过想了想,还是简单写一下吧,不一定涵盖所有情况:
<1>如果开源库是maven形式的构建方式(比如okhttp),你最好下载一个maven工具安装了,然后你把想打成jar包的开源库源码用git clone下来,在项目目录下执行mvn package 命令就可以自动打包了,打出来的包在项目名/target目录下,比如我用okhttp源码打包,生成的jar包在okhttp/target目录下。

注意,这里需要给maven配置环境变量,要不然它可不是在任何目录下都能直接使用


<2>如果是gradle构建的开源库(比如glide),那可能就有些麻烦了,为什么说可能?因为gradle的所有用法我还不清楚,也许有一行命令就能解决问题的方式,但我现在不知道[笑哭脸.png].gradle是通过一个个的任务"task"来进行项目构建的,所以要想通过gradle打成jar包,你得找到人家项目中相应gradle文件里定义的打包task,然后通过"gradle task<名字>" 的命令方式就能打包了,问题是这个任务在哪个build.gradle中你可能需要花时间去找了,接下来我暂时就有些无能为力了[衰.gif].不过,记住先找找人家是不是提供了jar下载的连接,或者提供了官网链接,如果有就OK了。
 

猜你喜欢

转载自blog.csdn.net/Builder_Taoge/article/details/81180487