来自* .jar的* .so库涉及UnsatisfiedLinkError

时间:2012-12-07 14:46:24

标签: android jar android-ndk shared-libraries

我用原生共享库( libnativeext.so )编写了Android应用程序。

在app中的java类中,我用 libnativeext.so 加载 的System.loadLibrary( “nativeext”)。 一切都很棒。 本机代码编译, libnativeext.so 位于 / libs / armeabi / 文件夹中。 所以最终的first.apk文件包含 /lib/armeabi/libnativeext.so ,安装在设备上并且一切正常。

然后我在 javaext.jar 中导出项目。

此时 javaext.jar / libs / armeabi / 中包含 libnativeext.so

在新项目(second-proj)中,我包含 javaext.jar ,并在java构建路径中添加 javaext.jar 的路径。 项目在 javaext.jar 中仅使用警告构建有关本机库的内容。 我在eclipse偏好中禁用了警告。

但是在我得到的设备上:java.lang.UnsatisfiedLinkError:无法加载nativeext:findLibrary返回null

奇怪,因为second.apk里面有 /libs/armeabi/libnativeext.so 。我打电话找出手机上的文件夹 / data / data / myapp / lib / 是EMPTY!并且当然System.loadLibrary找不到 libnativeext.so

我为自己找到了解决方案,但它看起来非常难看,我想找到更好的方法。

我在现有的 second-proj / libs / 文件夹 armeabi 中创建,并在其中放置libnativeext.so。

第二PROJ:
/libs/armeabi/libnativeext.so
/libs/javaext.jar

当我构建项目时,我会查看second.apk:

/lib/armeabi/libnativeext.so< --- 新的
/libs/armeabi/libnativeext.so

这个版本在手机上非常完美。

所以我假设,在安装期间/ libs / armeabi /中的库被忽略,并且手机上只安装了来自/ lib / armeabi /的库。

所以问题是:如何强制apk bulder将* .so从* .jar复制到右* .apk文件夹?

1 个答案:

答案 0 :(得分:3)

如果无法将包含* .jar的* .so库打包到final * .apk中,我自己解决了这个问题。

我写了LibraryLoader,其中:

  1. 尝试使用System.loadLibrary()加载库。

  2. 如果失败,请在应用程序存储中加载搜索库,如果find使用System.load()加载它。

  3. 如果在应用程序存储中找不到库,它会在那里找到.apk文件,serch,如果加载器找到库 - 将其复制到应用程序存储,然后使用System.load()加载它。

  4. 在这里发布代码 - 可能对某人有帮助。

        import java.io.BufferedOutputStream;
        import java.io.File;
        import java.io.FileOutputStream;
        import java.io.IOException;
        import java.io.InputStream;
        import java.io.OutputStream;
        import java.util.Enumeration;
        import java.util.zip.ZipEntry;
        import java.util.zip.ZipFile;
    
        import android.content.Context;
        import android.util.Log;
    
        public class SharedLibraryLoader
        {
            private static Context context;
            private static String libDir = "lib";
            private static String shortLibName;
            private static String fullLibName;
    
            static public boolean loadLibrary(String libName, Context ctx)
            {
        context = ctx;
        shortLibName = libName;
        fullLibName = "lib" + libName + ".so";
    
        try
        {
            Log.d("SharedLibraryLoader", "Trying to load library");
            System.loadLibrary(shortLibName);
            Log.d("SharedLibraryLoader", "Library was loaded from default location");
            return true;
        }
        catch(UnsatisfiedLinkError e)
        {
            Log.d("SharedLibraryLoader","Lib wasn't found at default location. Trying to find in application private storage");
            String path = null;
            path = findInAppStorage(fullLibName);
            if(path != null)
            {
                Log.d("SharedLibraryLoader","Lib was found in application private storage. Loading lib...");
                System.load(path);
                return true;
            }
            else
            {
                Log.d("SharedLibraryLoader","Lib was not found in application private storage. Trying to find in apk...");
                path = findInApkAndCopyToAppStorage(fullLibName);
    
                if(path != null)
                {
                    Log.d("SharedLibraryLoader","Lib was found in apk and copied to application private storage. Loading lib...");
                    System.load(path);
                    return true;
                }
                else
                {
                    Log.d("SharedLibraryLoader", "FAILED TO LOAD LIBRARY");
                    return false;
                }
            }
        }
            }
    
            static private String findInAppStorage(String libName)
            {
    
        Log.d("SharedLibraryLoader","enter findInAppStorage()");
        String basePath = context.getApplicationInfo().dataDir;
        File dataDir = new File(basePath);
    
        String[] listFiles;
        String  lib = null;
        listFiles = dataDir.list();
    
    
        for(int i=0; i < listFiles.length; i++)
        {
            lib = findInStorage(basePath + "/" +listFiles[i], libName);
    
            if(lib != null)
            {
                return lib;
            }
                }
    
        Log.d("SharedLibraryLoader", "Lib wasn't found.");
        return null;
            }
    
            static private String findInStorage(String path, String nameOfLib)
            {
        File file = new File(path);
        if(file.isDirectory())
        {
            Log.d("SharedLibraryLoader","Strorage__dir: " + path + "/");
            String[]    list = file.list();
            String      target = null; 
            for(int i = 0; i < list.length; i++)
            {
                target = findInStorage(path + "/" + list[i], nameOfLib);
                if(target != null)
                {
                    return target;
                }
            }
        }
        else
        {
            Log.d("SharedLibraryLoader","Strorage_file: " + path);
            if(path.contains(nameOfLib))
            {
                Log.d("SharedLibraryLoader","Lib was found in: " + path);
                return path;
            }
        }
        return null;
            }
    
            static private String findInApkAndCopyToAppStorage(String libName)
            {
                Log.d("SharedLibraryLoader", "Enter findInApkAndCopyToStorage()");
    
                // ---------------- ZIP - find path to .so  inside .apk ------------------
        String apkPath = context.getPackageResourcePath();
        Log.d("SharedLibraryLoader", String.format("Path to Package resource is: %s", apkPath));
    
        try
        {
            ZipFile zf = new ZipFile(apkPath);
    
            Enumeration<ZipEntry> zipFiles = (Enumeration<ZipEntry>) zf.entries();
            ZipEntry    soZipEntry = null;
            ZipEntry    tempZipEntry;
            String      tmpString;
            for ( ; zipFiles.hasMoreElements();)
            {
                tempZipEntry = zipFiles.nextElement(); 
                tmpString = tempZipEntry.getName();
    
                if(tmpString.contains(libName))
                {
                    Log.d("SharedLibraryLoader", "Library " + fullLibName + " was found in: " + tmpString);
                    soZipEntry = tempZipEntry;
                }
            }
    
            //----------now copy library---------------
            Log.d("SharedLibraryLoader", "soZipEntry = " + soZipEntry.toString());
    
            if(soZipEntry != null)
            {
                InputStream soInputStream = zf.getInputStream(soZipEntry);
    
                File fileDir;
                File soFile;
                OutputStream outStream;
                fileDir = context.getApplicationContext().getDir(libDir, Context.MODE_PRIVATE); // but "app_lib" was created!
                String fullSoFilePath = fileDir.getAbsolutePath() + "/" + libName;
                Log.d("SharedLibraryLoader", "New libpath is "+ fullSoFilePath);
                soFile = new File(fullSoFilePath);
    
                Log.d("SharedLibraryLoader", "Is file already exists? - " + soFile.exists());
    
                outStream = new BufferedOutputStream(new FileOutputStream(soFile));
    
                Log.d("SharedLibraryLoader", "Start copying library...");
                byte[] byteArray = new byte[256];
                int copiedBytes = 0;
    
                while((copiedBytes = soInputStream.read(byteArray)) != -1)
                {
                    outStream.write(byteArray, 0, copiedBytes);
                }
    
                Log.d("SharedLibraryLoader", "Finish copying library");
                outStream.close();
    
                soInputStream.close();
                return fullSoFilePath;
            }
            else
            {
                Log.d("SharedLibraryLoader", "Library not Found in APK");
                return null;
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return null;
        }
            }
                }