本文将带你了解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频道!

android 开发art,Android应用开发之Android 系统启动原理(art 虚拟机)相关推荐

  1. android ndk 多线程mk,NDK开发之Android.mk文件编写

    8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? 现在我们把android稍微写复杂些.在项目根目录下创建一个lib1文件夹 结构如图: test10.h和test11. ...

  2. android vr播放器 开发,Android应用开发之Android VR Player(全景视频播放器)- ExoPlayer播放器MPEG-DASH视频播放...

    本文将带你了解Android应用开发之Android VR Player(全景视频播放器)- ExoPlayer播放器MPEG-DASH视频播放,希望本文对大家学Android有所帮助. Androi ...

  3. android studio开发工具介绍,Android应用开发之Android开发工具介绍、Android Studio配置...

    本文将带你了解Android应用开发之Android开发工具介绍.Android Studio配置,希望本文对大家学Android有所帮助. 2.1   Android Studio配置 2.1.1 ...

  4. android签到功能开发,Android应用开发之Android简单实现app每月签到功能

    本文将带你了解Android应用开发Android简单实现app每月签到功能,希望本文对大家学Android有所帮助. 本文实例为大家分享了Android实现app每月签到功能的具体代码,供大家参考, ...

  5. android离线语音开发,Android应用开发之Android 云之声离线语音合成

    本文将带你了解Android应用开发之Android 云之声离线语音合成,希望本文对大家学Android有所帮助. 离线语音解析 public class SpeechUtilOffline impl ...

  6. android添加截图功能,Android应用开发之Android 5.0及以上编程实现屏幕截图功能的方法...

    本文将带你了解Android应用开发Android 5.0及以上编程实现屏幕截图功能的方法,希望本文对大家学Android有所帮助. 本文实例讲述了Android   5.0及以上编程实现屏幕截图功能 ...

  7. java edittext 输入监听_Android应用开发之Android EditText 监听用户输入完成的实例

    本文将带你了解Android应用开发Android EditText 监听用户输入完成的实例,希望本文对大家学Android有所帮助. 我们都知道, Android   EditText输入框,并没有 ...

  8. android webview 太大,Android应用开发之Android WebView加载图片显示过大的处理教程(代码教程)...

    本文将带你了解Android应用开发Android  WebView加载图片显示过大的处理教程(代码教程),希望本文对大家学Android有所帮助. Webview加载图片时,经常会遇到图片显示不符合 ...

  9. android webview权限申请_Android应用开发之android 6.0下webview的定位权限设置方法

    本文将带你了解Android应用开发android 6.0下webview的定位权限设置方法,希望本文对大家学Android有所帮助. 如下所示: WebView webView =   (WebVi ...

最新文章

  1. PFLD:简单、快速、超高精度人脸特征点检测算法
  2. 人工智能及其应用(第5版).蔡自兴-3章课后习题。【参考答案】
  3. 口腔取模过程及注意事项_数字化口 腔的发展与展望
  4. python如何播放视频_如何用python做一个视频搜索+播放器
  5. obs多推流地址_抖音obs推流直播怎么操作,抖音直播推流地址如何获取?
  6. centos7重启桌面服务_CENTOS7安装桌面系统
  7. pdf 中的java运行,java - 从pdf文件读取特定位置的itext在intellij中运行,并提供所需的输出,但是可执行jar抛出错误 - 堆栈内存溢出...
  8. mysql gzip_在mysql中存储GZIP:ed文本?
  9. django-新的django项目
  10. 数据库中字段随机添加汉字
  11. ML 12 13 mixture of gaussions and EM
  12. Yann LeCun提出首个多模态高性能自监督算法,语音、图像文本全部SOTA
  13. one hot encoding
  14. Windows Phone笔记(5)加速计和位置服务(转)
  15. iOS - OC NSFileManager 文件管理
  16. Rocket - tilelink - AtomicAutomata
  17. Tomcat 7.X安装教程(简单易懂)
  18. switchhost提示没有切换权限
  19. SatSun CRMS V2010 整站系统
  20. pushplus通过企业微信应用给微信发送消息教程

热门文章

  1. 蓝色巨人IBM全力奔赴的混合云之旅能顺利吗?
  2. Spring精华问答 | Spring框架有哪些主要模块?
  3. 云存储精华问答 | 如何选择混合云提供商?
  4. 自己虚拟服务器都用json可以吗,vue+webpack项目中使用dev-server搭建虚拟服务器,请求json文件数据,实现前后台分离开发...
  5. deb 中标麒麟_「图」百度网盘Linux版放出deb包客户端:新增支持Ubuntu 18.04 LTS
  6. 未来教育计算机书,未来教育.全国计算机等级考试
  7. js提交java后台,双引号转义为amp;quot;解决办法……StringEscapeUtils.unescapeHtml4完美解决
  8. 实战06_SSM整合ActiveMQ支持多种类型消息
  9. 实战01_SSM整合ActiveMQ支持多种类型消息
  10. 利用python做一个小游戏_如何使用python做一个简单的猜数字的小游戏