部分手机不能加载so文件,couldn't find *.so

背景

当我们项目中使用到了.so库时,大部分手机能正常运行,但是部分手机在运行时,比如我们项目中使用到了mupdf这个开源的pdf查看器,里面需要使用到libmupdf.so,此时会报couldn’t find libmupdf.so错误,导致程序再浏览pdf文件时崩溃。

原因

一般情况下,当我们安装了应用时,项目中的.so库会安装到手机的/data/data/+包名+/lib 路径下,由于Android碎片化严重,很多厂商高度定制化,导致部分手机加载不成功。

解决方案

既然项目需要so文件,但是应用安装时没有成功加载so文件,那么我们是否可以手动完成将其拷贝到相应的路径下呢?这完全是可行的办法,而且经验证是个完美的解决方案。

public class SoRepairRunnable extends Runnable {

    private static final String TAG = "SoRepairRunnable";
    private Context context;

    public SoRepairRunnable(Context context) {
        this.context = context;
    }

    @Override
    public void run() {
        String apkLibPath = "/data/data/" + EnvironmentUtils.getPackageName() + "/lib";
        String pdfSoPath = apkLibPath + "/libmupdf.so";
        File pdfSoFile = new File(pdfSoPath);
        boolean pdfSoExist = pdfSoFile.exists();
        String apkPath = getApkPath();
        File v7aFile = new File("/data/data/" + EnvironmentUtils.getPackageName() + "/libs/armeabi-v7a");
        if(!v7aFile.exists()){
            v7aFile.mkdirs();
        }
            try {
                //获取apk压缩包
                ZipFile apkFile = new ZipFile(apkPath);
                //取得压缩包内so文件
                ZipEntry pdfZipEntry = apkFile.getEntry("lib/armeabi-v7a/libmupdf.so");
                //将so文件写入到手机目录下
                writeSoToLibs(apkFile, pdfZipEntry);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private String getApkPath(){
        return context.getPackageCodePath();
    }

    private void writeSoToLibs(ZipFile zipFile, ZipEntry entry) throws IOException{
        String entryName = entry.getName();
        String fileName = "/data/data/" + EnvironmentUtils.getPackageName() + "/" + entryName.replaceFirst("lib/", "libs/");
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = zipFile.getInputStream(entry);
            File file = new File(fileName  + ".tmp");
            if (!file.exists()) {
               file.createNewFile();
            }
            outputStream = new FileOutputStream(file);

            int byteCount = 0;

            byte[] bytes = new byte[8096];
            while ((byteCount = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, byteCount);
            }
            outputStream.flush();
            File newFile = new File(fileName);
            file.renameTo(newFile);
            file.delete();

        }
        catch (IOException e){
            e.printStackTrace();
        }finally {
            if(null != inputStream) {
                inputStream.close();
            }
            if(null != outputStream) {
                outputStream.close();
            }
        }
    }
}

这里为什么将so文件写入到另外一个libs文件夹下,而不是lib下,是因为lib文件夹是系统目录,我们没有权限读写,所以应用中在加载so文件时,也要相应的修改路径。

一般情况下,我们在应用中加载so文件是这样的,比如我的so库叫做libXXX,则加载时应该是(这是一个规则)

System.loadLibrary("XXX");

在我们的例子中应该有这样的一句静态语句块,在应用启动的时候去载入so文件

static {
        System.loadLibrary("mupdf");
    }

loadLibrary传入的是so库名称,它默认路径是/data/data/+包名+/lib,但是在部分手机下,它是不存在该so文件,经过我们上面的拷贝处理之后,so文件应该是在/data/data/+包名+/libs 路径下,所以这时候,我们应该载入的是在这个路径下的so文件,即应该这样处理:

System.load("/data/data/" + EnvironmentUtils.getPackageName() + "/libs/armeabi-v7a/libmupdf.so");

load方法,传入的是一个so文件的绝对路径。
完整的处理代码如下:

static {
        //若自己拷贝的so文件存在证明该设备无法载入lib里的so文件,改为获取自己拷贝的so文件
        String apkLibPath = "/data/data/" + EnvironmentUtils.getPackageName() + "/libs";
        String pdfSoPath = apkLibPath + "/armeabi-v7a/libmupdf.so";
        File pdfSoFile = new File(pdfSoPath);
        boolean isExist = pdfSoFile.exists();
        if(isExist){
            System.load(pdfSoFile.getAbsolutePath());
        }
        else{
            System.loadLibrary("mupdf");
        }
    }

你可能感兴趣的:(android,so文件加载失败,so文件找不到)