• 打包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 levels
        private 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包含什么文件是由你导出时选择来决定的,比如下图:
    android <wbr>关于生成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 block
    
    static {
      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();
      }
    }
  • 相关阅读:
    Delphi异常处理try except语句和try finally语句用法以及区别
    test
    Infopath resource
    C# IDE
    操作数据库
    不同版本数据库的导入
    workflow for sharepoint 2007
    http://www.cnblogs.com/BearStudyHard/archive/2008/03/26/1123267.html
    深入浅出InfoPath——安装VSTO
    如何使用Lotuscript管理Excel中的工作表?
  • 原文地址:https://www.cnblogs.com/bigben0123/p/5028352.html
Copyright © 2020-2023  润新知