国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android so的熱升級嘗試

2019-10-22 18:22:48
字體:
供稿:網(wǎng)友

一、So的熱升級嘗試

在Android代碼中,加載softtech/biancheng/281183.html">so庫是通過調(diào)用System.loadLibrary函數(shù)實現(xiàn)的。但和Android的許多特性一樣,只提供了加載,而沒有卸載和更換等功能。為了研究能否實現(xiàn)卸載和升級等功能,首先要了解清楚JNI so加載的流程。

android,so,熱升級,熱更新

在以上流程中,使用dlopen加載so之后,會繼續(xù)調(diào)用JNI_Onload函數(shù),通過系統(tǒng)提供的RegisterNatives函數(shù)完成一些列初始化,向虛擬機注冊so庫提供的JNI函數(shù)。So庫也可以不實現(xiàn)JNI_Onload函數(shù),而是采用自動查找的方式。

Android虛擬機會在首次調(diào)用JNI函數(shù)時按照JNI規(guī)范的命名規(guī)則自動查找。通過分析Android代碼,這種方法最終也會調(diào)用到上圖中的dvmSetNativeFunc等函數(shù),將函數(shù)地址保存到虛擬機中供下次調(diào)用。

二、卸載及重新加載

如果想要提供熱升級的能力,首先要做的是關(guān)閉已打開的so文件。但Android虛擬機沒有提供unloadLibrary這樣的接口,因此需要我們自己自己實現(xiàn)。

根據(jù)上一節(jié)的分析,loadLibrary在native層加載文件使用的是dlopen,與之對應(yīng)的系統(tǒng)接口是dlclose。而接下來的RegisterNatives由于沒有對應(yīng)的unRegister,我們暫且先放一放,看看卸載的效果再來處理。

卸載so

提供卸載能力的接口需要完成以下幾項任務(wù):

1、找到要卸載so的句柄;

2、調(diào)用JNI_OnUnload;

3、調(diào)用dlclose卸載。

如下便是我們寫出的卸載函數(shù):

void JNICALL Java_com_example_Unloader_unload(JNIEnv* env, jobject obj){void* handle = dlopen(“/data/data/com.example.unloader/lib/libtest.so”, RTLD_GLOBAL);if(!handle) return;LOGD(“unload so: 0x%x/n”, (unsigned int)handle);void* symbol = dlsym(handle, “JNI_OnUnload”);if(symbol){OnLoadFunc func = (OnLoadFunc)symbol;JavaVM* jvm = 0;(*env)->GetJavaVM(env, &jvm);if(jvm)(*func)(jvm, 0);}int result = dlclose(handle);LOGD(“unload result %d/n”, result);result = dlclose(handle);result = dlclose(handle);LOGD(“unload result %d/n”, result);}

其中dlclose調(diào)用了2次,因為函數(shù)內(nèi)的dlopen會增加handle的引用計數(shù)。

卸載之后如果我們先嘗試調(diào)用原來的JNI函數(shù),會發(fā)生什么事呢?顯而易見會出現(xiàn)crash。

android,so,熱升級,熱更新

究其原因,是由于so在加載或使用時已經(jīng)在虛擬機中注冊了JNI函數(shù)的地址,卸載后原地址變?yōu)榉欠ǖ刂罚瑢?dǎo)致crash。那我們再重新加載so會發(fā)生什么呢?

重新加載so

分析代碼可得知,由于so已經(jīng)使用System.loadLibrary加載過,我們之前在卸載時也沒有觸及到JNI層,因此重復(fù)調(diào)用loadLibrary并不會重新加載so。我們可以按照dvmLoadNativeCode的流程,在native層用dlopen重新加載so。

按照之前的分析,很容易就能寫出加載函數(shù):

void JNICALL Java_com_example_Unloader_load(JNIEnv* env, jobject obj){void* handle = dlopen(“/data/data/com.example.Unloader/lib/libtest.so”, RTLD_GLOBAL);if(!handle) return;LOGD(“load so: 0x%x/n”, (unsigned int)handle);void* symbol = dlsym(handle, “JNI_OnLoad”);if(symbol){OnLoadFunc func = (OnLoadFunc)symbol;JavaVM* jvm = 0;(*env)->GetJavaVM(env, &jvm);if(jvm)(*func)(jvm, 0);}}

三、問題及解決

重新加載so后,再次調(diào)用原來的JNI函數(shù)。發(fā)現(xiàn)有時候會成功,但有時候也會crash。經(jīng)過追蹤后注意到,報錯的函數(shù)地址和卸載前一樣,但so加載的地址變化了。

android,so,熱升級,熱更新

由于dlopen加載so時,并不能保證每次都加載在同一地址上。即使能夠加載到同一地址,如果升級造成so文件變化,那函數(shù)地址也是不準確的。所以要使新的so工作,那我們也必須要設(shè)法更新虛擬機已經(jīng)保存的函數(shù)指針,將其指向新加載so的正確地址。

這時候就需要我們之前忽略的RegisterNatives登場了,這個函數(shù)可以用來手動注冊JNI函數(shù)地址。讓我們重復(fù)與第一節(jié)文字相似但含義不同的這段話:

在以上流程中,so庫在使用dlopen加載后,還需要調(diào)用JNI_Onload函數(shù),通過系統(tǒng)提供的RegisterNatives函數(shù)完成一些列初始化,向虛擬機注冊新的JNI函數(shù)地址。

static JNINativeMethod gMethods[] = {{ “foo”, “()V”, (void*)Java_com_tencent_example_foo },};// Register several native methods for one class.static int registerNativeMethods(JNIEnv* env, const char* className,JNINativeMethod* gMethods, int numMethods){jclass clazz = (*env)->FindClass(env, className);if (clazz == NULL) {return JNI_FALSE;}if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {return JNI_FALSE;}return JNI_TRUE;}

使用RegisterNatives注冊后,即使so的地址發(fā)生變化,也能夠更新虛擬機中記錄的函數(shù)地址。

android,so,熱升級,熱更新 

本篇小結(jié)

如果想要在運行時更新so,則新的so文件必須要實現(xiàn)JNI_Onload函數(shù),并且在JNI_Onload中調(diào)用系統(tǒng)提供的RegisterNatives注冊所有的JNI函數(shù),不能使用自動查找JNI函數(shù)名的方式。

四、其他問題

以上方案主要解決了so的卸載,重加載和JNI函數(shù)調(diào)用問題。但除了這些問題之外,so代碼的細節(jié)上還有許多要注意的地方。

CRASH

卸載so后,除了JNI函數(shù)的指針,其它指向so地址的指針也都會失效,包括指向靜態(tài)變量,常量,native函數(shù)的指針等。所有引用到該so地址的指針都需要更新。

內(nèi)存和資源泄漏

native代碼中可能存在各種分配內(nèi)存和資源的行為,使用以上方法更新so前,如果沒有仔細處理這些資源,就會丟失原指針,造成內(nèi)存泄漏。

1、malloc/mmap/shmem等方式分配的內(nèi)存。

2、socket, pipe, mutex, thread等各種系統(tǒng)資源。

3、使用NewGlobalRef分配并持有Java對象,丟失指針后會造成虛擬機的Java內(nèi)存泄漏。

綜上所述,對于所有可能丟失,造成泄露的資源,必須在卸載so前設(shè)法保存或刪除。這些工作可以在卸載時調(diào)用的JNI_OnUnload中完成。

版權(quán)所屬,禁止轉(zhuǎn)載


注:相關(guān)教程知識閱讀請移步到Android開發(fā)頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 永善县| 大厂| 阿勒泰市| 喀喇沁旗| 宝丰县| 华宁县| 常山县| 秭归县| 金平| 祁阳县| 米林县| 沁源县| 德州市| 禹州市| 柳河县| 佛教| 靖西县| 阜南县| 轮台县| 松滋市| 红桥区| 三穗县| 沙河市| 龙海市| 永寿县| 洮南市| 无为县| 甘肃省| 顺义区| 恭城| 岢岚县| 山东| 德兴市| 土默特左旗| 长泰县| 建水县| 鞍山市| 阜南县| 泰州市| 鞍山市| 芷江|