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.mzph.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如有可能…