好久沒有寫點(diǎn)東西發(fā)了,工作中的事情有點(diǎn)雜,也找不到整塊東西可以寫的。

創(chuàng)新互聯(lián)是一家專注于做網(wǎng)站、成都網(wǎng)站制作與策劃設(shè)計(jì),扎蘭屯網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)十余年,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:扎蘭屯等地區(qū)。扎蘭屯做網(wǎng)站價(jià)格咨詢:18982081108
最近調(diào)查了一個(gè)問題,稍微追了一下流程,這里記錄一下。
由于我們支持的設(shè)備相對(duì)比競(jìng)品,zygote進(jìn)程多占用了好幾倍的內(nèi)存空間。通過dump meminfo后發(fā)現(xiàn),我們的設(shè)備在so庫,ttf,和unkonwn mmap的內(nèi)存空間相比競(jìng)品一共大了20多M,其中so庫多了15M左右。
通過查看zygote進(jìn)程的smaps,確定了占用空間最大的幾個(gè)so庫確實(shí)是我們自己的。雖然確定了內(nèi)存占用大的原因,還是得把這些so庫是加載在zygote進(jìn)程中的時(shí)機(jī)確定了才行。
我的第一反應(yīng)就是在zygote啟動(dòng)時(shí),加載sharedLibrary()時(shí),把這些庫加載了,于是去看了這部分源碼,并沒有。
通過反復(fù)調(diào)試以及追蹤源碼,最后發(fā)現(xiàn)是在JVM啟動(dòng)的過程中加載了這些so庫,這些so庫的配置在“system/etc/public.libraries.txt”下。
這個(gè)文件里配置的都是public的so庫,能夠被普通app訪問的。類似的配置文件還有“vendor/etc/”下面的,還有一些其他的配置地方,我沒有深入去看,想要看的盆友可以自己去看源碼或者注釋。
下面我就帶大家一起看看虛擬機(jī)加載這些so庫的流程。
調(diào)用棧:
frameworks/base/cmds/app_process/app_main.cpp
----runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
frameworks/base/core/jni/AndroidRuntime.cpp
----AndroidRuntime::startVm
---JNI_CreateJavaVM(pJavaVM, pEnv, initArgs)
art/runtime/jni/java_vm_ext.cc
---android::InitializeNativeLoader();
system/core/libnativeloader/native_loader.cpp
----Initialize()
----ReadConfig(public_native_libraries_system_config, sonames, always_true, error_msg)
其中,public_native_libraries_system_config 為 system/etc/public.libraries.txt
這部分流程是在安卓設(shè)備開機(jī)過程中的,在執(zhí)行ZygoteInit.main()之前會(huì)先啟動(dòng)java虛擬機(jī)的,這樣fork其他java進(jìn)程的時(shí)候,java環(huán)境就已經(jīng)有了,不用再創(chuàng)建虛擬機(jī)了。
最后貼一下Initialize()函數(shù):
1,在項(xiàng)目根目錄下建立文件夾libs/armeabi文件夾
2,將so庫放入libs/armeabi文件夾注意事項(xiàng):
1,如果采用靜態(tài)注冊(cè)的方式請(qǐng)注意C文件中嚴(yán)格按照命名規(guī)則Java_packageName_className_method()的方式命名
2,在Android項(xiàng)目中建立同上述命名規(guī)則中packageName中相同的包名,在此包名下建立同上述命名規(guī)則中className相同的類名
3,在className聲明native方法
4,程序中加載so庫System.loadLibrary(data/data/xxx.xxx.xxx/lib/xx.so)或者System.loadLibrary(xx),例如:System.loadLibrary(data/data/com.dtBank.app.service/lib/libjnixcld.so);
作者君主要做SDK開發(fā),對(duì)接一些廠商或運(yùn)行商的普通應(yīng)用或系統(tǒng)應(yīng)用。
當(dāng)對(duì)接系統(tǒng)應(yīng)用時(shí),由于系統(tǒng)應(yīng)用是由于覆蓋機(jī)型比較廣,會(huì)碰到Android多個(gè)版本機(jī)型,有的可能出現(xiàn)so找不到的問題。
普通install安裝apk的方式,apk會(huì)被安裝在 /data/app 目錄下,那么So則會(huì)被映射到/data/app/項(xiàng)目目錄下/lib。
首次安裝只能通過直接push到/system/app/下的方式來安裝,而不是如普通應(yīng)用般采取install的方式。
android在開機(jī)掃描應(yīng)用的時(shí)候會(huì)對(duì) 相應(yīng)目錄進(jìn)行掃描,如果發(fā)現(xiàn) data/app目錄下 存在和系統(tǒng)應(yīng)用同包名的應(yīng)用,并且版本號(hào)比系統(tǒng)應(yīng)用的版本號(hào)更高則構(gòu)成升級(jí)關(guān)系,校驗(yàn)簽名等安全驗(yàn)證通過。此時(shí)data/app下的這個(gè)應(yīng)用就是系統(tǒng)應(yīng)用
而push到系統(tǒng)目錄下如system/app/....,則會(huì)優(yōu)先尋找system/lib(lib64)目錄下的so,由于so
不會(huì)自動(dòng)釋放到該目錄下,所以需要手動(dòng)push到該路徑下。
作者君還遇到過這樣的問題,有時(shí)候?yàn)榱藴p少包的大小或者其他,不會(huì)把所有ABI類型的so都放進(jìn)目錄,另外so的提供者團(tuán)隊(duì)沒有提供64的so(哈哈,如果提供了,下面的問題直接就沒有了,那為什么還拿出來說呢,主要是簡(jiǎn)單地了解一下系統(tǒng)底層的一些基本原理,感興趣的可以看下一下~~)
機(jī)型類型是64位的,其他apk僅提供了64位的so。而某個(gè)apk由于只集成了32位so, install安裝是正常的,但是把a(bǔ)pk通過push到/system/app下,so放到/system/lib下則報(bào)如下錯(cuò)誤:
32位機(jī)器上當(dāng)然是正常的。
那么為什么會(huì)出錯(cuò)呢?首先是系統(tǒng)級(jí)應(yīng)用,需要理解Android系統(tǒng)的原理(當(dāng)然啦,也許廠商定制了一番,那則另一回事。):
系統(tǒng)有幾個(gè)屬性,其中app.info.primaryCpuAbi這個(gè)值用來決定apk關(guān)聯(lián)ABI類型。而PackageManager會(huì)對(duì)這個(gè)值有所影響。比如:通過apk包里包含的so庫的架構(gòu)來決定app的primaryCpuAbi的值。
另外:
如果機(jī)器里有64位的apk,且PackageManager掃描到第一正好是這個(gè)apk,PackageManager調(diào)整所有apk要加載的都是64位的so。不再去加載32位的so,那么只含32位so的apk就會(huì)跑出異常。反之,則64位的apk正常運(yùn)行,32位的則出錯(cuò)。
作者君能力有限,如有錯(cuò)處,請(qǐng)書友們指導(dǎo),作者君會(huì)第一時(shí)間修改。
一起學(xué)習(xí) 一起進(jìn)步 ?( ′???` )比心
1、在src/main中添加
jniLibs文件夾
,把.so復(fù)制進(jìn)去
2、在build.gradle中就添加這么幾行
,
看圖
復(fù)制內(nèi)容到剪貼板
sourceSets
{
main
{
jniLibs.srcDirs
=
['libs']
}
}
3、然后make
project
4、切換到android結(jié)構(gòu)下,你會(huì)看到
jniLibs
中.so已經(jīng)變成了.jar文件,證明已經(jīng)成功
android中加載so文件:
在Android中調(diào)用動(dòng)態(tài)庫文件(*.so)都是通過jni的方式,而且往往在apk或jar包中調(diào)用so文件時(shí),都要將對(duì)應(yīng)so文件打包進(jìn)apk或jar包,工程目錄下圖:
Android中加載so文件的提供的API:
void System.load(String pathName);
說明:
1、pathName:文件名+文件路勁;
2、該方法調(diào)用成功后so文件中的導(dǎo)出函數(shù)都將插入的系統(tǒng)提供的一個(gè)映射表(類型Map);
3、具體代碼如下:
try {?
String localPath = Environment.getExternalStorageDirectory() + path;?
Log.v(TAG, "LazyBandingLib localPath:" + localPath);
String[] tokens = mPatterns.split(path);?
if (null == tokens || tokens.length = 0?
|| tokens[tokens.length - 1] == "") {?
Log.v(TAG, "非法的文件路徑!");?
return -3;?
}?
// 開辟一個(gè)輸入流?
File inFile = new File(localPath);?
// 判斷需加載的文件是否存在?
if (!inFile.exists()) {?
// 下載遠(yuǎn)程驅(qū)動(dòng)文件?
Log.v(TAG, inFile.getAbsolutePath() + " is not fond!");?
return 1;?
}?
FileInputStream fis = new FileInputStream(inFile);
File dir = context.getDir("libs", Context.MODE_PRIVATE);?
// 獲取驅(qū)動(dòng)文件輸出流?
File soFile = new File(dir, tokens[tokens.length - 1]);?
if (!soFile.exists()) {?
Log.v(TAG, "### " + soFile.getAbsolutePath() + " is not exists");?
FileOutputStream fos = new FileOutputStream(soFile);?
Log.v(TAG, "FileOutputStream:" + fos.toString() + ",tokens:"?
+ tokens[tokens.length - 1]);
// 字節(jié)數(shù)組輸出流,寫入到內(nèi)存中(ram)?
ByteArrayOutputStream baos = new ByteArrayOutputStream();?
byte[] buffer = new byte[1024];?
int len = -1;?
while ((len = fis.read(buffer)) != -1) {?
baos.write(buffer, 0, len);?
}?
// 從內(nèi)存到寫入到具體文件?
fos.write(baos.toByteArray());?
// 關(guān)閉文件流?
baos.close();?
fos.close();?
}?
fis.close();?
Log.v(TAG, "### System.load start");?
// 加載外設(shè)驅(qū)動(dòng)?
System.load(soFile.getAbsolutePath());?
Log.v(TAG, "### System.load End");
return 0;
} catch (Exception e) {?
Log.v(TAG, "Exception?? " + e.getMessage());?
e.printStackTrace();?
return -1;
}
我們?cè)贏ndroid應(yīng)用程序會(huì)常常的加載一些So文件來完成我們的目標(biāo),那么我們的APK加載So是有哪些平時(shí)我們沒有注意到的事情呢?
1. 首先我們一般開發(fā)會(huì)遇見兩種APK(其實(shí)一般大部分只會(huì)遇到一種),一種為系統(tǒng)級(jí)APK,另外一種為普通APK。那么這個(gè)兩種APK跟So加載有什么關(guān)系呢?別急,讓我們先聊聊我們那些操作會(huì)產(chǎn)生這些類型的APK。
普通級(jí)AKP:?
pm install +?包名將會(huì)把APK安裝到 /data/app 目錄下,同時(shí)會(huì)把So映射到/data/app-lib/包命/ 目錄下。這個(gè)就是普通的APK(pm Install -r 會(huì)替換原有的APK,當(dāng)然必須是一樣的簽名)。
系統(tǒng)級(jí)APK:
push? + 絕對(duì)路徑 + 包名 /system/app 目錄下(必須把原有的包名刪除哦!),這時(shí)APK就會(huì)在System/app下面了,這時(shí)你需要把你的APK的So 同時(shí)push到system/lib里面。因?yàn)閍pk里面的So并不會(huì)自動(dòng)映射到system/lib下面。
一般我們?cè)谑褂眉虞dSo的方法時(shí)候,會(huì)使用到System.load(pathName)和?System.loadLibrary(libName)這兩種方法。這篇文章主要講講System.load(pathName)這個(gè)絕對(duì)路徑加載的注意點(diǎn)。
我們通常會(huì)直接使用
context.getApplicationInfo().nativeLibraryDir +/具體名字.so? 來讓系統(tǒng)幫我尋找加載So所需要的路徑。那么這里問題就來了。
如果是系統(tǒng)級(jí)APK
context.getApplicationInfo().nativeLibraryDir = /system/lib/
如果是普通級(jí)APK
context.getApplicationInfo().nativeLibraryDir ?=/data/data-lib/PackageName/ 對(duì)!就是那個(gè)映射的So系統(tǒng)會(huì)根據(jù)這個(gè)去data/app/包名下面尋找真正的So文件。
這個(gè)需要注意的細(xì)節(jié),主要用于在中間件,系統(tǒng)預(yù)置程序的研發(fā)人員與測(cè)試上面。我們?cè)谀玫叫酒瑥S商給予調(diào)試模式的開發(fā)硬件上進(jìn)行Demo和So的更換測(cè)試的時(shí)候,需要自己和測(cè)試都需要知道,自己安裝的APK是什么類型,會(huì)加載什么路徑,以免我們的底層老司機(jī)在幫忙測(cè)試問題的時(shí)候造成不必要的麻煩。
網(wǎng)站題目:androidso加載,android動(dòng)態(tài)加載so文件
文章URL:http://chinadenli.net/article7/dsgdeoj.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供域名注冊(cè)、網(wǎng)站內(nèi)鏈、軟件開發(fā)、關(guān)鍵詞優(yōu)化、云服務(wù)器、面包屑導(dǎo)航
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)