日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

打包jar文件 外部调用资源 so等

發(fā)布時(shí)間:2025/3/15 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 打包jar文件 外部调用资源 so等 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一個(gè)非常好的從jar文件中加載so動(dòng)態(tài)庫(kù)方法,在android的gif支持開(kāi)源中用到。這個(gè)項(xiàng)目的gif解碼是用jni c實(shí)現(xiàn)的,避免了OOM等問(wèn)題。

項(xiàng)目地址: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

這樣做會(huì)報(bào)錯(cuò),提示xml里面找不到GifImageView。

只能用項(xiàng)目之間依賴,so會(huì)自動(dòng)進(jìn)入生成的apk,不用拷貝。

?

調(diào)用方法:

//開(kāi)始調(diào)用:static {LibraryLoader.loadLibrary(null, LibraryLoader.BASE_LIBRARY_NAME);}

進(jìn)入這里:

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

獲取當(dāng)前apk路徑

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


對(duì)應(yīng)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;}

?

?

?

?

?

?

?

?

?

?

共外部調(diào)用資源:

?

背景:工作中需要開(kāi)發(fā)一個(gè)廣告插件,并提供給其它人使用。這里就需要把自己的插件程序,打成jar來(lái)提供給他人引用。
但是遇到一個(gè)問(wèn)題:插件程序中無(wú)法使用資源文件。

試過(guò)以下幾種方式解決:

1、從插件程序中導(dǎo)出jar包
論壇上有人說(shuō)導(dǎo)出的jar包中無(wú)法包含Drawable等資源文件,一些圖片等數(shù)據(jù),需要放到Assert文件中使用。
其實(shí),關(guān)于這個(gè)問(wèn)題,我做了嘗試:
首先,需要說(shuō)明導(dǎo)出jar包含什么文件是由你導(dǎo)出時(shí)選擇來(lái)決定的,比如下圖:

如果你選擇了res文件夾,則打包出的jar文件是可以包含res文件到。

但是包含文件并不代表可以使用。如果你想當(dāng)然在插件程序中使用R.drawable.XXXX等方式獲取
資源會(huì)報(bào)錯(cuò)!
當(dāng)然別人通過(guò)R.XX.XX也只能看到自己的資源文件,而無(wú)法獲取jar中的資源文件。

2、獲取jar包中的文件

雖然無(wú)法直接引用資源文件,但是如果外邊程序想獲取某個(gè)資源文件時(shí),也是可行的。
其原理是以數(shù)據(jù)流讀取jar中指定的文件。
比如讀取Assert文件下的icon.jpg文件:
你可以在插件中封裝一個(gè)對(duì)外的方法:
????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;
????}
直接使用該方法可以得到文件。
后來(lái)又嘗試在外部程序,直接使用context.getAssets().open(fileName)方法獲取jar中文件,
讓人喜出望外的是竟然成功了。呵呵!
后來(lái)分析,外部程序編譯時(shí),其實(shí)連同jar包中內(nèi)容一起混編。jar包中的Assert文件會(huì)同外部程序的Assert一起
由AssertManager管理。
所以當(dāng)你jar包中Assert內(nèi)部文件和外部Assert中的文件有命名沖突時(shí),編譯器會(huì)報(bào)錯(cuò)的。

另外,還有人提供另外一種方法來(lái)讀取諸如Drawable等文件夾下的文件。
????publicstatic Drawable getDrawableForJar(String resName,Classclass){
???????InputStreaminStream=class.getResourceAsStream(resName);
???????return newBitmapDrawable(BitmapFactory.decodeStream(inStream));
????}
使用class.getResourceAsStream()方法讀取,注意這里resName是文件的相對(duì)路徑,比如jar根目錄下res/drawable/icon.png,
則調(diào)用方法為:class.getResourceAsStream(/res/drawable/icon.png);

這里主要是采用ClassLoader的下面幾個(gè)方法來(lái)實(shí)現(xiàn):

  public URL getResource(String name);

  public InputStream getResourceAsStream(String name)

  public static InputStreamgetSystemResourceAsStream(String name)

  public static URL getSystemResource(String name)

  后兩個(gè)方法可以看出是靜態(tài)的方法,這幾個(gè)方法都可以從Jar中讀取圖片資源,但是對(duì)與動(dòng)畫(huà)的gif文件,筆者在嘗試過(guò)程中發(fā)現(xiàn),存在一些差異。

  String gifName為Gif文件在Jar中的相對(duì)路徑。

  (1)使用了兩個(gè)靜態(tài)方法

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

  或者

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

  這兩種方式可以成功地讀取gif文件,但是對(duì)于gif動(dòng)畫(huà),顯示出來(lái)地是靜態(tài)的。

  (2)使用其他兩個(gè)方法

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

  再這種方式下動(dòng)畫(huà)可以正常顯示了。


3、使用library方法加載資源文件?

在論壇中看到帖子講述如何把工程作為libarary,讓其他工程添加library,編譯后會(huì)自動(dòng)生成jar,然后在哪來(lái)使用。?
當(dāng)時(shí)看到此貼,喜出望外,所以趕緊嘗試下!?

方法:選擇插件工程,右鍵選擇屬性,選擇Android,勾選下面Is Liabrary選項(xiàng)。?
然后,選擇我們現(xiàn)有的工程,右鍵屬性,選擇Android,在library下add相應(yīng)的庫(kù)。你會(huì)看到,剛才我們?cè)O(shè)置的插件項(xiàng)目,就在其中。最后,點(diǎn)擊應(yīng)用,完成。?

詳細(xì)步驟:?

按如下方法設(shè)置:

1. 假設(shè)要引用的android工程叫LibProject,引入到的工程叫MainProject;

2.設(shè)置LibProject,右鍵->Properties->Android,將Islibrary項(xiàng)選中,然后Apply;

3.設(shè)置MainProject,右鍵->->Properties->Android,在Library中,點(diǎn)擊Add按鈕,將LibProject工程加入,Apply即可。



你會(huì)看到我們的工程中多出插件工程的引用,而且可以使用R.XXX.XXX獲取資源文件。?

以為可以解決了,但是發(fā)現(xiàn)并沒(méi)有生成想要的jar文件。在插件工程中,倒是有編譯的class文件,卻沒(méi)有jar包。?
而我們往往是不能像這樣把原工程給別人直接引用的。?
經(jīng)過(guò)多次試驗(yàn),始終沒(méi)有生成jar,非常奇怪別人怎么弄得。。。?

另外,拿以前通過(guò)這種方式生成的jar文件看,里面也不包含資源文件夾。。?
可以把生成的類共享出去。

把.so文件打包到j(luò)ar中

查了一些方法,其中一個(gè)我比較喜歡,再load動(dòng)態(tài)庫(kù)的時(shí)候,把so文件復(fù)制到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(); } }

?

轉(zhuǎn)載于:https://www.cnblogs.com/bigben0123/p/5028352.html

總結(jié)

以上是生活随笔為你收集整理的打包jar文件 外部调用资源 so等的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。