java.lang.IllegalArgumentException: Unable to find native library main using classloader: dalvik.sys

一个三方apk在androidN上forceclose,错误信息以下,经过对比测试,在android M上运行正常。java

 

 01-0107:06:59.989 E/AndroidRuntime( 6082): FATAL EXCEPTION: mainandroid

01-01 07:06:59.989 E/AndroidRuntime(6082): Process: com.ice.cream.maker, PID: 6082app

01-01 07:06:59.989 E/AndroidRuntime(6082): java.lang.RuntimeException: Unable to start activityComponentInfo{com.ice.cream.maker/com.unity3d.player.UnityPlayerNativeActivity}:java.lang.IllegalArgumentException: Unable to find native library main usingclassloader: dalvik.system.DexClassLoader[DexPathList[[zip file"/data/user/0/com.ice.cream.maker/app_vnr/classes.jar"],nativeLibraryDirectories=[/system/lib,/vendor/lib]]]ionic

01-01 07:06:59.989 E/AndroidRuntime(6082):      atandroid.app.ActivityThread.performLaunchActivity(ActivityThread.java:2665)函数

01-01 07:06:59.989 E/AndroidRuntime(6082):      atandroid.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)oop

01-01 07:06:59.989 E/AndroidRuntime(6082):      atandroid.app.ActivityThread.-wrap12(ActivityThread.java)测试

01-01 07:06:59.989 E/AndroidRuntime(6082):      atandroid.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)优化

01-01 07:06:59.989 E/AndroidRuntime(6082):      atandroid.os.Handler.dispatchMessage(Handler.java:102)ui

01-01 07:06:59.989 E/AndroidRuntime(6082):      atandroid.os.Looper.loop(Looper.java:154)this

01-01 07:06:59.989 E/AndroidRuntime(6082):      atandroid.app.ActivityThread.main(ActivityThread.java:6126)

01-01 07:06:59.989 E/AndroidRuntime(6082):      atjava.lang.reflect.Method.invoke(Native Method)

01-01 07:06:59.989 E/AndroidRuntime(6082):      atcom.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)

01-01 07:06:59.989 E/AndroidRuntime(6082):      atcom.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

01-01 07:06:59.989 E/AndroidRuntime(6082): Caused by: java.lang.IllegalArgumentException: Unable to find nativelibrary main using classloader: dalvik.system.DexClassLoader[DexPathList[[zipfile"/data/user/0/com.ice.cream.maker/app_vnr/classes.jar"],nativeLibraryDirectories=[/system/lib,/vendor/lib]]]

01-01 07:06:59.989 E/AndroidRuntime(6082):      atandroid.app.NativeActivity.onCreate(NativeActivity.java:168)

01-01 07:06:59.989 E/AndroidRuntime(6082):      atcom.unity3d.player.UnityPlayerNativeActivity.onCreate(Unknown Source)

01-01 07:06:59.989 E/AndroidRuntime(6082):      atandroid.app.Activity.performCreate(Activity.java:6720)

01-01 07:06:59.989 E/AndroidRuntime(6082):      atandroid.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)

01-01 07:06:59.989 E/AndroidRuntime(6082):      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2618)

01-01 07:06:59.989 E/AndroidRuntime(6082):      ... 9 more

01-01 07:06:59.994 W/ActivityManager(2598):   Force finishing activitycom.ice.cream.maker/com.unity3d.player.UnityPlayerNativeActivity

经过log会发现问题在android.app.NativeActivity.onCreate,对比N和M的代码会发现,N的代码在打开so的时候,方式发生变化。

N的代码以下,

         BaseDexClassLoader classLoader =(BaseDexClassLoader) getClassLoader();

        String path= classLoader.findLibrary(libname);

 

        if (path ==null) {

            thrownew IllegalArgumentException("Unable to find native library " + libname+

                                              " using classloader: " + classLoader.toString());

        }

 

        byte[]nativeSavedState = savedInstanceState != null

                ?savedInstanceState.getByteArray(KEY_NATIVE_SAVED_STATE) : null;

 

       mNativeHandle = loadNativeCode(path, funcname, Looper.myQueue(),

               getAbsolutePath(getFilesDir()), getAbsolutePath(getObbDir()),

               getAbsolutePath(getExternalFilesDir(null)),

               Build.VERSION.SDK_INT, getAssets(), nativeSavedState,

                classLoader,classLoader.getLdLibraryPath());

 

 

而M的代码以下

         String path = null;

       

        FilelibraryFile = new File(ai.applicationInfo.nativeLibraryDir,

               System.mapLibraryName(libname));

        if(libraryFile.exists()) {

            path =libraryFile.getPath();

        }

       

        if (path ==null) {

            thrownew IllegalArgumentException("Unable to find native library: " +libname);

        }

       

        byte[]nativeSavedState = savedInstanceState != null

                ?savedInstanceState.getByteArray(KEY_NATIVE_SAVED_STATE) : null;

 

       mNativeHandle = loadNativeCode(path, funcname, Looper.myQueue(),

               getAbsolutePath(getFilesDir()), getAbsolutePath(getObbDir()),

                getAbsolutePath(getExternalFilesDir(null)),

               Build.VERSION.SDK_INT, getAssets(), nativeSavedState);

 

 

能够看出,N使用了classLoader.findLibrary,并在loadNativeCode这个native接口添加了两个参数,类加载器和so库路径。

 

首先,在classLoader.findLibrary的时候,就没有找到对应的so,经过对DexClassLoader的分析和调试(DexClassLoader和类加载器知识见其余文档),咱们能够看到应用的类加载器在建立时,传入构造函数的参数,

 

 12-18 03:36:57.949E/System  ( 5359): shah:DexPathList:dexPath=/data/app/com.ice.cream.maker-1/base.apk

12-18 03:36:57.949 E/System  ( 5359): shah:DexPathList:librarySearchPath=/data/app/com.ice.cream.maker-1/lib/arm:/system/fake-libs:/data/app/com.ice.cream.maker-1/base.apk!/lib/armeabi-v7a

12-18 03:36:57.955 E/System  ( 5359): shah:DexPathList:nativeLibraryDirectories=[/data/app/com.ice.cream.maker-1/lib/arm,/system/fake-libs, /data/app/com.ice.cream.maker-1/base.apk!/lib/armeabi-v7a]

12-18 03:36:57.956 E/System  ( 5359): shah:DexPathList: systemNativeLibraryDirectories=[/system/lib,/vendor/lib]

12-18 03:36:57.957 E/System  ( 5359):shah:DexPathList:nativeLibraryPathElements  element=directory "/data/app/com.ice.cream.maker-1/lib/arm"

12-18 03:36:57.957 E/System  ( 5359): shah:DexPathList:nativeLibraryPathElements   element=directory"/system/fake-libs"

12-18 03:36:57.957 E/System  ( 5359):shah:DexPathList:nativeLibraryPathElements  element=zip file "/data/app/com.ice.cream.maker-1/base.apk",dir "lib/armeabi-v7a"

12-18 03:36:57.958 E/System  ( 5359):shah:DexPathList:nativeLibraryPathElements  element=directory "/system/lib"

12-18 03:36:57.958 E/System  ( 5359):shah:DexPathList:nativeLibraryPathElements  element=directory "/vendor/lib"

12-18 03:36:58.015 E/System  ( 5359): shah:DexPathList: dexPath=/data/user/0/com.ice.cream.maker/app_vnr/classes.jar

12-18 03:36:58.015 E/System  ( 5359): shah:DexPathList: librarySearchPath=null

12-18 03:36:58.015 E/System  ( 5359): shah:DexPathList:optimizedDirectory=/data/user/0/com.ice.cream.maker/app_vnr

 

能够看到,在用DexClassLoader加载的优化后的classes.jar文件对应的librarySearchPath为null,那么在classLoader.findLibrary的时候,天然是找不到对应的so库了,估计这个三方应用在使用DexClassLoader的时候,并无设置对相应的librarySearchPath值。

 

 

由于loadNativeCode也发生了变化,这里顺便看看android N对loadNativeCode的修改,这是一个native函数,映射在android_app_NativeActivity.cpp(frameworks\base\core\jni),对应的函数是loadNativeCode_native,函数有一点长,N和M的主要差异在打开库文件的方式:

 

M

     void* handle = dlopen(pathStr, RTLD_LAZY);

 

N

     void* handle = OpenNativeLibrary(env,sdkVersion, pathStr, classLoader, libraryPath);

 

 

再一路看下去,

 

Native_loader.cpp(system\core\libnativeloader),根据宏开关和参数能够看出通用的调用流程,在N上增长了命名空间的处理,而且这个目录和文件是N上新增的,专门用来处理命名空间的。

 void*OpenNativeLibrary(JNIEnv* env,

                       int32_t target_sdk_version,

                        const char* path,

                       jobject class_loader,

                       jstring library_path) {

#if defined(__ANDROID__)

 UNUSED(target_sdk_version);

  if (class_loader== nullptr) {

    returndlopen(path, RTLD_NOW);

  }

 

 std::lock_guard<std::mutex> guard(g_namespaces_mutex);

 android_namespace_t* ns =g_namespaces->FindNamespaceByClassLoader(env, class_loader);

 

  if (ns ==nullptr) {

    // This is thecase where the classloader was not created by ApplicationLoaders

    // In this case we create an isolatednot-shared namespace for it.

    ns =g_namespaces->Create(env, class_loader, false, library_path, nullptr);

    if (ns ==nullptr) {

      returnnullptr;

    }

  }

 

  android_dlextinfoextinfo;

  extinfo.flags =ANDROID_DLEXT_USE_NAMESPACE;

 extinfo.library_namespace = ns;

 

  returnandroid_dlopen_ext(path, RTLD_NOW, &extinfo);

#else

  UNUSED(env,target_sdk_version, class_loader, library_path);

  returndlopen(path, RTLD_NOW);

#endif

}

 

 

Dlfcn.cpp(bionic\linker):void* android_dlopen_ext()

 

 void*android_dlopen_ext(const char* filename, int flags, const android_dlextinfo*extinfo) {

  void* caller_addr= __builtin_return_address(0);

  returndlopen_ext(filename, flags, extinfo, caller_addr);

}

 

 

最后 经过dlopen_ext调用do_dlopen,打开库文件,这个处理逻辑和dlopen是同样的,因此实际只增长了命名空间的处理。

 static void*dlopen_ext(const char* filename, int flags,

                       const android_dlextinfo* extinfo, void* caller_addr) {

 ScopedPthreadMutexLocker locker(&g_dl_mutex);

  void* result =do_dlopen(filename, flags, extinfo, caller_addr);

  if (result ==nullptr) {

   __bionic_format_dlerror("dlopen failed",linker_get_error_buffer());

    return nullptr;

  }

  return result;

}

 

 

 

 void* dlopen(constchar* filename, int flags) {

  void* caller_addr= __builtin_return_address(0);

  returndlopen_ext(filename, flags, nullptr, caller_addr);

}