深入分析Android加載so文件源碼
Android系統(tǒng)中使用ndk進(jìn)行編程,有很多的好處(Java的跨平臺(tái)特性導(dǎo)致其本地交互的能力不夠強(qiáng)大,一些和操作系統(tǒng)相關(guān)的特性Java無(wú)法完成;代碼的保護(hù):由于apk的java層代碼很容易被反編譯,而C/C++庫(kù)反匯難度較大;可以方便地使用C/C++開(kāi)源庫(kù);便于移植,用C/C++寫(xiě)的庫(kù)可以方便在其他平臺(tái)上再次使用;提供程序在某些特定情形下的執(zhí)行效率,但是并不能明顯提升Android程序的性能)。
要使用ndk進(jìn)行編程,在Java層就必須要對(duì)so進(jìn)行加載。Java層加載so的函數(shù)有兩個(gè):
System.load(String pathName) System.loadLibraray(String libName)
兩個(gè)函數(shù)的區(qū)別就是load函數(shù)的參數(shù)是so文件的絕對(duì)地址。loadLibrary的參數(shù)是so的名稱,這個(gè)so文件必須放在apk的lib目錄下,而且so的名稱必須去掉前面的lib和后邊的“.so”。如下所示:
System.load("/data/local/tmp/libhello.so");
System.loadLibrary("hello");
System.java
load和loadLibraray函數(shù)在/android6.0/libcore/luni/src/main/java/java/lang/System.java中:
public static void load(String pathName) {
Runtime.getRuntime().load(pathName, VMStack.getCallingClassLoader());
}
/**
* See {@link Runtime#loadLibrary}.
*/
public static void loadLibrary(String libName) {
Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());
}
Runtime.java
getRuntime()函數(shù)用于獲取Runtime的一個(gè)實(shí)例。
public static Runtime getRuntime() {
return mRuntime;
}
loadLibrary():
public void loadLibrary(String nickname) {
loadLibrary(nickname, VMStack.getCallingClassLoader());
}
void loadLibrary(String libraryName, ClassLoader loader) {
if (loader != null) {
String filename = loader.findLibrary(libraryName);
if (filename == null) {
// It's not necessarily true that the ClassLoader used
// System.mapLibraryName, but the default setup does, and it's
// misleading to say we didn't find "libMyLibrary.so" when we
// actually searched for "liblibMyLibrary.so.so".
throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
System.mapLibraryName(libraryName) + "\"");
}
String error = doLoad(filename, loader);
if (error != null) {
throw new UnsatisfiedLinkError(error);
}
return;
}
String filename = System.mapLibraryName(libraryName);
List<String> candidates = new ArrayList<String>();
String lastError = null;
for (String directory : mLibPaths) {
String candidate = directory + filename;
candidates.add(candidate);
if (IoUtils.canOpenReadOnly(candidate)) {
String error = doLoad(candidate, loader);
if (error == null) {
return; // We successfully loaded the library. Job done.
}
lastError = error;
}
}
if (lastError != null) {
throw new UnsatisfiedLinkError(lastError);
}
throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
}
loadLibrary()函數(shù)主要進(jìn)行了兩步操作。
第一步:獲取library的path:
根據(jù)ClassLoader的不同,會(huì)有兩種不同的處理方法。
如果ClassLoader非空,會(huì)利用ClassLoader的findLibrary()方法獲取library的path。
如果ClassLoader為空,會(huì)通過(guò)傳入的library name和System.mapLibraryName獲得真正的library name。例如傳入的是hello,
得到的是libhello.so,然后在mLibPaths查找`libhello.so',最終確定library的path。
第二步:調(diào)用doLoad()方法。
第一步目前我不關(guān)心,不去深究。主要看doLoad的實(shí)現(xiàn)。
private String doLoad(String name, ClassLoader loader) {
String ldLibraryPath = null;
String dexPath = null;
if (loader == null) {
// We use the given library path for the boot class loader. This is the path
// also used in loadLibraryName if loader is null.
ldLibraryPath = System.getProperty("java.library.path");
} else if (loader instanceof BaseDexClassLoader) {
BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;
ldLibraryPath = dexClassLoader.getLdLibraryPath();
}
// nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless
// of how many ClassLoaders are in the system, but dalvik doesn't support synchronized
// internal natives.
synchronized (this) {
return nativeLoad(name, loader, ldLibraryPath);
}
}
獲得libbrary的路徑;
調(diào)用native函數(shù)nativeLoad()進(jìn)行加載加載。

java_lang_Runtime.cc
文件位置:/android6.0.1_r66/art/runtime/native/java_lang_Runtime.cc
static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, jobject javaLoader,
jstring javaLdLibraryPathJstr) {
ScopedUtfChars filename(env, javaFilename);
if (filename.c_str() == nullptr) {
return nullptr;
}
SetLdLibraryPath(env, javaLdLibraryPathJstr);
std::string error_msg;
{
JavaVMExt* vm = Runtime::Current()->GetJavaVM();
bool success = vm->LoadNativeLibrary(env, filename.c_str(), javaLoader, &error_msg);
if (success) {
return nullptr;
}
}
// Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.
env->ExceptionClear();
return env->NewStringUTF(error_msg.c_str());
}
nativeLoad()主要做了兩件事:
第一件事:利用SetLdLibraryPath()將Java的library的path轉(zhuǎn)換成native的。
第二件事情:調(diào)用LoadNativeLibrary進(jìn)行加載。<關(guān)鍵>
java_vm_ext.cc
位置:/android6.0/art/runtime/java_vm_ext.cc
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject class_loader,
std::string* error_msg) {
...
const char* path_str = path.empty() ? nullptr : path.c_str();
void* handle = dlopen(path_str, RTLD_NOW);
...
if (needs_native_bridge) {
library->SetNeedsNativeBridge();
sym = library->FindSymbolWithNativeBridge("JNI_OnLoad", nullptr);
} else {
sym = dlsym(handle, "JNI_OnLoad");
}
if (sym == nullptr) {
VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
was_successful = true;
} else {
利用dlopen()打開(kāi)so文件,得到函數(shù)的指針
利用dlsym()調(diào)用so文件中的JNI_OnLoad方法,開(kāi)始so文件的執(zhí)行。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
安卓(Android)動(dòng)態(tài)創(chuàng)建多個(gè)按鈕并添加監(jiān)聽(tīng)事件
本文主要介紹Android動(dòng)態(tài)創(chuàng)建多個(gè)按鈕并給每個(gè)按鍵添加監(jiān)聽(tīng)事件,在做Android項(xiàng)目會(huì)經(jīng)常遇到的,希望對(duì)需要用到的同學(xué)有所幫助2016-07-07
ubuntu 12.10 上 android 編譯環(huán)境搭建的深入解析
本篇文章是對(duì)ubuntu 12.10上android 編譯環(huán)境的搭建進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
Android NotificationManager簡(jiǎn)單使用詳解
這篇文章主要為大家詳細(xì)介紹了Android NotificationManager的簡(jiǎn)單使用,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
詳解Android Libgdx中ScrollPane和Actor事件沖突問(wèn)題的解決辦法
這篇文章主要介紹了詳解Android Libgdx中ScrollPane和Actor事件沖突問(wèn)題的解決辦法的相關(guān)資料,希望通過(guò)本文能幫助到大家,需要的朋友可以參考下2017-09-09
Android進(jìn)階教程之ViewGroup自定義布局
這篇文章主要給大家介紹了關(guān)于Android進(jìn)階教程之ViewGroup自定義布局的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android開(kāi)發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06
詳解android studio游戲搖桿開(kāi)發(fā)教程,仿王者榮耀搖桿
這篇文章主要介紹了android studio游戲搖桿開(kāi)發(fā)教程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05
Popupwindow 的簡(jiǎn)單實(shí)用案例(顯示在控件下方)
下面小編就為大家?guī)?lái)一篇Popupwindow 的簡(jiǎn)單實(shí)用案例(顯示在控件下方)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04
android使用intent傳遞參數(shù)實(shí)現(xiàn)乘法計(jì)算
這篇文章主要為大家詳細(xì)介紹了android使用intent傳遞參數(shù)實(shí)現(xiàn)乘法計(jì)算,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04

