You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多次调用System.load加载Native库致Android应用崩溃问题

问题分析与解决方案

问题根源

  1. 重复覆盖已加载的Native库文件:每次进入MainActivity都会执行so文件复制操作,直接覆盖磁盘上已存在、且已被加载到进程内存中的so文件。此时进程内存中对该so的内存映射与磁盘文件内容不一致,访问Native方法时会触发段错误(SIGSEGV)。
  2. Native库重复加载风险:Android进程中同一个Native库只能被加载一次,重复调用System.load会抛出异常,但本次崩溃的直接原因是文件覆盖导致的内存映射失效,而非重复加载。

修复方案

1. 避免重复复制so文件

在复制so前先判断目标文件是否存在,仅当文件不存在时才执行复制操作:

File libSavePath = new File(getFilesDir().getAbsolutePath() + "/libreproducecrash.so");
// 仅当文件不存在时才复制
if (!libSavePath.exists()) {
    try {
        InputStream is = getAssets().open("libreproducecrash.so");
        FileOutputStream fos = new FileOutputStream(libSavePath);
        byte[] buffer = new byte[1024];
        int byteCount = 0;
        while ((byteCount = is.read(buffer)) != -1) {
            fos.write(buffer, 0, byteCount);
        }
        fos.flush();
        fos.close();
        is.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

2. 确保Native库仅加载一次

将Native库加载逻辑移至Application类中,利用应用生命周期确保整个进程内仅加载一次:

步骤1:创建自定义Application类

public class MyApplication extends Application {
    private static boolean sLibLoaded = false;

    @Override
    public void onCreate() {
        super.onCreate();
        loadNativeLibrary();
    }

    private void loadNativeLibrary() {
        if (sLibLoaded) {
            return;
        }
        File libSavePath = new File(getFilesDir().getAbsolutePath() + "/libreproducecrash.so");
        if (libSavePath.exists()) {
            System.load(libSavePath.getPath());
            Log.i("MyLogger", libSavePath.getPath() + " loaded");
            sLibLoaded = true;
        }
    }
}

步骤2:在AndroidManifest.xml中声明自定义Application

<application
    android:name=".MyApplication"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.ReproduceCrash">
    <!-- 其他组件声明 -->
</application>

3. 调整MainActivity逻辑

移除MainActivity中的System.load调用,直接调用Native方法即可:

@SuppressLint("UnsafeDynamicallyLoadedCode")
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    binding = ActivityMainBinding.inflate(getLayoutInflater());
    setContentView(binding.getRoot());

    // 仅保留文件复制逻辑(已加存在判断)
    File libSavePath = new File(getFilesDir().getAbsolutePath() + "/libreproducecrash.so");
    if (!libSavePath.exists()) {
        try {
            InputStream is = getAssets().open("libreproducecrash.so");
            FileOutputStream fos = new FileOutputStream(libSavePath);
            byte[] buffer = new byte[1024];
            int byteCount = 0;
            while ((byteCount = is.read(buffer)) != -1) {
                fos.write(buffer, 0, byteCount);
            }
            fos.flush();
            fos.close();
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 直接调用Native方法
    TextView tv = binding.sampleText;
    tv.setText(stringFromJNI());
}

补充说明

若不想使用自定义Application,也可将加载逻辑放在全局静态工具类中,但Application方式更符合Android应用生命周期的设计,能确保库在应用启动时就完成加载,避免后续页面调用时的问题。

内容的提问来源于stack exchange,提问作者Finley

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 17:50:21