一个非常好的从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

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-08-30
  • 2022-03-08
  • 2022-12-23
  • 2021-09-02
  • 2021-12-13
猜你喜欢
  • 2021-08-25
  • 2022-03-10
  • 2022-12-23
  • 2021-11-09
  • 2022-01-28
  • 2022-12-23
  • 2021-11-27
相关资源
相似解决方案