多次调用System.load加载Native库致Android应用崩溃问题
问题分析与解决方案
问题根源
- 重复覆盖已加载的Native库文件:每次进入
MainActivity都会执行so文件复制操作,直接覆盖磁盘上已存在、且已被加载到进程内存中的so文件。此时进程内存中对该so的内存映射与磁盘文件内容不一致,访问Native方法时会触发段错误(SIGSEGV)。 - 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
相关产品推荐
相关产品推荐

