一个三方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);
}