ClassLoader的源码学习路径:
ClassLoader源码学习-- 学习源码的方法
ClassLoader源码学习 -- JVM启动之 Launcher,ClassLoader构建
ClassLoader源码学习-- ClassLoader的创建 -- Android Pie
ClassLoader源码学习 -- PathClassLoader,DexClassLoader
发布文章前,要尊重别人的劳动成功,感谢这篇文章给了我学习的思路。
https://www.jianshu.com/p/23031578e654
大家可能都清楚,几个ClassLoader怎么用,翻到ClassLoader.java, 直接看到static函数createSystemClassLoader, 那么系统究竟是怎么调用这个函数的?
在这里,我深入源码,把整个过程链了起来,先给大家一个现成答案,下面再慢慢追踪:
1.app_main.cpp中main函数, 构造了AndroidRuntime, 并根据zygote是否有init的情况,给runtime.start时不同的className,ZygoteInit或RuntimeInit
2.AndroidRuntime中start函数, startSystemServer,并设置ANDROID_ROOT,最后初始化JniInvocation,动态链接到libart.so,创建JNI_CreateJavaVM_函数的用指针。
3.AndroidRuntime中startVm函数,初始化大量启动VM的参数与环境参数,最后调用刚刚创建的JNI_CreateJavaVM函数,链接到ClassLoader.java中静态函数,createSystemClassLoader
4.createSystemClassLoader中,同过System.getProperties,获取classPath, librarySearchPath,直接创建PathClassLoader。
文件清单:
/frameworks/base/cmds/app_process/app_main.cpp
/frameworks/base/core/jni/AndroidRuntime.cpp
/libnativehelper/JniInvocation.cpp
/art/runtime/runtime.cc
/libcore/ojluni/src/main/java/java/lang/ClassLoader.java
基本原理
学习Android类加载之前,顾名思义ClassLoader是加载.class文件。
当然Android于java有所用不同,编译生产.class文件这里查了点资料:
第一步:打包资源文件,生成R.java文件
第二步:处理AIDL文件,生成对应的.java文件(当然,有很多工程没有用到AIDL,那这个过程就可以省了)
第三步:编译Java文件,生成对应的.class文件
第四步:把.class文件转化成Davik VM支持的.dex文件
第五步:打包生成未签名的.apk文件
第六步:对未签名.apk文件进行签名
第七步:对签名后的.apk文件进行对齐处理(不进行对齐处理是不能发布到Google Market的)
逃课党,借鉴别人的图:
Android跟Java不一样的地方,除了Jvm与Dvm的区别,Android一个进程运行在一个Dvm实例上。每个进程从Zygote中fork克隆出新实例。
具体步骤
1.从app_main的main函数入手,记下调用链上重要的类和方法,以及文件清单
2.这次学习主要是根据sdk 28, Android 9.0 pie,下载相关的源码。https://www.androidos.net.cn/sourcecode
第一站app_main.cpp
作为应用启动的入口,通过main函数,开启旅程
int main(int argc, char* const argv[])
{
//省略很多代码
//初始化 AppRuntime, AppRuntime也就是继承AndroidRuntime
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
//省略很多代码, 反正就是构建些runtime启动的参数
if (zygote) {
//操作系统中,zygote没有启动过,需要初始化ZygoteInit
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
//操作系统中,zygote已经初始化了,直接fork zygote,启动初始化Runtime
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.");
}
}
第二站AndroidRuntime.cpp
刚刚app_main中,对runtime做了构造函数,已经start,现在我们聚焦这两个函数
//Runtime 构造函数
AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) :
mExitWithoutCleanup(false),
mArgBlockStart(argBlockStart),
mArgBlockLength(argBlockLength)
{
//图形渲染引擎初始化
SkGraphics::Init();
// Pre-allocate enough space to hold a fair number of options.
mOptions.setCapacity(20);
assert(gCurRuntime == NULL); // one per process
gCurRuntime = this;
}
//Runtime 析构函数
AndroidRuntime::~AndroidRuntime()
{
}
//初始化
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
ALOGD(">>>>>> START %s uid %d <<<<<<\n",
className != NULL ? className : "(unknown)", getuid());
static const String8 startSystemServer("start-system-server");
/*
* 'startSystemServer == true' means runtime is obsolete and not run from
* init.rc anymore, so we print out the boot start event here.
*/
for (size_t i = 0; i < options.size(); ++i) {
//app_main中,参数传入,需要启动SystemServer
if (options[i] == startSystemServer) {
/* track our progress through the boot sequence */
const int LOG_BOOT_PROGRESS_START = 3000;
LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
}
}
const char* rootDir = getenv("ANDROID_ROOT");
if (rootDir == NULL) {
rootDir = "/system";
if (!hasDir("/system")) {
LOG_FATAL("No root directory specified, and /android does not exist.");
return;
}
setenv("ANDROID_ROOT", rootDir, 1);
}
//const char* kernelHack = getenv("LD_ASSUME_KERNEL");
//ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
onVmCreated(env);
/*
* Register android functions.
*/
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
/*
* We want to call main() with a String array with arguments in it.
* At present we have two arguments, the class name and an option string.
* Create an array to hold them.
*/
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
for (size_t i = 0; i < options.size(); ++i) {
jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
assert(optionsStr != NULL);
env->SetObjectArrayElement(strArray, i + 1, optionsStr);
}
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
char* slashClassName = toSlashClassName(className != NULL ? className : "");
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
free(slashClassName);
ALOGD("Shutting down VM\n");
if (mJavaVM->DetachCurrentThread() != JNI_OK)
ALOGW("Warning: unable to detach main thread\n");
if (mJavaVM->DestroyJavaVM() != 0)
ALOGW("Warning: VM did not shut down cleanly\n");
}
可见,启动的时候,做了4件事
1、startSystemServer
2、设置Android_ROOT,并设置好VM的基本环境参数
3、startVm
4、寻找开屏页SplashClassName, 并使用调用main方法,调用完释放。
第三站,startVm
//启动VM
/*
* Start the Dalvik Virtual Machine.
*
* Various arguments, most determined by system properties, are passed in.
* The "mOptions" vector is updated.
*
* CAUTION: when adding options in here, be careful not to put the
* char buffer inside a nested scope. Adding the buffer to the
* options using mOptions.add() does not copy the buffer, so if the
* buffer goes out of scope the option may be overwritten. It's best
* to put the buffer at the top of the function so that it is more
* unlikely that someone will surround it in a scope at a later time
* and thus introduce a bug.
*
* Returns 0 on success.
*/
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{
... ... ... ... ... ...
//此处省略90%的代码,基本上都在解析参数
/*
* 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;
}
return 0;
}
startVm主要做了两件事:
1.解析启动vm的大部分参数
2.调用JNI_CreateJavaVM创建虚拟机
第四站 JNI_CreateJavaVM
在AndroidRuntime.cpp中,并没有找到这个方法
事实上在start()函数里,还有
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
所以我们去分析JniInvocation
static const char* kLibraryFallback = "libart.so";
bool JniInvocation::Init(const char* library) {
...
if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),
"JNI_CreateJavaVM")) {
return false;
}
...
}
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
return JniInvocation::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args);
}
jint JniInvocation::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
return JNI_CreateJavaVM_(p_vm, p_env, vm_args);
}
bool JniInvocation::FindSymbol(void** pointer, const char* symbol) {
*pointer = dlsym(handle_, symbol);
if (*pointer == NULL) {
ALOGE("Failed to find symbol %s: %s\n", symbol, dlerror());
dlclose(handle_);
handle_ = NULL;
return false;
}
return true;
}
dlsym是一个计算机函数,功能是根据动态链接库操作句柄与符号,返回符号对应的地址,不但可以获取函数地址,也可以获取变量地址。
dlopen以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程
这里使用两个关键函数,通过句柄动态链接到库libart.so中,也就是说,CreateJavaVM的任务,交给了libart.so中。
下一站 libart.so
也许art和dalvik的区别,核心就在libart.so和libdvm.so吧
libart.so的代码都位于/art目录下,而JNI_CreateJavaVM定义在/art/runtime/java_vm_ext.cc中:
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
ScopedTrace trace(__FUNCTION__);
const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);
if (JavaVMExt::IsBadJniVersion(args->version)) {
LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;
return JNI_EVERSION;
}
RuntimeOptions 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;
}
// Initialize native loader. This step makes sure we have
// everything set up before we start using JNI.
android::InitializeNativeLoader();
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;
}
这里核心在于 通过runtime->Start()以后,创建的职责交给了runtime
继续一部,runtime.cc
bool Runtime::Start() {
VLOG(startup) << "Runtime::Start entering";
CHECK(!no_sig_chain_) << "A started runtime should have sig chain enabled";
// If a debug host build, disable ptrace restriction for debugging and test timeout thread dump.
// Only 64-bit as prctl() may fail in 32 bit userspace on a 64-bit kernel.
#if defined(__linux__) && !defined(ART_TARGET_ANDROID) && defined(__x86_64__)
if (kIsDebugBuild) {
CHECK_EQ(prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY), 0);
}
#endif
// Restore main thread state to kNative as expected by native code.
Thread* self = Thread::Current();
self->TransitionFromRunnableToSuspended(kNative);
started_ = true;
if (!IsImageDex2OatEnabled() || !GetHeap()->HasBootImageSpace()) {
ScopedObjectAccess soa(self);
StackHandleScope<2> hs(soa.Self());
auto class_class(hs.NewHandle<mirror::Class>(mirror::Class::GetJavaLangClass()));
auto field_class(hs.NewHandle<mirror::Class>(mirror::Field::StaticClass()));
class_linker_->EnsureInitialized(soa.Self(), class_class, true, true);
// Field class is needed for register_java_net_InetAddress in libcore, b/28153851.
class_linker_->EnsureInitialized(soa.Self(), field_class, true, true);
}
// InitNativeMethods needs to be after started_ so that the classes
// it touches will have methods linked to the oat file if necessary.
{
ScopedTrace trace2("InitNativeMethods");
InitNativeMethods();
}
// IntializeIntrinsics needs to be called after the WellKnownClasses::Init in InitNativeMethods
// because in checking the invocation types of intrinsic methods ArtMethod::GetInvokeType()
// needs the SignaturePolymorphic annotation class which is initialized in WellKnownClasses::Init.
InitializeIntrinsics();
// Initialize well known thread group values that may be accessed threads while attaching.
InitThreadGroups(self);
Thread::FinishStartup();
// Create the JIT either if we have to use JIT compilation or save profiling info. This is
// done after FinishStartup as the JIT pool needs Java thread peers, which require the main
// ThreadGroup to exist.
//
// TODO(calin): We use the JIT class as a proxy for JIT compilation and for
// recoding profiles. Maybe we should consider changing the name to be more clear it's
// not only about compiling. b/28295073.
if (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) {
std::string error_msg;
if (!IsZygote()) {
// If we are the zygote then we need to wait until after forking to create the code cache
// due to SELinux restrictions on r/w/x memory regions.
CreateJit();
} else if (jit_options_->UseJitCompilation()) {
if (!jit::Jit::LoadCompilerLibrary(&error_msg)) {
// Try to load compiler pre zygote to reduce PSS. b/27744947
LOG(WARNING) << "Failed to load JIT compiler with error " << error_msg;
}
}
}
// Send the start phase event. We have to wait till here as this is when the main thread peer
// has just been generated, important root clinits have been run and JNI is completely functional.
{
ScopedObjectAccess soa(self);
callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kStart);
}
system_class_loader_ = CreateSystemClassLoader(this);
if (!is_zygote_) {
if (is_native_bridge_loaded_) {
PreInitializeNativeBridge(".");
}
NativeBridgeAction action = force_native_bridge_
? NativeBridgeAction::kInitialize
: NativeBridgeAction::kUnload;
InitNonZygoteOrPostFork(self->GetJniEnv(),
/* is_system_server */ false,
action,
GetInstructionSetString(kRuntimeISA));
}
// Send the initialized phase event. Send it before starting daemons, as otherwise
// sending thread events becomes complicated.
{
ScopedObjectAccess soa(self);
callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInit);
}
StartDaemonThreads();
{
ScopedObjectAccess soa(self);
self->GetJniEnv()->AssertLocalsEmpty();
}
VLOG(startup) << "Runtime::Start exiting";
finished_starting_ = true;
if (trace_config_.get() != nullptr && trace_config_->trace_file != "") {
ScopedThreadStateChange tsc(self, kWaitingForMethodTracingStart);
Trace::Start(trace_config_->trace_file.c_str(),
-1,
static_cast<int>(trace_config_->trace_file_size),
0,
trace_config_->trace_output_mode,
trace_config_->trace_mode,
0);
}
// In case we have a profile path passed as a command line argument,
// register the current class path for profiling now. Note that we cannot do
// this before we create the JIT and having it here is the most convenient way.
// This is used when testing profiles with dalvikvm command as there is no
// framework to register the dex files for profiling.
if (jit_.get() != nullptr && jit_options_->GetSaveProfilingInfo() &&
!jit_options_->GetProfileSaverOptions().GetProfilePath().empty()) {
std::vector<std::string> dex_filenames;
Split(class_path_string_, ':', &dex_filenames);
RegisterAppInfo(dex_filenames, jit_options_->GetProfileSaverOptions().GetProfilePath());
}
return true;
}
在函数中,启动art vm的核心源码终于找到了
包括jit创建,deamonThread, 还有我们的关注点,CreateSystemClassLoader
static jobject CreateSystemClassLoader(Runtime* runtime) {
if (runtime->IsAotCompiler() && !runtime->GetCompilerCallbacks()->IsBootImage()) {
return nullptr;
}
ScopedObjectAccess soa(Thread::Current());
ClassLinker* cl = Runtime::Current()->GetClassLinker();
auto pointer_size = cl->GetImagePointerSize();
StackHandleScope<2> hs(soa.Self());
Handle<mirror::Class> class_loader_class(
hs.NewHandle(soa.Decode<mirror::Class>(WellKnownClasses::java_lang_ClassLoader)));
CHECK(cl->EnsureInitialized(soa.Self(), class_loader_class, true, true));
ArtMethod* getSystemClassLoader = class_loader_class->FindClassMethod(
"getSystemClassLoader", "()Ljava/lang/ClassLoader;", pointer_size);
CHECK(getSystemClassLoader != nullptr);
CHECK(getSystemClassLoader->IsStatic());
JValue result = InvokeWithJValues(soa,
nullptr,
jni::EncodeArtMethod(getSystemClassLoader),
nullptr);
JNIEnv* env = soa.Self()->GetJniEnv();
ScopedLocalRef<jobject> system_class_loader(env, soa.AddLocalReference<jobject>(result.GetL()));
CHECK(system_class_loader.get() != nullptr);
soa.Self()->SetClassLoaderOverride(system_class_loader.get());
Handle<mirror::Class> thread_class(
hs.NewHandle(soa.Decode<mirror::Class>(WellKnownClasses::java_lang_Thread)));
CHECK(cl->EnsureInitialized(soa.Self(), thread_class, true, true));
ArtField* contextClassLoader =
thread_class->FindDeclaredInstanceField("contextClassLoader", "Ljava/lang/ClassLoader;");
CHECK(contextClassLoader != nullptr);
// We can't run in a transaction yet.
contextClassLoader->SetObject<false>(
soa.Self()->GetPeer(),
soa.Decode<mirror::ClassLoader>(system_class_loader.get()).Ptr());
return env->NewGlobalRef(system_class_loader.get());
}
现在终于找到了
ArtMethod* getSystemClassLoader = class_loader_class->FindClassMethod(
"getSystemClassLoader", "()Ljava/lang/ClassLoader;", pointer_size);
CHECK(getSystemClassLoader != nullptr);
CHECK(getSystemClassLoader->IsStatic());
调用Java的类 java.lang.ClassLoader中的静态方法getSystemClassLoader,
最后一站, 终于回到我们熟悉的java代码, java.lang.ClassLoader.java
public abstract class ClassLoader {
static private class SystemClassLoader {
public static ClassLoader loader = ClassLoader.createSystemClassLoader();
}
}
private static ClassLoader createSystemClassLoader() {
String classPath = System.getProperty("java.class.path", ".");
String librarySearchPath = System.getProperty("java.library.path", "");
// String[] paths = classPath.split(":");
// URL[] urls = new URL[paths.length];
// for (int i = 0; i < paths.length; i++) {
// try {
// urls[i] = new URL("file://" + paths[i]);
// }
// catch (Exception ex) {
// ex.printStackTrace();
// }
// }
//
// return new java.net.URLClassLoader(urls, null);
// TODO Make this a java.net.URLClassLoader once we have those?
return new PathClassLoader(classPath, librarySearchPath, BootClassLoader.getInstance());
}
原来是,直接new了一个PathClassLoader, 其中classPath的路径,还有librarySearchPath的路径,可以直接打印出来。
这一节学习到此为止,下一节在继续学习应用层,BaseDexClassLoader, PathClassLoader, ClassLoader