之前的Android熱更新實(shí)踐里面使用替換默認(rèn)ClassLoader的方式實(shí)現(xiàn)了熱修復(fù),但偶然發(fā)現(xiàn)這種方式在加載某些系統(tǒng)so庫(kù)的時(shí)候出現(xiàn)了問(wèn)題。
背景是我們的某個(gè)功能依賴了我司開(kāi)發(fā)的XXX.so這個(gè)系統(tǒng)so,這個(gè)XXX.so依賴了libandroid_runtime.so,而libandroid_runtime.so又依賴了libandroidicu.so:
然后在應(yīng)用層調(diào)用System.loadLibrary
去加載XXX.so的時(shí)候報(bào)了下面的異常:
03-26 02:27:07.385 3671 3695 E Demo: err : java.lang.UnsatisfiedLinkError: dlopen failed: library "libandroidicu.so" not found: needed by /system/lib64/libandroid_runtime.so in namespace classloader-namespace
03-26 02:27:07.385 3671 3695 E Demo: at java.lang.Runtime.loadLibrary0(Runtime.java:1077)
03-26 02:27:07.385 3671 3695 E Demo: at java.lang.Runtime.loadLibrary0(Runtime.java:998)
03-26 02:27:07.385 3671 3695 E Demo: at java.lang.System.loadLibrary(System.java:1661)
so加載
從報(bào)錯(cuò)來(lái)看是libandroidicu.so在classloader-namespace這個(gè)命名空間里面不可訪問(wèn)。原生庫(kù)的命名空間是安卓7.0引入的東西,目的在于限制native層私有api的訪問(wèn)。
從文檔上并不能直接定位到我們的問(wèn)題,但是從觸發(fā)異常的條件"默認(rèn)的ClassLoader去加載這個(gè)so是沒(méi)有問(wèn)題的,用我們熱修復(fù)的ClassLoader去加載就會(huì)出現(xiàn)異常"來(lái)看,
問(wèn)題原因應(yīng)該就是出在我們熱修復(fù)的ClassLoader的對(duì)應(yīng)的命名空間沒(méi)有權(quán)限去加載libandroidicu.so了。
我們先用find命令看看libandroidicu.so在系統(tǒng)的什么目錄:
console:/ # find . -name libandroidicu.so 2> /dev/null
./apex/com.android.i18n/lib/libandroidicu.so
./apex/com.android.i18n/lib64/libandroidicu.so
./system/apex/com.android.i18n/lib/libandroidicu.so
./system/apex/com.android.i18n/lib64/libandroidicu.so
的確不在/system/lib64下面,應(yīng)用加載不到它是合理的。但我好奇的是默認(rèn)的ClassLoader又是怎么加載到的?
從錯(cuò)誤堆棧的Runtime.loadLibrary0一路往jni追蹤可以發(fā)現(xiàn)so實(shí)際并不是由ClassLoader去加載的,而是通過(guò)ClassLoader找到了對(duì)應(yīng)的NativeLoaderNamespace,然后用NativeLoaderNamespace::Load去加載的:
具體的代碼調(diào)用如下:
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r39:libcore/ojluni/src/main/java/java/lang/Runtime.java
public class Runtime {
...
public void loadLibrary(String libname) {
loadLibrary0(Reflection.getCallerClass(), libname);
}
void loadLibrary0(Class<?> fromClass, String libname) {
ClassLoader classLoader = ClassLoader.getClassLoader(fromClass);
loadLibrary0(classLoader, fromClass, libname);
}
private synchronized void loadLibrary0(ClassLoader loader, Class<?> callerClass, String libname) {
...
nativeLoad(filename, loader);
...
}
private static String nativeLoad(String filename, ClassLoader loader) {
return nativeLoad(filename, loader, null);
}
private static native String nativeLoad(String filename, ClassLoader loader, Class<?> caller);
...
}
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r39:libcore/ojluni/src/main/native/Runtime.c
JNIEXPORT jstring JNICALL
Runtime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename,
jobject javaLoader, jclass caller)
{
return JVM_NativeLoad(env, javaFilename, javaLoader, caller);
}
static JNINativeMethod gMethods[] = {
FAST_NATIVE_METHOD(Runtime, freeMemory, "()J"),
FAST_NATIVE_METHOD(Runtime, totalMemory, "()J"),
FAST_NATIVE_METHOD(Runtime, maxMemory, "()J"),
NATIVE_METHOD(Runtime, nativeGc, "()V"),
NATIVE_METHOD(Runtime, nativeExit, "(I)V"),
NATIVE_METHOD(Runtime, nativeLoad,
"(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Class;)"
"Ljava/lang/String;"),
};
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r39:art/openjdkjvm/OpenjdkJvm.cc
JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
jstring javaFilename,
jobject javaLoader,
jclass caller) {
...
bool success = vm->LoadNativeLibrary(env,
filename.c_str(),
javaLoader,
caller,
&error_msg);
...
}
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r39:art/runtime/jni/java_vm_ext.cc
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
const std::string& path,
jobject class_loader,
jclass caller_class,
std::string* error_msg) {
...
void* handle = android::OpenNativeLibrary(
env,
runtime_->GetTargetSdkVersion(),
path_str,
class_loader,
(caller_location.empty() ? nullptr : caller_location.c_str()),
library_path.get(),
&needs_native_bridge,
&nativeloader_error_msg);
...
}
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r39:art/libnativeloader/native_loader.cpp
LibraryNamespaces* g_namespaces = new LibraryNamespaces;
void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
jobject class_loader, const char* caller_location, jstring library_path,
bool* needs_native_bridge, char** error_msg) {
...
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
NativeLoaderNamespace* ns;
if ((ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader)) == nullptr) {
// This is the case where the classloader was not created by ApplicationLoaders
// In this case we create an isolated not-shared namespace for it.
Result<NativeLoaderNamespace*> isolated_ns =
CreateClassLoaderNamespaceLocked(env,
target_sdk_version,
class_loader,
/*is_shared=*/false,
/*dex_path=*/nullptr,
library_path,
/*permitted_path=*/nullptr,
/*uses_library_list=*/nullptr);
if (!isolated_ns.ok()) {
*error_msg = strdup(isolated_ns.error().message().c_str());
return nullptr;
} else {
ns = *isolated_ns;
}
}
return OpenNativeLibraryInNamespace(ns, path, needs_native_bridge, error_msg);
...
}
void* OpenNativeLibraryInNamespace(NativeLoaderNamespace* ns, const char* path,
bool* needs_native_bridge, char** error_msg) {
auto handle = ns->Load(path);
...
return handle.ok() ? *handle : nullptr;
}
從ld.config.txt
可以看到不同的namespace有不同的search paths:
additional.namespaces = com_android_adbd,com_android_art,com_android_conscrypt,com_android_cronet,com_android_i18n,com_android_media,com_android_neuralnetworks,com_android_os_statsd,com_android_resolv,com_android_runtime,com_product_service1,product,rs,sphal,vndk,vndk_product
...
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /system_ext/${LIB}
...
namespace.com_android_i18n.search.paths = /apex/com.android.i18n/${LIB}
public libs
g_namespaces負(fù)責(zé)NativeLoaderNamespace的創(chuàng)建和緩存,NativeLoaderNamespace也是通過(guò)g_namespaces去Create出來(lái)的:
Result<NativeLoaderNamespace*> CreateClassLoaderNamespaceLocked(JNIEnv* env,
int32_t target_sdk_version,
jobject class_loader,
bool is_shared,
jstring dex_path,
jstring library_path,
jstring permitted_path,
jstring uses_library_list)
REQUIRES(g_namespaces_mutex) {
Result<NativeLoaderNamespace*> ns = g_namespaces->Create(env,
target_sdk_version,
class_loader,
is_shared,
dex_path,
library_path,
permitted_path,
uses_library_list);
...
return ns;
}
我們看看g_namespaces的Create方法創(chuàng)建NativeLoaderNamespace干了些啥:
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r39:art/libnativeloader/library_namespaces.cpp
Result<NativeLoaderNamespace*> LibraryNamespaces::Create(JNIEnv* env, uint32_t target_sdk_version,
jobject class_loader, bool is_shared,
jstring dex_path_j,
jstring java_library_path,
jstring java_permitted_path,
jstring uses_library_list) {
...
auto app_ns = NativeLoaderNamespace::Create(
namespace_name, library_path, permitted_path, parent_ns, is_shared,
target_sdk_version < 24 /* is_exempt_list_enabled */, also_used_as_anonymous);
...
for (const auto&[apex_ns_name, public_libs] : apex_public_libraries()) {
auto ns = NativeLoaderNamespace::GetExportedNamespace(apex_ns_name, is_bridged);
// Even if APEX namespace is visible, it may not be available to bridged.
if (ns.ok()) {
linked = app_ns->Link(&ns.value(), public_libs);
if (!linked.ok()) {
return linked.error();
}
}
}
...
// 緩存并返回app_ns
}
可以看到它在創(chuàng)建出app_ns之后會(huì)遍歷apex_public_libraries去鏈接apex里面的公共庫(kù):
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r39:art/libnativeloader/public_libraries.cpp
constexpr const char* kApexLibrariesConfigFile = "/linkerconfig/apex.libraries.config.txt";
const std::map<std::string, std::string>& apex_public_libraries() {
static std::map<std::string, std::string> public_libraries = InitApexLibraries("public");
return public_libraries;
}
static std::map<std::string, std::string> InitApexLibraries(const std::string& tag) {
std::string file_content;
if (!base::ReadFileToString(kApexLibrariesConfigFile, &file_content)) {
// config is optional
return {};
}
auto config = ParseApexLibrariesConfig(file_content, tag);
if (!config.ok()) {
LOG_ALWAYS_FATAL("%s: %s", kApexLibrariesConfigFile, config.error().message().c_str());
return {};
}
return *config;
}
在實(shí)機(jī)的/linkerconfig/apex.libraries.config.txt
下可以看到:
jni com_android_appsearch libicing.so
public com_android_art libnativehelper.so
jni com_android_btservices libbluetooth_jni.so
jni com_android_conscrypt libjavacrypto.so
public com_android_i18n libicui18n.so:libicuuc.so:libicu.so
public com_android_neuralnetworks libneuralnetworks.so
jni com_android_os_statsd libstats_jni.so
jni com_android_tethering libframework-connectivity-jni.so:libframework-connectivity-tiramisu-jni.so:libandroid_net_connectivity_com_android_net_module_util_jni.so:libservice-connectivity.so
jni com_android_uwb libuwb_uci_jni_rust.so
com_android_i18n的libicui18n.so
、libicuuc.so
、libicu.so
是公共的可以直接訪問(wèn)會(huì)被鏈接到app_ns允許訪問(wèn)。而報(bào)錯(cuò)的libandroidicu.so的確沒(méi)有在public里面,所以不會(huì)被鏈接,于是無(wú)法訪問(wèn)。
shared libs
實(shí)際上除了這個(gè)public libs配置之外,還有個(gè)shared libs的配置可以用于配置NativeLoaderNamespace對(duì)哪些NativeLoaderNamespace暴露哪些so。
詳細(xì)的規(guī)則可以參考鏈接器命名空間的文檔
具體的原理我們可以繼續(xù)往下追一層看看NativeLoaderNamespace::Create是怎么創(chuàng)建app_ns的:
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r39:art/libnativeloader/native_loader_namespace.cpp
Result<NativeLoaderNamespace> NativeLoaderNamespace::Create(
const std::string& name, const std::string& search_paths, const std::string& permitted_paths,
const NativeLoaderNamespace* parent, bool is_shared, bool is_exempt_list_enabled,
bool also_used_as_anonymous) {
...
// All namespaces for apps are isolated
uint64_t type = ANDROID_NAMESPACE_TYPE_ISOLATED;
...
if (is_shared) {
type |= ANDROID_NAMESPACE_TYPE_SHARED;
}
...
android_namespace_t* raw =
android_create_namespace(name.c_str(), nullptr, search_paths.c_str(), type,
permitted_paths.c_str(), effective_parent.ToRawAndroidNamespace());
if (raw != nullptr) {
return NativeLoaderNamespace(name, raw);
}
...
}
可以看到is_shared為true的時(shí)候會(huì)給type添加一個(gè)ANDROID_NAMESPACE_TYPE_SHARED的flag,這個(gè)flag最終在create_namespace會(huì)判斷:
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r39:bionic/linker/linker.cpp
static android_namespace_t* g_anonymous_namespace = &g_default_namespace;
...
android_namespace_t* create_namespace(const void* caller_addr,
const char* name,
const char* ld_library_path,
const char* default_library_path,
uint64_t type,
const char* permitted_when_isolated_path,
android_namespace_t* parent_namespace) {
if (parent_namespace == nullptr) {
// if parent_namespace is nullptr -> set it to the caller namespace
soinfo* caller_soinfo = find_containing_library(caller_addr);
parent_namespace = caller_soinfo != nullptr ?
caller_soinfo->get_primary_namespace() :
g_anonymous_namespace;
}
ProtectedDataGuard guard;
std::vector<std::string> ld_library_paths;
std::vector<std::string> default_library_paths;
std::vector<std::string> permitted_paths;
...
android_namespace_t* ns = new (g_namespace_allocator.alloc()) android_namespace_t();
...
if ((type & ANDROID_NAMESPACE_TYPE_SHARED) != 0) {
// append parent namespace paths.
std::copy(parent_namespace->get_ld_library_paths().begin(),
parent_namespace->get_ld_library_paths().end(),
back_inserter(ld_library_paths));
std::copy(parent_namespace->get_default_library_paths().begin(),
parent_namespace->get_default_library_paths().end(),
back_inserter(default_library_paths));
std::copy(parent_namespace->get_permitted_paths().begin(),
parent_namespace->get_permitted_paths().end(),
back_inserter(permitted_paths));
// If shared - clone the parent namespace
add_soinfos_to_namespace(parent_namespace->soinfo_list(), ns);
// and copy parent namespace links
for (auto& link : parent_namespace->linked_namespaces()) {
ns->add_linked_namespace(link.linked_namespace(), link.shared_lib_sonames(),
link.allow_all_shared_libs());
}
} else {
// If not shared - copy only the shared group
add_soinfos_to_namespace(parent_namespace->get_shared_group(), ns);
}
ns->set_ld_library_paths(std::move(ld_library_paths));
ns->set_default_library_paths(std::move(default_library_paths));
ns->set_permitted_paths(std::move(permitted_paths));
...
}
可以看到添加了ANDROID_NAMESPACE_TYPE_SHARED這個(gè)flag的話會(huì)添加父namespace的所有so和鏈接父namespace連接過(guò)的namespace。而如果為false則只會(huì)添加父namespace的shared so。
系統(tǒng)默認(rèn)創(chuàng)建的Classloader對(duì)應(yīng)的namespace的parent_namespace為null,于是會(huì)把parent_namespace設(shè)置成默認(rèn)的"default"這個(gè)namespace。
而從/linkerconfig/ld.config.txt
里面可以看到"default"這個(gè)namespace是鏈接了com_android_i18n這個(gè)namespace,可以從里面訪問(wèn)libandroidicu.so
:
...
namespace.default.links = com_android_adbd,com_android_i18n,default,com_android_art,com_android_resolv,com_android_tethering,com_android_neural
namespace.default.link.com_android_adbd.shared_libs = libadb_pairing_auth.so
namespace.default.link.com_android_adbd.shared_libs += libadb_pairing_connection.so
namespace.default.link.com_android_adbd.shared_libs += libadb_pairing_server.so
namespace.default.link.com_android_i18n.shared_libs = libandroidicu.so
namespace.default.link.com_android_i18n.shared_libs += libicu.so
namespace.default.link.com_android_i18n.shared_libs += libicui18n.so
namespace.default.link.com_android_i18n.shared_libs += libicuuc.so
...
所以默認(rèn)的ClassLoader創(chuàng)建NativeLoaderNamespace的時(shí)候is_shared是true可以加載到libandroidicu.so
而我們自定義的ClassLoader創(chuàng)建NativeLoaderNamespace的時(shí)候is_shared是false沒(méi)有繼承"default"這個(gè)parent_namepsace的links配置無(wú)法加載libandroidicu.so
另外也可以看到默認(rèn)的Classloader對(duì)應(yīng)的namespace會(huì)連接com_android_i18n這個(gè)命名空間兩次
第一次在create\_namespace
函數(shù)里由于ANDROID_NAMESPACE_TYPE_SHARED
繼承了defalut
這個(gè)parent_namespace的links配置能訪問(wèn)/linkerconfig/ld.config.txt
里的namespace.default.link.com_android_i18n.shared_libs
配置的shared so,
第二次則是在LibraryNamespaces::Create里面賭錢/linkerconfig/apex.libraries.config.txt
里public com_android_i18n
配置鏈接com_android_i18n
的public so。
鏈接器命名空間這個(gè)文檔里也有提到:
此屬性與 public.libraries.txt 文件在底層實(shí)現(xiàn)上是相同的。這兩種機(jī)制都通過(guò)使用庫(kù)名稱過(guò)濾器指定鏈接的方式來(lái)控制導(dǎo)入的共享庫(kù)。
is shared
從前面CreateClassLoaderNamespaceLocked的傳參來(lái)看我們的自定義Classloader創(chuàng)建namespace的時(shí)候is_shared的確為false:
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r39:art/libnativeloader/native_loader.cpp
LibraryNamespaces* g_namespaces = new LibraryNamespaces;
void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
jobject class_loader, const char* caller_location, jstring library_path,
bool* needs_native_bridge, char** error_msg) {
...
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
NativeLoaderNamespace* ns;
if ((ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader)) == nullptr) {
// This is the case where the classloader was not created by ApplicationLoaders
// In this case we create an isolated not-shared namespace for it.
Result<NativeLoaderNamespace*> isolated_ns =
CreateClassLoaderNamespaceLocked(env,
target_sdk_version,
class_loader,
/*is_shared=*/false,
/*dex_path=*/nullptr,
library_path,
/*permitted_path=*/nullptr,
/*uses_library_list=*/nullptr);
if (!isolated_ns.ok()) {
*error_msg = strdup(isolated_ns.error().message().c_str());
return nullptr;
} else {
ns = *isolated_ns;
}
}
return OpenNativeLibraryInNamespace(ns, path, needs_native_bridge, error_msg);
...
}
那默認(rèn)的ClassLoader的is_shared是怎么設(shè)置成true的呢?從LoadedApk代碼里面可以看到系統(tǒng)app最終就會(huì)調(diào)用ClassLoaderFactory.createClassLoader在里面創(chuàng)建Classloader的同時(shí)調(diào)用createClassloaderNamespace創(chuàng)建namespace,傳入的is_shared為isBundledApp即true:
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r39:frameworks/base/core/java/android/app/LoadedApk.java
public ClassLoader getClassLoader() {
synchronized (mLock) {
if (mClassLoader == null) {
createOrUpdateClassLoaderLocked(null /*addedPaths*/);
}
return mClassLoader;
}
}
private void createOrUpdateClassLoaderLocked(List<String> addedPaths) {
...
boolean isBundledApp = mApplicationInfo.isSystemApp()
&& !mApplicationInfo.isUpdatedSystemApp();
...
mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(
zip, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
libraryPermittedPath, mBaseClassLoader,
mApplicationInfo.classLoaderName, sharedLibraries.first, nativeSharedLibraries,
sharedLibraries.second);
mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
...
}
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r39:frameworks/base/core/java/android/app/ApplicationLoaders.java
ClassLoader getClassLoaderWithSharedLibraries(
String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String classLoaderName,
List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries,
List<ClassLoader> sharedLibrariesLoadedAfterApp) {
// For normal usage the cache key used is the same as the zip path.
return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries,
nativeSharedLibraries, sharedLibrariesLoadedAfterApp);
}
ClassLoader getClassLoaderWithSharedLibraries(
String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String classLoaderName,
List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries,
List<ClassLoader> sharedLibrariesLoadedAfterApp) {
// For normal usage the cache key used is the same as the zip path.
return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries,
nativeSharedLibraries, sharedLibrariesLoadedAfterApp);
}
rivate ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String cacheKey,
String classLoaderName, List<ClassLoader> sharedLibraries,
List<String> nativeSharedLibraries,
List<ClassLoader> sharedLibrariesLoadedAfterApp) {
...
ClassLoader classloader = ClassLoaderFactory.createClassLoader(
zip, librarySearchPath, libraryPermittedPath, parent,
targetSdkVersion, isBundled, classLoaderName, sharedLibraries,
nativeSharedLibraries, sharedLibrariesLoadedAfterApp);
...
return loader;
...
}
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r39:frameworks/base/core/java/com/android/internal/os/ClassLoaderFactory.java
public static ClassLoader createClassLoader(String dexPath,
String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
int targetSdkVersion, boolean isNamespaceShared, String classLoaderName,
List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries,
List<ClassLoader> sharedLibrariesAfter) {
final ClassLoader classLoader = createClassLoader(dexPath, librarySearchPath, parent,
classLoaderName, sharedLibraries, sharedLibrariesAfter);
...
String errorMessage = createClassloaderNamespace(classLoader,
targetSdkVersion,
librarySearchPath,
libraryPermittedPath,
isNamespaceShared,
dexPath,
sonameList);
...
return classLoader;
}
解決方案
方法1
由于ClassLoaderFactory.createClassloaderNamespace是private的不能在外部調(diào)用,所以解決自定義classloader找不到libandroidicu.so的方法就是不要自己直接new ClassLoader,而是調(diào)用ClassLoaderFactory.createClassLoader去創(chuàng)建,傳入isNamespaceShared為true。
方法2
由于系統(tǒng)默認(rèn)的classloader對(duì)應(yīng)的namespace已經(jīng)加載了libandroid_runtime.so,如果將我們自定義的classloader的父classloader設(shè)置成系統(tǒng)默認(rèn)的classloder,則自定義classloader對(duì)應(yīng)的namespace的parent_namespace也會(huì)指向默認(rèn)classloader的namespace。
然后這個(gè)namespace已經(jīng)加載了libandroid_runtime.so,于是在后面的add_soinfos_to_namespace(parent_namespace->get_shared_group(), ns);
里面就能直接使用已經(jīng)加載好的shared so libandroid_runtime.so:
使用setprop debug.ld.all dlopen,dlerror
命令打開(kāi)全部linker打印也可以看到libandroid_runtime.so Already loaded的日志:
03-29 04:36:10.024 6046 6069 D linker : find_library_internal(ns=classloader-namespace, task=libandroid_runtime.so): Already loaded (by sona me): /system/lib64/libandroid_runtime.so`
但是當(dāng)父classloader設(shè)置成系統(tǒng)默認(rèn)的classloader之后由于雙親委托的機(jī)制,會(huì)先從父classloader去加載class,達(dá)不到熱修復(fù)的需求。
于是我們需要修改自定義classloader的loadClass打破雙親委托機(jī)制,先自己去加載class,加載不到再讓父classloder去加載:
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
//先從自己查找,找不到再?gòu)母竎lassloader查找,實(shí)現(xiàn)熱修復(fù)
Class<?> c = null;
try {
c = findClass(name);
} catch (ClassNotFoundException e) {
// ignore
}
if (c == null) {
c = getParent().loadClass(name);
}
return c;
}