android 開發art,Android應用開發之Android 系統啟動原理(art 虛擬機)

本文將帶你了解Android應用開發之Android 系統啟動原理(art 虛擬機),希望本文對大家學Android有所幫助。

Android ? 系統啟動原理(art 虛擬機)

一、虛擬機的啟動

Android 是一個 Linux 的虛擬機,當虛擬機啟動的時候,會執行手機根目錄下的 init.rc(實際上就是 .sh 文件) 這個可執行文件。

在 init.rc 中,有一行 on init 執行命令。這是調用 init.rc 同級文件 init ,init 是所有安卓手機的入口執行文件,無法打開查看,是亂碼。

xpose 的強大功能,就是對 init 進行 ? hook,然后修改。但是替換 init 這個文件是需要 root 權限的,所以使用 xpose 這個框架,是需要進行 root 的。

1.init 源碼

inti 文件的源碼是在 \system\core\init 這個文件夾下,會把里面所有的東西編譯成 init 這個可執行文件,各個手機廠商會對這塊文件進行修改。

init 的唯一入口是改文件夾下的 init.cpp 這個文件,里面有一個 main 函數,處理環境變量,開啟服務,渲染等。

main 部分代碼:

?1234567891011121314// If we're in the kernel ? domain, re-exec init to transition to the init domain now// that the SELinux ? policy has been loaded.if (is_first_stage) {????if ? (restorecon("/init") == -1) {????????ERROR("restorecon ? failed: %s\n", ? strerror(errno));????????security_failure();????}????char* ? path = argv[0];????char* args[] = { path, ? const_cast("--second-stage"), nullptr ? };????if (execv(path, args) == -1) ? {????????ERROR("execv(\"%s\") ? failed: %s\n", path, strerror(errno));????????security_failure();????}}

代碼中的 path 是指系統定義好的一些環境變量,這些路徑是 ? \frameworks\base\cmds 下的所有東西。

所以在這里是判斷是否是第一次啟動,如果是第一次啟動,則會執行 \frameworks\base\cmds 下所有的可執行文件,包括開啟虛擬機的文件 app_process。

2.app_process 源碼

\frameworks\base\cmds\app_process 下有個 app_main.cpp 文件,里面就是 app_process 源碼。

app_process 部分代碼:

?12345678910if (zygote) ? {????runtime.start("com.android.internal.os.ZygoteInit", ? args, zygote);} else if (className) ? {????runtime.start("com.android.internal.os.RuntimeInit", ? args, zygote);} else {????fprintf(stderr, "Error: no ? class name or --zygote ? supplied.\n");????app_usage();????LOG_ALWAYS_FATAL("app_process: ? no class name or --zygote supplied.");????return ? 10;}

在 app_main 里面的 main 方法最后,調用了 runtime.start(“com.android.internal.os.ZygoteInit”, args, ? zygote);

點擊查看 run 是第一 AppRuntime。

所以 app_process 調用了 ? com.android.internal.os.ZygoteInit 這個類,這是第一個被調用的 java 類。對應源碼位置是 \frameworks\base\core\java\com\android\internal\os

3.AndroidRuntime

AppRuntime 繼承于 AndroidRuntime,AndroidRuntime 位于\frameworks\base\core\jni。

start 部分代碼:

?123if (startVm(&mJavaVM, &env, zygote) ? != 0) {????return;}

在 AndroidRuntime 的 start 方法中,調用了 startVm,這個方法,這個方法才是真正的去開啟虛擬機。手機啟動的時候只是開啟 Linux 系統,當執行到這里的時候,Linux 系統開啟安卓運行的虛擬機。

startVm 部分代碼:

?1234567891011/*?* Initialize the ? VM.?*?* The JavaVM* is essentially per-process, and the JNIEnv* is ? per-thread.?* If this call succeeds, the VM is ready, and we can start ? issuing?* JNI calls.?*/if (JNI_CreateJavaVM(pJavaVM, pEnv, ? &initArgs) < 0) {????ALOGE("JNI_CreateJavaVM ? failed\n");????return -1;}

在 startVm 末尾調用 JNI_CreateJavaVM,去創建一個虛擬機。

4.JNI_CreateJavaVM

JNI_CreateJavaVM 方法位于 \art\runtime\jni_internal.cc 文件中。

JNI_CreateJavaVM :

?1234567891011121314151617181920212223242526272829// ? JNI Invocation interface.?extern "C" jint ? JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) ? {??const JavaVMInitArgs* args = ? static_cast(vm_args);??if ? (IsBadJniVersion(args->version)) {????LOG(ERROR) ? << "Bad JNI version passed to CreateJavaVM: " << ? args->version;????return ? JNI_EVERSION;??}??Runtime::Options ? options;??for (int i = 0; i < args->nOptions; ++i) ? {????JavaVMOption* option = ? &args->options[i];????options.push_back(std::make_pair(std::string(option->optionString), ? option->extraInfo));??}??bool ignore_unrecognized = ? args->ignoreUnrecognized;??if (!Runtime::Create(options, ? ignore_unrecognized)) {????return ? JNI_ERR;??}??Runtime* runtime = Runtime::Current();??bool ? started = runtime->Start();??if (!started) ? {????delete ? Thread::Current()->GetJniEnv();????delete ? runtime->GetJavaVM();????LOG(WARNING) << "CreateJavaVM ? failed";????return ? JNI_ERR;??}??*p_env = ? Thread::Current()->GetJniEnv();??*p_vm = ? runtime->GetJavaVM();??return JNI_OK;}

其中最主要的最后兩行代碼,實例化了 ? p_env 和 p_vm ,p_env 就是我們編寫 jni 方法的第一個參數 JNIEnv *env ,p_vm 就是虛擬機。

//JNIEnv *env 實例化

*p_env = Thread::Current()->GetJniEnv();

//實例化虛擬機的地方

*p_vm = runtime->GetJavaVM();

注:虛擬機在 Linux 就是一個結構體的方式保存著。

5.p_env

GetJniEnv() 這個函數定義在文件 \art\runtime 下的 thread.h 中。

* thread.h *

?1234567// Every thread may have an associated ? JNI environmentJNIEnvExt* jni_env_;?// JNI methodsJNIEnvExt* GetJniEnv() ? const {??return jni_env_;}

JNI 方法的第一個參數是 JNIEnv,JNIEnv ? 是一個接口, JNIEnvExt 是 JNIEnv子類。

二、加載 java 文件

在 ? \frameworks\base\core\jni\AndroidRuntime 中繼續往下,會發現加載 java 類,實際上是調用 env->FindClass(slashClassName) 進行加載的。(java 中 雙親委托機制 ClassLoader 進行加載 java 文件,最底層的實現也是使用 FindClass 這個方法)

1.FindClass

FindClass 是在 libnativehelper\include\nativehelper\jni.h 中,

jni.h 下 FindClass :

jclass FindClass(const char* name){ return ? functions->FindClass(this, name); }

這里的 functions 是 JNINativeInterface,最終調用的是 \art\runtime\jni_internal.cc ? 下的 FindClass 。

\jni_internal.cc 下 FindClass:

static jclass ? FindClass(JNIEnv* env, const char* name) ? {??CHECK_NON_NULL_ARGUMENT(name);??Runtime* runtime = ? Runtime::Current();??ClassLinker* class_linker = ? runtime->GetClassLinker();??std::string ? descriptor(NormalizeJniClassDescriptor(name));??ScopedObjectAccess ? soa(env);??mirror::Class* c = nullptr;??//判斷虛擬機是否開啟??if ? (runtime->IsStarted()) {????StackHandleScope<1>? hs(soa.Self());????Handleclass_loader(hs.NewHandle(GetClassLoader(soa)));????c = ? class_linker->FindClass(soa.Self(), descriptor.c_str(), ? class_loader);??} else {????//還沒開啟虛擬機,即加載的是系統的類????c ? = class_linker->FindSystemClass(soa.Self(), ? descriptor.c_str());??}??return ? soa.AddLocalReference(c);}

最終程序調用到 class_linker 的 FindClass 方法進行加載類。

2. class_linker 的 FindClass

class_linker 所在目錄 \art\runtime 下有一個 class_linker.cc 文件,找到里面的 FindClass 方法。

FindClass 部分代碼:

if (pair.second != nullptr) {??return ? DefineClass(self,?????????????????????descriptor,?????????????????????hash,?????????????????????ScopedNullHandle(),?????????????????????*pair.first,?????????????????????*pair.second);}

在這邊調用了 DefineClass。

DefineClass 部分代碼:

// Add the newly loaded class to the loaded ? classes table.mirror::Class* existing = InsertClass(descriptor, klass.Get(), ? hash);if (existing != nullptr) {??// We failed to insert because we ? raced with another thread. Calling EnsureResolved may cause??// ? this thread to block.??return EnsureResolved(self, descriptor, ? existing);}?// Load the fields and other things after we are inserted in ? the table. This is so that we don't// end up allocating unfree-able linear ? alloc resources and then lose the race condition. The// other reason is that ? the field roots are only visited from the class table. So we need to be// ? inserted before we allocate / fill in these fields.LoadClass(self, dex_file, ? dex_class_def, klass);

這是調用了兩個比較重要的方法, ? InsertClass 和 LoadClass。

InsertClass(descriptor, klass.Get(), hash); 有一個參數是 hash,這樣會把類進行緩存,在 DefineClass 執行 InsertClass 之前,會先進行這個判斷,如果已經加載的就不再進行加載。

LoadClass(self, dex_file, dex_class_def, klass); ? 是真正的去進行加載 Class。

LoadClass:

void ? ClassLinker::LoadClass(Thread* ? self,????????????????????????????const ? DexFile& ? dex_file,????????????????????????????const ? DexFile::ClassDef& dex_class_def,????????????????????????????Handleklass) {??const uint8_t* class_data = ? dex_file.GetClassData(dex_class_def);??if (class_data == nullptr) ? {????return;? // no fields or methods - for example ? a marker interface??}??bool has_oat_class = ? false;??if (Runtime::Current()->IsStarted() && !Runtime::Current()->IsAotCompiler()) ? {????OatFile::OatClass oat_class = FindOatClass(dex_file, ? klass->GetDexClassDefIndex(),???????????????????????????????????????????????&has_oat_class);????if ? (has_oat_class) {??????LoadClassMembers(self, ? dex_file, class_data, klass, ? &oat_class);????}??}??if ? (!has_oat_class) {????LoadClassMembers(self, dex_file, ? class_data, klass, nullptr);??}}

最開始是通過 DexFile 去獲取到 ClassData。因為在類還沒加載的時候,class 是以 dex格式 存在在 磁盤 文件下,這時候需要先把 dex 轉為 ? class,再把 class 加載到內存中。

然后通過 LoadClassMembers ? 進行加載類的信息,分配內存。LoadClassMembers ? 中分別對 ArtField 和 ArtMethod 進行初始化。

本文由職坐標整理并發布,希望對同學們有所幫助。了解更多詳情請關注職坐標移動開發之Android頻道!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/529099.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/529099.shtml
英文地址,請注明出處:http://en.pswp.cn/news/529099.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

電腦文件夾可以分屏的軟件_電腦上什么便簽軟件可以添加音頻?

提及便簽&#xff0c;很多人都會很自然地想到手機便簽。這是因為隨著智能手機和移動互聯網的發展&#xff0c;現在很多手機上都有了系統自帶的便簽app。其實&#xff0c;除了手機便簽外&#xff0c;還有電腦便簽呢&#xff01;這不&#xff0c;Win7及其以上版本的電腦上還有系統…

jsp form提交到后臺中文亂碼_JSP與servlet之間的數據傳遞

【51】Jsp與Servlet之間的傳值有兩種&#xff0c;一種是Jsp傳值給Sevlet&#xff0c;另一種是Servlet傳值給Jsp&#xff1b;使用request、response對象完成傳值&#xff0c;具體實現如下&#xff1a;Jsp與Servlet之間的傳值有兩種&#xff0c;一種是Jsp傳值給Sevlet&#xff0c…

android jni 中jnienv,android JNI中JNIEnv類型和jobject類型的解釋

JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_sayHello (JNIEnv *env, jobject obj){cout<}對於這個方法參數中的JNIEnv* env參數的解釋:JNIEnv類型實際上代表了Java環境&#xff0c;通過這個JNIEnv* 指針&#xff0c;就可以對Java端的代碼進行操作。例如&#xff0c;…

yang模型中rpc_領域驅動模型(DDD)設計講解

一. 什么是領域驅動模型(DDD)&#xff1f;領域驅動模型一種設計思想&#xff0c;我們又稱為DDD設計思想。是一種為了解決傳統設計思想帶來的維護困難&#xff0c;溝通困難和交互困難而產生的一種新的思想。也解決了在部分公司中&#xff0c;一個項目組就是一套服務&#xff0c;…

鴻蒙系統操作界面跟蘋果很像,鴻蒙手機UI界面曝出!圖標擬物化、操作邏輯近似蘋果iOS13...

原標題&#xff1a;鴻蒙手機UI界面曝出&#xff01;圖標擬物化、操作邏輯近似蘋果iOS13?【IT爆料王-原創文章-具備版權效力】就在近日&#xff0c;筆者收到了網友的匿名私信&#xff0c;提供給筆者華為鴻蒙系統的UI界面截圖&#xff0c;以及搭載鴻蒙系統的華為手機的曝光圖片。…

python3中的int類型占64位,有沒有什么辦法來強制Python來使用64位整數的Windows?

I’ve noticed that whenever any integer surpasses 2^31-1 my number heavy code suffers a large slowdown, despite the fact I’m using a 64 bit build of Python on a 64bit version of Windows. This seems to be true on Python 2.7 and Python 3. I’ve read that Wi…

crtsiii型無砟軌道板_無砟軌道裂縫破損怎么修補

隨著高速鐵路、客運專線、城市地鐵的快速發展&#xff0c;無砟軌道軌道板&#xff08;道床板&#xff09;廣泛應用&#xff0c;但施工中和運營期都發現軌道板混凝土存在不同程度的微細裂縫&#xff0c;對無砟軌道造成了一定的病害。高鐵軌道板裂縫是不可避免的。為確保無砟軌道…

c調用python第三方庫_Python使用ctypes模塊調用DLL函數之C語言數組與numpy數組傳遞...

在Python語言中&#xff0c;可以使用ctypes模塊調用其它如C語言編寫的動態鏈接庫DLL文件中的函數&#xff0c;在提高軟件運行效率的同時&#xff0c;也可以充分利用目前市面上各種第三方的DLL庫函數&#xff0c;以擴充Python軟件的功能及應用領域&#xff0c;減少重復編寫代碼、…

妲己機器人怎么升級固件_臺灣重金設計的3D妲己,亮瞎了

大家還記得前幾天米醋分享的國內首檔二次元選秀&#xff0c;遭網友瘋狂吐槽&#xff1a;不知道怎么形容的丑&#xff01;當米醋看到了這檔綜藝的宣傳海報時瞬間被這一批選手的顏值所吸引&#xff01;太魔幻了&#xff01;沒成想看到3D人物效果時米醋卻被這盛世丑顏丑到裂開&…

go語言通道插入0_Go語言入門必知教程-通道

Golang提供了一種稱為通道的機制&#xff0c;用于在協程之間共享數據。當函數作為協程執行并發活動時&#xff0c;需要它們共享資源或數據&#xff0c;通道便充當協程之間的管道(管道)&#xff0c;提供一種確保同步交換數據的機制。需要在聲明通道時指定數據類型&#xff0c;可…

aes加密字符串c++_springboot2.2.X手冊:防抓包?快速實現API接口數據加密

溪云閣&#xff1a;專注編程教學&#xff0c;架構&#xff0c;JAVA&#xff0c;Python&#xff0c;微服務&#xff0c;機器學習等&#xff0c;歡迎關注上一篇&#xff1a;springboot2.2.X手冊&#xff1a;redis的7種類型100個方法全解析有沒有遇到這樣子的接口&#xff0c;放到…

鴻蒙系統打造完備終端,搭載鴻蒙系統的手機很快推出,華為生態更加完善

2019年的8月9日&#xff0c;在華為開發者大會上華為向大家正式的發布了一款操作系統——鴻蒙系統。這個系統備受大家的關注&#xff0c;鴻蒙2.0的發布也在時刻期待中。因為在目前的操作系統中&#xff0c;華為的鴻蒙操作系統是僅次于安卓、ios的存在&#xff0c;而今日&#xf…

curl socket 訪問_使用Curl、socket、file_get_contents三種方法POST提交數據 | 學步園

# <?php # /**# * Socket版本# * 使用方法&#xff1a;# * $post_string "appsocket&versionbeta";# * request_by_socket(facebook.cn,/restServer.php,$post_string);# */# function request_by_socket($remote_server,$remote_path,$post_string,$port …

html 標簽 r語言,從R中的字符串中刪除html標簽

我正在嘗試將網頁源代碼讀入R并將其作為字符串處理。我正在嘗試刪除段落并從段落文本中刪除html標簽。我遇到了以下問題&#xff1a;我嘗試實現一個功能來刪除html標簽&#xff1a;cleanFunfunction(fullStr){#find location of tags and citationstagLoccbind(str_locate_all(…

python給圖片加半透明水印_Python 批量加水印就這么簡單!

工作的時候&#xff0c;尤其是自媒體&#xff0c;我們必備水印添加工具以保護我們的知識產權,網上有許多的在線/下載的水印添加工具&#xff0c;但他們或多或少都存在以下問題&#xff1a; 在線工具需要將圖片上傳到對方服務器&#xff0c;信息不安全。 很多工具不具備批量處理…

html 選中狀態,html默認選中狀態

html中標簽用法解析及如何設置selec標簽定義和用法 select 元素可創建單選或多選菜單。當提交表單時&#xff0c;瀏覽器會提交選定的項目&#xff0c;或者收集用逗號分隔的多個選項&#xff0c;將其合成一個單獨的參數列表&#xff0c;并且在將 表單數據提交給服務器時包括 nam…

nemesis什么車_nemesis是什么意思_nemesis的翻譯_音標_讀音_用法_例句_愛詞霸在線詞典...

全部報應Was he aiming at Bryant, his old nemesis and a favorite target in the past?他是不是暗指科比, 一直的“競爭對手”和過去最中意的目標?期刊摘選After the defeat of their old arch nemesis, the Turtle have grown apart as a family.在擊敗舊時強敵后, 忍者神…

wxpython制作表格界面_[Python] wxPython 菜單欄控件學習總結(原創)

1、總結 1、大體創建過程 1、創建一個 菜單欄 : menuBar wx.MenuBar()相當于這個白色地方&#xff0c;沒有File這個菜單 2、創建 菜單 : fileMenu wx.Menu()這兩個不是直接“用的”&#xff0c;叫菜單。既用來分類其他 菜單項 的文件夾樣 3、創建 菜單項 : newItem wx.MenuI…

android 8三星note8,信息太多很煩瑣?告訴你三星Note8有妙招

不知從何時開始&#xff0c;我們眼前的信息變得豐富而繁雜。簡潔的新聞無需經過報紙過濾&#xff0c;發生數分鐘已經城皆知。預測晴雨也無需依靠天氣先生&#xff0c;點亮手機即可洞悉風雨。生活在信息時代的我們僅用幾英寸的窗口觀察世界&#xff0c;信息的洪流難免會遮蔽眼前…

realloc函_realloc(重新分配內存空間的函數)

在頭文件中定義void * realloc(void * ptr&#xff0c;size_t new_size);重新分配給定的內存區域。它必須預先分配malloc()&#xff0c;calloc()或realloc()尚未釋放與free或呼叫realloc。否則&#xff0c;結果是不確定的。重新分配由以下兩者之一完成&#xff1a;a)ptr如有可能…