*.so library from included *.jar involves Unsatisf

2020-07-28 11:18发布

I've wrote android app with native shared library ( libnativeext.so ).

Inside java class in app I load libnativeext.so with System.loadLibrary("nativeext"). All works great. Native code compiles, and libnativeext.so places in /libs/armeabi/ folder. So final first.apk file contains /lib/armeabi/libnativeext.so, installs on device and all work ok.

Then I export project in javaext.jar.

At this point javaext.jar contains libnativeext.so in /libs/armeabi/.

In the new project (second-proj) I include javaext.jar and add path to javaext.jar in java build path. Project builds with only warning about native library in javaext.jar. I disable warning in eclipse preferences.

But on device I got: java.lang.UnsatisfiedLinkError: Couldn't load nativeext: findLibrary returned null

Strange, because second.apk have /libs/armeabi/libnativeext.so inside. I go to phone and figure out than folder on phone /data/data/myapp/lib/ is EMPTY! And ofcourse System.loadLibrary can't find libnativeext.so.

I find solution for myself, but it looks very ugly, and I want to find better way.

I create inside existing second-proj/libs/ folder armeabi and place libnativeext.so inside.

second-proj:
/libs/armeabi/libnativeext.so
/libs/javaext.jar

When I build project, I look inside second.apk:

/lib/armeabi/libnativeext.so <--- new one
/libs/armeabi/libnativeext.so

And this version work perfect on the phone.

So I assume, that during installation libraries from /libs/armeabi/ is ignored, and only libraries from /lib/armeabi/ is installed on the phone.

So question is: How to force apk bulder to copy *.so from *.jar to right *.apk folder?

1条回答
贼婆χ
2楼-- · 2020-07-28 11:53

In case there are no way to pack *.so library from included *.jar into final *.apk I solve this problem for myself.

I write LibraryLoader, which:

  1. Tries to load library with System.loadLibrary().

  2. If it fails, loader search library in application storage, and if find loads it with System.load().

  3. If no library was found in the app storage, it find .apk file, serch there, and if loader find library - copies it to app storage, then loads it with System.load().

Post code here - may be it helps somebody.

    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;
    }
        }
            }
查看更多
登录 后发表回答