首页 > 编程语言 >android类加载源码分析

android类加载源码分析

时间:2022-10-20 03:55:05浏览次数:78  
标签:dex class field 源码 file android method 加载

Dex文件加载过程

PathClassLoader 和 DexClassLoader都可以加载dex和apk文件,其对应的基类都是BaseDexClassLoader。在new一个PathClassLoader/DexClassLoader对象时就会调用其对应的构造函数,然后调用父类BaseDexClassLoader的构造函数。最终的调用链为:

Method file
BaseDexClassLoader() libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
new DexPathList libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
DexPathList.makeDexElements ...
DexPathList.loadDexFile ...
new DexFile libcore/dalvik/src/main/java/dalvik/system/DexFile.java
DexFile.openDexFile ...
DexFile.openDexFileNative ...

DexFile_openDexFileNative

DexFile.openDexFileNative对应的native函数为DexFile_openDexFileNative,此函数会调用OpenDexFilesFromOat

//art/runtime/native/dalvik_system_DexFile.cc
static jobject DexFile_openDexFileNative(JNIEnv* env,
                                         jclass,
                                         jstring javaSourceName,
                                         jstring javaOutputName ATTRIBUTE_UNUSED,
                                         jint flags ATTRIBUTE_UNUSED,
                                         jobject class_loader,
                                         jobjectArray dex_elements) {
  std::vector<std::string> error_msgs;
  const OatFile* oat_file = nullptr;
  std::vector<std::unique_ptr<const DexFile>> dex_files =
      Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),
                                                                  class_loader,
                                                                  dex_elements,
                                                                  /*out*/ &oat_file,
                                                                  /*out*/ &error_msgs);
}

OatFileManager::OpenDexFilesFromOat_Impl

OpenDexFilesFromOat最终会调用OpenDexFilesFromOat_Impl,OpenDexFilesFromOat_Impl的主要代码如下,最重要的就是调用dex_file_loader.Open去加载dex文件

std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat_Impl(
    std::vector<MemMap>&& dex_mem_maps,
    jobject class_loader,
    jobjectArray dex_elements,
    const OatFile** out_oat_file,
    std::vector<std::string>* error_msgs) {
  
  ...
  std::vector<std::unique_ptr<const DexFile>> dex_files;
  for (size_t i = 0; i < dex_mem_maps.size(); ++i) {
    static constexpr bool kVerifyChecksum = true;
    const ArtDexFileLoader dex_file_loader;

    std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open(
        DexFileLoader::GetMultiDexLocation(i, dex_location.c_str()),
        dex_headers[i]->checksum_,
        std::move(dex_mem_maps[i]),
        /* verify= */ (vdex_file == nullptr) && Runtime::Current()->IsVerificationEnabled(),
        kVerifyChecksum,
        &error_msg));
    if (dex_file != nullptr) {
      dex::tracking::RegisterDexFile(dex_file.get());  // Register for tracking.
      dex_files.push_back(std::move(dex_file));
    } else {
      error_msgs->push_back("Failed to open dex files from memory: " + error_msg);
    }
  }

  ...
  return dex_files;
}

android类加载过程

在一个类加载之前,其类对应的dex文件已经在内存中。

ClassLoader::LoadClass

ClassLoader类是所有类加载器的虚基类,在静态/动态加载一个类时会首先调用此虚基类的LoadClass函数。

//libcore/ojluni/src/main/java/java/lang/ClassLoader.java

     protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
            // First, check if the class has already been loaded
            //查找此类是否已经加载
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                try {
                    if (parent != null) {
                        //父加载器不为空就先让父加载器加载此类
                        c = parent.loadClass(name, false);
                    } else {
                        //父加载器为空说明此加载器为根加载器BootClassLoader,让根加载器去加载
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    //如果父加载器都加载不了就让当前类加载器调用findclass去加载。
                    c = findClass(name);
                }
            }
            return c;
    }

BaseDexClassLoader::findClass

因为BaseDexClassLoader继承了ClassLoader虚类,其他加载器的类基本都继承于BaseDexClassLoader类,BaseDexClassLoader类中重写了findClass方法,所以上一步会去调用BaseDexClassLoader类中的findClass方法。

//libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        ...
        // Check whether the class in question is present in the dexPath that
        // this classloader operates on.
        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
        Class c = pathList.findClass(name, suppressedExceptions);
        if (c != null) {
            return c;
        }
    }

BaseDexClassLoader的findclass会继续调用DexPathList的findClass方法

//libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
    public Class<?> findClass(String name, List<Throwable> suppressed) {
        for (Element element : dexElements) {
            Class<?> clazz = element.findClass(name, definingContext, suppressed);
            if (clazz != null) {
                return clazz;
            }
        }

        if (dexElementsSuppressedExceptions != null) {
            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
        }
        return null;
    }

DexPathList的findClass方法调用Element类的findClass方法。

//libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
    public Class<?> findClass(String name, ClassLoader definingContext,
                List<Throwable> suppressed) {
            return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
                    : null;
        }

defineClassNative

Element类的findClass方法去调用loadClassBinaryName函数,loadClassBinaryName函数接着会调用defineClass->defineClassNative。defineClassNative函数是一个native函数在art虚拟机中实现。

//libcore/dalvik/src/main/java/dalvik/system/DexFile.java
    public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
        return defineClass(name, loader, mCookie, this, suppressed);
    }

    private static Class defineClass(String name, ClassLoader loader, Object cookie,
                                     DexFile dexFile, List<Throwable> suppressed) {
        Class result = null;
        try {
            result = defineClassNative(name, loader, cookie, dexFile);
        } catch (NoClassDefFoundError e) {
            if (suppressed != null) {
                suppressed.add(e);
            }
        } catch (ClassNotFoundException e) {
            if (suppressed != null) {
                suppressed.add(e);
            }
        }
        return result;
    }

defineClassNative对应的art中的native函数为DexFile_defineClassNative,在art中进行类的字段,方法的加载。

DexFile_defineClassNative

  • FindClassDef枚举此类加载器的所有dex文件,并在这些dex文件中寻找Class_def看是否等于当前加载类,如果找不到则返回失败
  • ClassLinker::DefineClass加载对应类的字段和方法
  • 将对应的DexFile添加到类加载器对应的ClassTable中
//art/runtime/native/dalvik_system_DexFile.cc
static jclass DexFile_defineClassNative(JNIEnv* env,
                                        jclass,
                                        jstring javaName,
                                        jobject javaLoader,
                                        jobject cookie,
                                        jobject dexFile) {
  std::vector<const DexFile*> dex_files;
  const std::string descriptor(DotToDescriptor(class_name.c_str()));
  const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));
  //枚举此类加载器的所有dex文件
  for (auto& dex_file : dex_files) {
    //找到dex中的ClassDef
    const dex::ClassDef* dex_class_def =
        OatDexFile::FindClassDef(*dex_file, descriptor.c_str(), hash);
    if (dex_class_def != nullptr) {
      ScopedObjectAccess soa(env);
      ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
      StackHandleScope<1> hs(soa.Self());
      Handle<mirror::ClassLoader> class_loader(
          hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader)));
      ObjPtr<mirror::DexCache> dex_cache =
          class_linker->RegisterDexFile(*dex_file, class_loader.Get());
      if (dex_cache == nullptr) {
        // OOME or InternalError (dexFile already registered with a different class loader).
        soa.Self()->AssertPendingException();
        return nullptr;
      }
      //完成目标类的加载
      ObjPtr<mirror::Class> result = class_linker->DefineClass(soa.Self(),
                                                               descriptor.c_str(),
                                                               hash,
                                                               class_loader,
                                                               *dex_file,
                                                               *dex_class_def);
      //将对应的DexFile添加到类加载器对应的ClassTable中
      // Add the used dex file. This only required for the DexFile.loadClass API since normal
      // class loaders already keep their dex files live.
      class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object>(dexFile),
                                                 class_loader.Get());
}

ClassLinker::DefineClass

DefineClass主要就是调用ClassLinker::LoadClass加载对应类的字段和方法

//art/runtime/class_linker.cc
ObjPtr<mirror::Class> ClassLinker::DefineClass(Thread* self,
                                               const char* descriptor,
                                               size_t hash,
                                               Handle<mirror::ClassLoader> class_loader,
                                               const DexFile& dex_file,
                                               const dex::ClassDef& dex_class_def) {
  //从DexFile dex文件中(内存中的)加载所有的field和method
  // 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, *new_dex_file, *new_class_def, klass);
}

ClassLinker::LoadClass

ClassLinker::LoadClass调用LoadField加载所有的字段,调用LoadMethod加载所有的方法。

//art/runtime/class_linker.cc
void ClassLinker::LoadClass(Thread* self,
                            const DexFile& dex_file,
                            const dex::ClassDef& dex_class_def,
                            Handle<mirror::Class> klass) {

    uint16_t hotness_threshold = runtime->GetJITOptions()->GetWarmupThreshold();
    // Use the visitor since the ranged based loops are bit slower from seeking. Seeking to the
    // methods needs to decode all of the fields.
    accessor.VisitFieldsAndMethods([&](
        //加载所有的静态field
        const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) {
          uint32_t field_idx = field.GetIndex();
          DCHECK_GE(field_idx, last_static_field_idx);  // Ordering enforced by DexFileVerifier.
          if (num_sfields == 0 || LIKELY(field_idx > last_static_field_idx)) {
            LoadField(field, klass, &sfields->At(num_sfields));
            ++num_sfields;
            last_static_field_idx = field_idx;
          }
        }, [&](const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) {
          //加载所有的field
          uint32_t field_idx = field.GetIndex();
          DCHECK_GE(field_idx, last_instance_field_idx);  // Ordering enforced by DexFileVerifier.
          if (num_ifields == 0 || LIKELY(field_idx > last_instance_field_idx)) {
            LoadField(field, klass, &ifields->At(num_ifields));
            ++num_ifields;
            last_instance_field_idx = field_idx;
          }
        }, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) {
          //加载所有的对象方法(direct method)
          ArtMethod* art_method = klass->GetDirectMethodUnchecked(class_def_method_index,
              image_pointer_size_);
          LoadMethod(dex_file, method, klass.Get(), art_method);
          LinkCode(this, art_method, oat_class_ptr, class_def_method_index);
          uint32_t it_method_index = method.GetIndex();
          if (last_dex_method_index == it_method_index) {
            // duplicate case
            art_method->SetMethodIndex(last_class_def_method_index);
          } else {
            art_method->SetMethodIndex(class_def_method_index);
            last_dex_method_index = it_method_index;
            last_class_def_method_index = class_def_method_index;
          }
          art_method->ResetCounter(hotness_threshold);
          ++class_def_method_index;
        }, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) {
          //加载所有的抽象方法(virtual method)
          ArtMethod* art_method = klass->GetVirtualMethodUnchecked(
              class_def_method_index - accessor.NumDirectMethods(),
              image_pointer_size_);
          art_method->ResetCounter(hotness_threshold);
          LoadMethod(dex_file, method, klass.Get(), art_method);
          LinkCode(this, art_method, oat_class_ptr, class_def_method_index);
          ++class_def_method_index;
        });
}

标签:dex,class,field,源码,file,android,method,加载
From: https://www.cnblogs.com/revercc/p/16808386.html

相关文章

  • vue源码解析
           入口   在runtime经过再次扩展  在core下的index再次被扩展   最后是core的instance里是真正的vue构造函数继续扩展vue实......
  • Android开发踩坑日记
    ViewModelProviders被弃用,改为ViewModelProviderViewModelProvider使用方法MyViewModelmodel=newViewModelProvider(this).get(MyViewModel.class);可能报错Can......
  • Linux源码编译——添加新模块
    C源码文件(如new_module.c)添加到适当目录在menuconfig中增加新条目:修改C源码文件所在目录下的Kconfig文件,参考该文件中已有的编译选项照猫画虎,添加新的项configNEW_MOD......
  • weblogic洞若观火第3篇之源码安装weblogic
    引言上一篇文章,主要讲解:集群规划设计、主流的规划方案、企业级的集群规划。在本篇文章中,我们接着介绍:weblogic的源码安装。服务器规划本次搭建使用的虚拟机,相信很多兄弟......
  • GeoServer加载Arcgis切片服务
    使用GeoServer中的GeoWebCache加载Arcgis切片服务下载安装GeoServer和GeoWebCache的下载安装都非常简单,这里选择“独立于平台的二进制版本”,即通常讲的免安装版。选择Geo......
  • #yyds干货盘点#前端图片懒加载
    前端性能优化里有图片的加载,有懒加载和预加载。那么什么是懒加载呢?懒加载也叫做延迟加载、按需加载,指的是在长网页中延迟加载图片数据,是一种较好的网页性能优化的方式。有......
  • Java中Excel的irr函数计算(附源码)
    publicstaticdoubleirr(double[]income){returnirr(income,0.1D);}publicstaticdoubleirr(double[]values,doubleguess){......
  • 视频直播系统源码,简单的移动端轮播图
    视频直播系统源码,简单的移动端轮播图1.页面布局1.1页面框架 <body>  <divclass="box">    <divclass="tupian">      <imgsrc="4.webp"......
  • 每天学习一个Android中的常用框架——0.目录
    文章目录​​1.前言​​​​2.环境​​​​3.目录​​​​3.1持久化​​​​3.1.1Litepal​​​​3.1.2GreenDao​​​​3.1.3Realm​​​​3.1.4DBFlow​​​​3.2网......
  • centos8(linux):通过源码编译安装imagemagick7(ImageMagick 7.1.0-51)
    一,ImageMagick的相关文档:1,官网:https://imagemagick.org/2,下载页https://imagemagick.org/script/download.php#linux如图:说明:刘宏缔的架构森林是一个......