一个非常好的从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,不用拷贝。

调用方法:

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

进入这里:

package pl.droidsonroids.gif;import android.content.Context;
import android.support.annotation.NonNull;import java.lang.reflect.Method;/*** Helper used to work around native libraries loading on some systems.* See <a href="https://medium.com/keepsafe-engineering/the-perils-of-loading-native-libraries-on-android-befa49dce2db">ReLinker</a> for more details.*/
public class LibraryLoader {static final String SURFACE_LIBRARY_NAME = "pl_droidsonroids_gif_surface";static final String BASE_LIBRARY_NAME = "pl_droidsonroids_gif";private static Context sAppContext;/*** Intitializes loader with given `Context`. Subsequent calls should have no effect since application Context is retrieved.* Libraries will not be loaded immediately but only when needed.* @param context any Context except null*/public static void initialize(@NonNull final Context context) {sAppContext = context.getApplicationContext();}static Context getContext() {if (sAppContext == null) {try {final Class<?> activityThread = Class.forName("android.app.ActivityThread");final Method currentApplicationMethod = activityThread.getDeclaredMethod("currentApplication");sAppContext = (Context) currentApplicationMethod.invoke(null);} catch (Exception e) {throw new RuntimeException("LibraryLoader not initialized. Call LibraryLoader.initialize() before using library classes.", e);}}return sAppContext;}static void loadLibrary(Context context, final String library) {try {System.loadLibrary(library);} catch (final UnsatisfiedLinkError e) {if (SURFACE_LIBRARY_NAME.equals(library)) {loadLibrary(context, BASE_LIBRARY_NAME);}if (context == null) {context = getContext();}ReLinker.loadLibrary(context, library);}}
}

最终到这里:

  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 }

View Code

获取当前apk路径

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


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

    @SuppressWarnings("deprecation") //required for old API levelsprivate static ZipEntry getLibraryEntry(final String libName, final ZipFile zipFile) {String jniNameInApk;ZipEntry libraryEntry = null;
//        if (Build.VERSION.SDK_INT >= 21 && Build.SUPPORTED_ABIS.length > 0) {
//            for (final String ABI : Build.SUPPORTED_ABIS) {
//                jniNameInApk = "lib/" + ABI + "/" + libName;
//                libraryEntry = zipFile.getEntry(jniNameInApk);
//
//                if (libraryEntry != null) {
//                    break;
//                }
//            }
//        } else
        {jniNameInApk = "lib/" + Build.CPU_ABI + "/" + libName;Log.d("zhibin","Search directory for jniNameInApk: "+ jniNameInApk);libraryEntry = zipFile.getEntry(jniNameInApk);//直接指定if(libraryEntry == null){jniNameInApk = "lib/armeabi" + "/" + libName;Log.d("zhibin","Correct it to  jniNameInApk: "+ jniNameInApk);libraryEntry = zipFile.getEntry(jniNameInApk);}}if (libraryEntry == null) {throw new IllegalStateException("Library " + libName + " for supported ABIs not found in APK file");}return libraryEntry;}

共外部调用资源:

背景:工作中需要开发一个广告插件,并提供给其它人使用。这里就需要把自己的插件程序,打成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目录下,然后删掉

//modify the static blockstatic {try { Class c = HelloJNI.class; URL location = c.getProtectionDomain().getCodeSource().getLocation(); ZipFile zf = new ZipFile(location.getPath()); // libhellojni.so is put in the lib folder InputStream in = zf.getinputStream(zf.getEntry("lib/libhellojni.so")); File f = File.createTempFile("JNI-", "Temp"); FileOutputStream out = new FileOutputStream(f); byte [] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0) out.write(buf, 0, len); in.close(); out.close(); System.load(f.getAbsolutePath()); f.delete(); } catch (Exception e) { // I am still lazy ~~~ e.printStackTrace(); } }

转载于:https://www.cnblogs.com/bigben0123/p/5028352.html

打包jar文件 外部调用资源 so等相关推荐

  1. exe4j 打包 jar 文件为 exe 文件(GUI程序)

    exe4j 打包 jar 文件为 exe 文件(GUI程序) 1.资源分享 2.把项目打包成 exe 文件 2.1 把项目打包成 jar 包(用 fatjar 打包项目) (1) 把 net.sf.f ...

  2. 【Java 虚拟机原理】Dalvik 虚拟机 ( 打包 Jar 文件和 Dex 文件 | 反编译 Dex 文件 | 分析 Dex 文件反编译结果 )

    文章目录 前言 一.打包 Jar 文件和 Dex 文件 1.示例代码 2.打包 Jar 文件 3.打包 Dex 文件 二.反编译 Dex 文件 三.分析 Dex 文件 1.Student 类相关信息 ...

  3. java生成dex_打包jar文件,jar转dex,class转dex等各种转换整理

    打包jar文件 主模块build.gradle根路径加入任务 task makeJar(type: org.gradle.api.tasks.bundling.Jar) { //指定生成的jar名称 ...

  4. java文件打包jar文件_把java文件打包成.jar (jar命令详解)

    把java文件打包成.jar (jar命令详解) 先打开命令提示符(win2000或在运行框里执行cmd命令,win98为DOS提示符),输入jar Chelp,然后回车(如果你盘上已经有了jdk1. ...

  5. java文件打包jar文件_Java打包成jar文件,以及将jar文件导出为exe文件方法汇总(图形说明)...

    方法一:用j2ewiz,使用方法如下: 操作步骤: 1.         双击j2ewiz.exe,出现软件注册窗口,点击下一步. 2.         选择待转换的Jar文件及最低Jvm版本.我选择 ...

  6. 打包jar文件后的spring部署及hibernate自动建表经验总结

    楔子 用spring+hibernate做一个服务器运行程序,在部署到服务器时(打包成jar,在window server2008下运行),出现了以下两个纠结问题: 1. 加载不了Spring容器 2 ...

  7. java文件打包jar文件_如何把JAVA文件打包成jar文件

    本文讲解了把java文件打包成jar文件的方法,经测试可行.由于作者没时间用批处理文件进行操作的方法没有列出来. (1)把一个java文件打包到当前目录   javac –d . *.java (2) ...

  8. Win打包jar文件

    提示:unity 跟Android Studio交互,需要在as中导出jar包,因为里面有个UnityPlayerActivity.cs,需要删除,不然Unity会报重复文件的错. 前言 本人不太喜欢 ...

  9. java打包后的图片文件在哪,java程序打包jar文件自带图片

    今天打包jar的文件的时候发觉图片无法定位导致图片加载失败.然后把图片放在jar文件路径下,图片就可以加载了.然后尝试写一段能够使jar文件定位图片位置的方法,成功了.但是会发觉,后者比前者在载入的时 ...

最新文章

  1. 【C#食谱】【杭帮菜】菜单2:写一个TCP客户端
  2. 利用Matlab优化工具箱求解旅行商最短路径问题
  3. 【Linux部署】Greenplum数据库6.13.0单机版 [CRITICAL]:-Error occurred: non-zero rc: 1(报错详情+问题处理:内存释放)
  4. 美术学考计算机,艺术设计专业能跨专业考计算机研究生吗?
  5. 有一个3x4的矩阵,输出最大值,且输出对应的行和列;
  6. 你最隐秘的性格在哪?
  7. linux下无法安装VMware的解决方法
  8. 定时器函数执行原理揭秘
  9. js动态显示时间和日期
  10. 【转】keil5 missing close quote 错误解决
  11. 教你如何免费使用云服务器
  12. Python系列之面向对象编程
  13. Jquery 对 身份证号码的验证 (15/18位)
  14. java8日期加减_java时间加减
  15. 百度地图定位API,精度提高
  16. 微服务是去ESB总线、去中心化和分布式
  17. dpo指标详解买入绝技_DPO指标详解 DPO指标使用技巧
  18. 转:一套大而全的系统架构体系与具体落地方案
  19. 详解Paint的setXfermode(Xfermode xfermode)
  20. word2010打开97-03格式的word文件失败的解决方法

热门文章

  1. 基于java教学管理系统设计(含源文件)
  2. Android 自定义焦点框,Android给自定义按键添加广播和通过广播给当前焦点输入框赋值...
  3. 斜挎包长度到哪里合适_斜挎包带子多长合适 看个人身高
  4. Qt文档阅读笔记-QGraphicsItem events解析与实例
  5. Qt工作笔记-QDialog模式对话框传递数据给主窗口
  6. 2.4一元多项式的表示及相加
  7. linux 管理工具 scrt,Linux服务器管理之终端管理软件(SecureCRT)介绍
  8. linux ls不显示total,Linux中使用ls指令时total的意思
  9. Python中append和extend的区别
  10. mysql中函数大全_MySql 函数大全(一)