一个非常好的从jar文件中加载so动态库方法,在android的gif支持开源中用到。这个项目的gif解码是用jni c实现的,避免了OOM等问题。

项目地址:https://github.com/koral--/android-gif-drawable

如果是把java文件生成jar。jni生成的so文件放到使用apk的libs/armeabi/lib_gif.so.....

gifExample.apk:

/libs/gif.jar

/libs/armeabi/lib_gi.so

这样做会报错,提示xml里面找不到GifImageView。

只能用项目之间依赖,so会自动进入生成的apk,不用拷贝。

调用方法:

  1. //开始调用:
  2. static {
  3. LibraryLoader.loadLibrary(null, LibraryLoader.BASE_LIBRARY_NAME);
  4. }

进入这里:

  1. package pl.droidsonroids.gif;
  2.  
  3. import android.content.Context;
  4. import android.support.annotation.NonNull;
  5.  
  6. import java.lang.reflect.Method;
  7.  
  8. /**
  9. * Helper used to work around native libraries loading on some systems.
  10. * See <a href="https://medium.com/keepsafe-engineering/the-perils-of-loading-native-libraries-on-android-befa49dce2db">ReLinker</a> for more details.
  11. */
  12. public class LibraryLoader {
  13. static final String SURFACE_LIBRARY_NAME = "pl_droidsonroids_gif_surface";
  14. static final String BASE_LIBRARY_NAME = "pl_droidsonroids_gif";
  15. private static Context sAppContext;
  16.  
  17. /**
  18. * Intitializes loader with given `Context`. Subsequent calls should have no effect since application Context is retrieved.
  19. * Libraries will not be loaded immediately but only when needed.
  20. * @param context any Context except null
  21. */
  22. public static void initialize(@NonNull final Context context) {
  23. sAppContext = context.getApplicationContext();
  24. }
  25.  
  26. static Context getContext() {
  27. if (sAppContext == null) {
  28. try {
  29. final Class<?> activityThread = Class.forName("android.app.ActivityThread");
  30. final Method currentApplicationMethod = activityThread.getDeclaredMethod("currentApplication");
  31. sAppContext = (Context) currentApplicationMethod.invoke(null);
  32. } catch (Exception e) {
  33. throw new RuntimeException("LibraryLoader not initialized. Call LibraryLoader.initialize() before using library classes.", e);
  34. }
  35. }
  36. return sAppContext;
  37. }
  38.  
  39. static void loadLibrary(Context context, final String library) {
  40. try {
  41. System.loadLibrary(library);
  42. } catch (final UnsatisfiedLinkError e) {
  43. if (SURFACE_LIBRARY_NAME.equals(library)) {
  44. loadLibrary(context, BASE_LIBRARY_NAME);
  45. }
  46. if (context == null) {
  47. context = getContext();
  48. }
  49. ReLinker.loadLibrary(context, library);
  50. }
  51. }
  52. }

最终到这里:

 
  1. /**
  2. * Copyright 2015 KeepSafe Software, Inc.
  3. * <p/>
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. * <p/>
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. * <p/>
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package pl.droidsonroids.gif;
  17.  
  18. import android.annotation.SuppressLint;
  19. import android.content.Context;
  20. import android.content.pm.ApplicationInfo;
  21. import android.os.Build;
  22.  
  23. import java.io.Closeable;
  24. import java.io.File;
  25. import java.io.FileOutputStream;
  26. import java.io.FilenameFilter;
  27. import java.io.IOException;
  28. import java.io.InputStream;
  29. import java.io.OutputStream;
  30. import java.util.zip.ZipEntry;
  31. import java.util.zip.ZipFile;
  32.  
  33. /**
  34. * Based on https://github.com/KeepSafe/ReLinker
  35. * ReLinker is a small library to help alleviate {@link UnsatisfiedLinkError} exceptions thrown due
  36. * to Android's inability to properly install / load native libraries for Android versions before
  37. * API 21
  38. */
  39. class ReLinker {
  40. private static final String LIB_DIR = "lib";
  41. private static final int MAX_TRIES = 5;
  42. private static final int COPY_BUFFER_SIZE = 8192;
  43.  
  44. private ReLinker() {
  45. // No instances
  46. }
  47.  
  48. /**
  49. * Utilizes the regular system call to attempt to load a native library. If a failure occurs,
  50. * then the function extracts native .so library out of the app's APK and attempts to load it.
  51. * <p/>
  52. * <strong>Note: This is a synchronous operation</strong>
  53. */
  54. static void loadLibrary(Context context, final String library) {
  55. final String libName = System.mapLibraryName(library);
  56. synchronized (ReLinker.class) {
  57. final File workaroundFile = unpackLibrary(context, libName);
  58. System.load(workaroundFile.getAbsolutePath());
  59. }
  60. }
  61.  
  62. /**
  63. * Attempts to unpack the given library to the workaround directory. Implements retry logic for
  64. * IO operations to ensure they succeed.
  65. *
  66. * @param context {@link Context} to describe the location of the installed APK file
  67. * @param libName The name of the library to load
  68. */
  69. private static File unpackLibrary(final Context context, final String libName) {
  70. File outputFile = new File(context.getDir(LIB_DIR, Context.MODE_PRIVATE), libName);// + BuildConfig.VERSION_NAME);
  71. if (outputFile.isFile()) {
  72. return outputFile;
  73. }
  74.  
  75. final File cachedLibraryFile = new File(context.getCacheDir(), libName );//+ BuildConfig.VERSION_NAME);
  76. if (cachedLibraryFile.isFile()) {
  77. return cachedLibraryFile;
  78. }
  79.  
  80. final FilenameFilter filter = new FilenameFilter() {
  81. @Override
  82. public boolean accept(File dir, String filename) {
  83. return filename.startsWith(libName);
  84. }
  85. };
  86. clearOldLibraryFiles(outputFile, filter);
  87. clearOldLibraryFiles(cachedLibraryFile, filter);
  88.  
  89. final ApplicationInfo appInfo = context.getApplicationInfo();
  90. final File apkFile = new File(appInfo.sourceDir);
  91. ZipFile zipFile = null;
  92. try {
  93. zipFile = openZipFile(apkFile);
  94.  
  95. int tries = 0;
  96. while (tries++ < MAX_TRIES) {
  97. ZipEntry libraryEntry = getLibraryEntry(libName, zipFile);
  98.  
  99. InputStream inputStream = null;
  100. FileOutputStream fileOut = null;
  101. try {
  102. inputStream = zipFile.getInputStream(libraryEntry);
  103. fileOut = new FileOutputStream(outputFile);
  104. copy(inputStream, fileOut);
  105. } catch (IOException e) {
  106. if (tries > MAX_TRIES / 2) {
  107. outputFile = cachedLibraryFile;
  108. }
  109. continue;
  110. } finally {
  111. closeSilently(inputStream);
  112. closeSilently(fileOut);
  113. }
  114. setFilePermissions(outputFile);
  115. break;
  116. }
  117. } finally {
  118. closeSilently(zipFile);
  119. }
  120. return outputFile;
  121. }
  122.  
  123. @SuppressWarnings("deprecation") //required for old API levels
  124. private static ZipEntry getLibraryEntry(final String libName, final ZipFile zipFile) {
  125. String jniNameInApk;
  126.  
  127. ZipEntry libraryEntry = null;
  128. // if (Build.VERSION.SDK_INT >= 21 && Build.SUPPORTED_ABIS.length > 0) {
  129. // for (final String ABI : Build.SUPPORTED_ABIS) {
  130. // jniNameInApk = "lib/" + ABI + "/" + libName;
  131. // libraryEntry = zipFile.getEntry(jniNameInApk);
  132. //
  133. // if (libraryEntry != null) {
  134. // break;
  135. // }
  136. // }
  137. // } else
  138.  
  139. {
  140. jniNameInApk = "lib/" + Build.CPU_ABI + "/" + libName;
  141. libraryEntry = zipFile.getEntry(jniNameInApk);
  142. }
  143.  
  144. if (libraryEntry == null) {
  145. throw new IllegalStateException("Library " + libName + " for supported ABIs not found in APK file");
  146. }
  147. return libraryEntry;
  148. }
  149.  
  150. private static ZipFile openZipFile(final File apkFile) {
  151. int tries = 0;
  152. ZipFile zipFile = null;
  153. while (tries++ < MAX_TRIES) {
  154. try {
  155. zipFile = new ZipFile(apkFile, ZipFile.OPEN_READ);
  156. break;
  157. } catch (IOException ignored) {
  158. }
  159. }
  160.  
  161. if (zipFile == null) {
  162. throw new RuntimeException("Could not open APK file: " + apkFile.getAbsolutePath());
  163. }
  164. return zipFile;
  165. }
  166.  
  167. @SuppressWarnings("ResultOfMethodCallIgnored") //intended, nothing useful can be done
  168. private static void clearOldLibraryFiles(final File outputFile, final FilenameFilter filter) {
  169. final File[] fileList = outputFile.getParentFile().listFiles(filter);
  170. if (fileList != null) {
  171. for (File file : fileList) {
  172. file.delete();
  173. }
  174. }
  175. }
  176.  
  177. @SuppressWarnings("ResultOfMethodCallIgnored") //intended, nothing useful can be done
  178. @SuppressLint("SetWorldReadable") //intended, default permission
  179. private static void setFilePermissions(File outputFile) {
  180. // Try change permission to rwxr-xr-x
  181. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
  182. outputFile.setReadable(true, false);
  183. outputFile.setExecutable(true, false);
  184. outputFile.setWritable(true);
  185. }
  186. }
  187.  
  188. /**
  189. * Copies all data from an {@link InputStream} to an {@link OutputStream}.
  190. *
  191. * @param in The stream to read from.
  192. * @param out The stream to write to.
  193. * @throws IOException when a stream operation fails.
  194. */
  195. private static void copy(InputStream in, OutputStream out) throws IOException {
  196. final byte[] buf = new byte[COPY_BUFFER_SIZE];
  197. while (true) {
  198. final int bytesRead = in.read(buf);
  199. if (bytesRead == -1) {
  200. break;
  201. }
  202. out.write(buf, 0, bytesRead);
  203. }
  204. }
  205.  
  206. /**
  207. * Closes a {@link Closeable} silently (without throwing or handling any exceptions)
  208. *
  209. * @param closeable {@link Closeable} to close
  210. */
  211. private static void closeSilently(final Closeable closeable) {
  212. try {
  213. if (closeable != null) {
  214. closeable.close();
  215. }
  216. } catch (IOException ignored) {
  217. }
  218. }
  219. }

获取当前apk路径

final ApplicationInfo appInfo = context.getApplicationInfo();
Log.d("zhibin","appInfo.sourceDir: "+ appInfo.sourceDir);
输出:/system/app/xxx.apk


对应sdk 5.0以下版本,修正一些不支持的变量:

  1. @SuppressWarnings("deprecation") //required for old API levels
  2. private static ZipEntry getLibraryEntry(final String libName, final ZipFile zipFile) {
  3. String jniNameInApk;
  4.  
  5. ZipEntry libraryEntry = null;
  6. // if (Build.VERSION.SDK_INT >= 21 && Build.SUPPORTED_ABIS.length > 0) {
  7. // for (final String ABI : Build.SUPPORTED_ABIS) {
  8. // jniNameInApk = "lib/" + ABI + "/" + libName;
  9. // libraryEntry = zipFile.getEntry(jniNameInApk);
  10. //
  11. // if (libraryEntry != null) {
  12. // break;
  13. // }
  14. // }
  15. // } else
  16.  
  17. {
  18. jniNameInApk = "lib/" + Build.CPU_ABI + "/" + libName;
  19. Log.d("zhibin","Search directory for jniNameInApk: "+ jniNameInApk);
  20. libraryEntry = zipFile.getEntry(jniNameInApk);
  21.  
  22. //直接指定
  23. if(libraryEntry == null){
  24. jniNameInApk = "lib/armeabi" + "/" + libName;
  25. Log.d("zhibin","Correct it to jniNameInApk: "+ jniNameInApk);
  26. libraryEntry = zipFile.getEntry(jniNameInApk);
  27. }
  28. }
  29.  
  30. if (libraryEntry == null) {
  31. throw new IllegalStateException("Library " + libName + " for supported ABIs not found in APK file");
  32. }
  33. return libraryEntry;
  34. }

共外部调用资源:

背景:工作中需要开发一个广告插件,并提供给其它人使用。这里就需要把自己的插件程序,打成jar来提供给他人引用。
但是遇到一个问题:插件程序中无法使用资源文件。

试过以下几种方式解决:

1、从插件程序中导出jar包
论坛上有人说导出的jar包中无法包含Drawable等资源文件,一些图片等数据,需要放到Assert文件中使用。
其实,关于这个问题,我做了尝试:
首先,需要说明导出jar包含什么文件是由你导出时选择来决定的,比如下图:

如果你选择了res文件夹,则打包出的jar文件是可以包含res文件到。

但是包含文件并不代表可以使用。如果你想当然在插件程序中使用R.drawable.XXXX等方式获取
资源会报错!
当然别人通过R.XX.XX也只能看到自己的资源文件,而无法获取jar中的资源文件。

2、获取jar包中的文件

虽然无法直接引用资源文件,但是如果外边程序想获取某个资源文件时,也是可行的。
其原理是以数据流读取jar中指定的文件。
比如读取Assert文件下的icon.jpg文件:
你可以在插件中封装一个对外的方法:
    publicstatic Drawable getAssertDrawable(Context context,StringfileName){
       try {
          InputStreaminStream=context.getAssets().open(fileName);
          return newBitmapDrawable(BitmapFactory.decodeStream(inStream));
       } catch(IOException e) {
         Log.e(LOG_TAG, "Assert中"+fileName+"不存在");
       }
       returnnull;
    }
直接使用该方法可以得到文件。
后来又尝试在外部程序,直接使用context.getAssets().open(fileName)方法获取jar中文件,
让人喜出望外的是竟然成功了。呵呵!
后来分析,外部程序编译时,其实连同jar包中内容一起混编。jar包中的Assert文件会同外部程序的Assert一起
由AssertManager管理。
所以当你jar包中Assert内部文件和外部Assert中的文件有命名冲突时,编译器会报错的。

另外,还有人提供另外一种方法来读取诸如Drawable等文件夹下的文件。
    publicstatic Drawable getDrawableForJar(String resName,Classclass){
       InputStreaminStream=class.getResourceAsStream(resName);
       return newBitmapDrawable(BitmapFactory.decodeStream(inStream));
    }
使用class.getResourceAsStream()方法读取,注意这里resName是文件的相对路径,比如jar根目录下res/drawable/icon.png,
则调用方法为:class.getResourceAsStream(/res/drawable/icon.png);

这里主要是采用ClassLoader的下面几个方法来实现:

  public URL getResource(String name);

  public InputStream getResourceAsStream(String name)

  public static InputStreamgetSystemResourceAsStream(String name)

  public static URL getSystemResource(String name)

  后两个方法可以看出是静态的方法,这几个方法都可以从Jar中读取图片资源,但是对与动画的gif文件,笔者在尝试过程中发现,存在一些差异。

  String gifName为Gif文件在Jar中的相对路径。

  (1)使用了两个静态方法

  BufferedImageimage = ImageIO.read(ClassLoader.getSystemResourceAsStream(gifName));

  或者

  Image image = Toolkit.getDefaultToolkit().getImage(ClassLoader.getSystemResource(gifName));

  这两种方式可以成功地读取gif文件,但是对于gif动画,显示出来地是静态的。

  (2)使用其他两个方法

  Image image = Toolkit.getDefaultToolkit().getImage( this .getClass.getClassLoader()
.getResource(gifName));

  再这种方式下动画可以正常显示了。

3、使用library方法加载资源文件

在论坛中看到帖子讲述如何把工程作为libarary,让其他工程添加library,编译后会自动生成jar,然后在哪来使用。 
当时看到此贴,喜出望外,所以赶紧尝试下!

方法:选择插件工程,右键选择属性,选择Android,勾选下面Is Liabrary选项。 
然后,选择我们现有的工程,右键属性,选择Android,在library下add相应的库。你会看到,刚才我们设置的插件项目,就在其中。最后,点击应用,完成。

详细步骤:

按如下方法设置:

1. 假设要引用的android工程叫LibProject,引入到的工程叫MainProject;

2.设置LibProject,右键->Properties->Android,将Islibrary项选中,然后Apply;

3.设置MainProject,右键->->Properties->Android,在Library中,点击Add按钮,将LibProject工程加入,Apply即可。

你会看到我们的工程中多出插件工程的引用,而且可以使用R.XXX.XXX获取资源文件。

以为可以解决了,但是发现并没有生成想要的jar文件。在插件工程中,倒是有编译的class文件,却没有jar包。 
而我们往往是不能像这样把原工程给别人直接引用的。 
经过多次试验,始终没有生成jar,非常奇怪别人怎么弄得。。。

另外,拿以前通过这种方式生成的jar文件看,里面也不包含资源文件夹。。 
可以把生成的类共享出去。

把.so文件打包到jar中

查了一些方法,其中一个我比较喜欢,再load动态库的时候,把so文件复制到tmp目录下,然后删掉

  1. //modify the static block
  2. static {
  3. try {
  4. Class c = HelloJNI.class;
  5. URL location =
  6. c.getProtectionDomain().getCodeSource().getLocation();
  7. ZipFile zf = new ZipFile(location.getPath());
  8. // libhellojni.so is put in the lib folder
  9. InputStream in = zf.getinputStream(zf.getEntry("lib/libhellojni.so"));
  10. File f = File.createTempFile("JNI-", "Temp");
  11. FileOutputStream out = new FileOutputStream(f);
  12. byte [] buf = new byte[1024];
  13. int len;
  14. while ((len = in.read(buf)) > 0)
  15. out.write(buf, 0, len);
  16. in.close();
  17. out.close();
  18. System.load(f.getAbsolutePath());
  19. f.delete();
  20. } catch (Exception e) { // I am still lazy ~~~
  21. e.printStackTrace();
  22. }
  23. }

打包jar文件 外部调用资源 so等的更多相关文章

  1. 关于在打包Jar文件时遇到的资源路径问题(二)

    在关于<关于在打包Jar文件时遇到的资源路径问题(一)>中,以及描述了当资源与可执行JAr分离时的资源路径代码的编写问题,后来想了想,为什么将<Java核心技术卷一>中的程序1 ...

  2. 关于在打包Jar文件时遇到的资源路径问题(一)

    当我们将程序写好,并进行打包成Jar文件时,通常都带有各种资源,这些资源可以是图像或者声音文件,也可以是别的如文本文件或二进制文件等,这些资源都和代码密切相关.例如在一个JPanel类上显示一些可能变 ...

  3. Eclipse将android项目打包jar文件

    Eclipse+android打包jar文件 蔡建良 2016-3-12 以Android-SlideExpandableListView开源框架为例,将源码Library打包成jar文件并包含R.c ...

  4. AndroidStduio3.0 使用gradle将module打包jar文件

    AndroidStduio3.0使用gradle将module打包jar文件,首先需要安装gradle. 打开控制台输入      open -e .bash_profile     命令,就可以打开 ...

  5. Intellij打包jar文件,“java.lang.SecurityException: Invalid signature file digest for Manifest main attrib

    下面是使用Intellij 打包jar文件的步骤,之后会有运行jar文件时遇到的错误. 打包完成. ================================================== ...

  6. eclipse打包jar文件(含外部jar包)的方法

    在项目发布前,使用eclipse导出普通的jar包时,如果配置不好,在运行命令Java -jar /test.jar 时可能会出现如下三类错误信息: 1.no main manifest attrib ...

  7. eclipse打包jar文件

    论文仿真做线性回归分类在人脸识别中应用与研究,在单机下实现LRC算法后,又在Hadoop云平台下实现了该算法.在比较实验结果时候需要放在相同硬件条件下比较.但是LRC单机算法是在windows下的ec ...

  8. java 打包jar文件以在没有安装JDK或JRE的机子上运行

    前言: java号称“一次编译,到处运行”,但这有个前提,那就是你的机子上得安装java环境.对于开发人员或其他一些比较懂计算机的人来说这没什么,但是对于一些不懂计算机的人来说这会很麻烦,他们更希望的 ...

  9. Java 图片爬虫,java打包jar文件

    目录 1. Java 图片爬虫,制作 .jar 文件 spider.java 制作 jar 文件 添加执行权限 1. Java 图片爬虫,制作 .jar 文件 spider.java spider.j ...

随机推荐

  1. 教你写一个web远程控制小工具

    惯例先上图 晚上躺床上了,发现忘关电脑了,又不想起来关,来用手机控制电脑多好,百度了下,果然一大把.哈,我自己为什么不自己也实现个呢,任意的自己diy.Just do it. 如果不想看如何实现,那么 ...

  2. 项目分享五:H5图片压缩与上传

    一.简介 图片的压缩与上传,是APP里一个很常用的功能.我们来年看 ChiTuStore 是怎样做的.相关文件 App/Module/User/UserInfo.html,App/Module/Use ...

  3. css 图片的无缝滚动

    转载:http://blog.sina.com.cn/s/blog_6387e82401013kx8.html js的图片的横向或者竖向的无缝滚动图片. ttp://zx.bjmylike.com/ ...

  4. MATLAB中的set函数

    1.MATLAB给每种对象的每一个属性规定了一个名字,称为属性名,而属性名的取值成为属性值.例如,LineStyle是曲线对象的一个属性名,它的值决定着线型,取值可以是'-' .':'.'-.'.'- ...

  5. class文件概述

    将java代码编译后会产生class文件,并且一个clas文件会对应唯一一个java类或者接口.下面对一个通过一个简单的例子来简述一下class文件的结构. java代码 public class J ...

  6. 【JavaEE企业应用实战学习记录】struts配置文件详细解析

    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-/ ...

  7. yii2组件之下拉框带搜索功能(yii-select2)

    简单的小功能,但是用起来还是蛮爽的.分享出来让更多的人有更快的开发效率,开开心心快乐编程. 如果你还没有使用过composer,你可就out了,看我的教程分享,composer简直就是必备神奇有木有. ...

  8. Android  PNG透明图片转JPG格式背景变黑

    Android  PNG透明图片转JPG格式背景变黑 在上传图片是,需要把PNG格式转换成JPG格式的,但是在遇上透明背景时,转过来就变成黑色底图了! 原因是PNG支持透明图而 JPG格式不支持透明底 ...

  9. Android性能优化文章转载

    今天看到几篇比较好的文章就转了!(链接如下) 转载注明出处:Sunzxyong Android性能优化之Bitmap的内存优化 Android性能优化之常见的内存泄漏 Android最佳实践之Syst ...

  10. Android Studio 优秀插件汇总

    第一部分 插件的介绍 Google 在2013年5月的I/O开发者大会推出了基于IntelliJ IDEA java ide上的Android Studio.AndroidStudio是一个功能齐全的 ...