打包jar文件 外部调用资源 so等
一个非常好的从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)使用了两个静态方法
或者
这两种方式可以成功地读取gif文件,但是对于gif动画,显示出来地是静态的。
(2)使用其他两个方法
再这种方式下动画可以正常显示了。
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等相关推荐
- exe4j 打包 jar 文件为 exe 文件(GUI程序)
exe4j 打包 jar 文件为 exe 文件(GUI程序) 1.资源分享 2.把项目打包成 exe 文件 2.1 把项目打包成 jar 包(用 fatjar 打包项目) (1) 把 net.sf.f ...
- 【Java 虚拟机原理】Dalvik 虚拟机 ( 打包 Jar 文件和 Dex 文件 | 反编译 Dex 文件 | 分析 Dex 文件反编译结果 )
文章目录 前言 一.打包 Jar 文件和 Dex 文件 1.示例代码 2.打包 Jar 文件 3.打包 Dex 文件 二.反编译 Dex 文件 三.分析 Dex 文件 1.Student 类相关信息 ...
- java生成dex_打包jar文件,jar转dex,class转dex等各种转换整理
打包jar文件 主模块build.gradle根路径加入任务 task makeJar(type: org.gradle.api.tasks.bundling.Jar) { //指定生成的jar名称 ...
- java文件打包jar文件_把java文件打包成.jar (jar命令详解)
把java文件打包成.jar (jar命令详解) 先打开命令提示符(win2000或在运行框里执行cmd命令,win98为DOS提示符),输入jar Chelp,然后回车(如果你盘上已经有了jdk1. ...
- java文件打包jar文件_Java打包成jar文件,以及将jar文件导出为exe文件方法汇总(图形说明)...
方法一:用j2ewiz,使用方法如下: 操作步骤: 1. 双击j2ewiz.exe,出现软件注册窗口,点击下一步. 2. 选择待转换的Jar文件及最低Jvm版本.我选择 ...
- 打包jar文件后的spring部署及hibernate自动建表经验总结
楔子 用spring+hibernate做一个服务器运行程序,在部署到服务器时(打包成jar,在window server2008下运行),出现了以下两个纠结问题: 1. 加载不了Spring容器 2 ...
- java文件打包jar文件_如何把JAVA文件打包成jar文件
本文讲解了把java文件打包成jar文件的方法,经测试可行.由于作者没时间用批处理文件进行操作的方法没有列出来. (1)把一个java文件打包到当前目录 javac –d . *.java (2) ...
- Win打包jar文件
提示:unity 跟Android Studio交互,需要在as中导出jar包,因为里面有个UnityPlayerActivity.cs,需要删除,不然Unity会报重复文件的错. 前言 本人不太喜欢 ...
- java打包后的图片文件在哪,java程序打包jar文件自带图片
今天打包jar的文件的时候发觉图片无法定位导致图片加载失败.然后把图片放在jar文件路径下,图片就可以加载了.然后尝试写一段能够使jar文件定位图片位置的方法,成功了.但是会发觉,后者比前者在载入的时 ...
最新文章
- 【C#食谱】【杭帮菜】菜单2:写一个TCP客户端
- 利用Matlab优化工具箱求解旅行商最短路径问题
- 【Linux部署】Greenplum数据库6.13.0单机版 [CRITICAL]:-Error occurred: non-zero rc: 1(报错详情+问题处理:内存释放)
- 美术学考计算机,艺术设计专业能跨专业考计算机研究生吗?
- 有一个3x4的矩阵,输出最大值,且输出对应的行和列;
- 你最隐秘的性格在哪?
- linux下无法安装VMware的解决方法
- 定时器函数执行原理揭秘
- js动态显示时间和日期
- 【转】keil5 missing close quote 错误解决
- 教你如何免费使用云服务器
- Python系列之面向对象编程
- Jquery 对 身份证号码的验证 (15/18位)
- java8日期加减_java时间加减
- 百度地图定位API,精度提高
- 微服务是去ESB总线、去中心化和分布式
- dpo指标详解买入绝技_DPO指标详解 DPO指标使用技巧
- 转:一套大而全的系统架构体系与具体落地方案
- 详解Paint的setXfermode(Xfermode xfermode)
- word2010打开97-03格式的word文件失败的解决方法
热门文章
- 基于java教学管理系统设计(含源文件)
- Android 自定义焦点框,Android给自定义按键添加广播和通过广播给当前焦点输入框赋值...
- 斜挎包长度到哪里合适_斜挎包带子多长合适 看个人身高
- Qt文档阅读笔记-QGraphicsItem events解析与实例
- Qt工作笔记-QDialog模式对话框传递数据给主窗口
- 2.4一元多项式的表示及相加
- linux 管理工具 scrt,Linux服务器管理之终端管理软件(SecureCRT)介绍
- linux ls不显示total,Linux中使用ls指令时total的意思
- Python中append和extend的区别
- mysql中函数大全_MySql 函数大全(一)