做Android插件框架时,经常会用到dex的动态加载,就要直接或间接的使用DexClassLoader,在new DexClassLoader的时候Android系统做了很多工作,下面我们详细分析一下:

public class DexClassLoader extends BaseDexClassLoader {
    public DexClassLoader(String dexPath, String optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    }
}
public class BaseDexClassLoader extends ClassLoader {
    private final DexPathList pathList;

    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
    }
  ......
@Override
protected URL findResource(String name) { return pathList.findResource(name); } @Override protected Enumeration<URL> findResources(String name) { return pathList.findResources(name); } @Override public String findLibrary(String name) { return pathList.findLibrary(name); }
  ......
}

看到关键步骤了,设置完parent的ClassLoader之后,创建了DexPathList对象pathList,可以看到,很多操作都是直接委托给pathList的,我们看下这个对象里面做了什么。

这里涉及到两个目录,optimizedDirectory和libraryPath,我们先从简单的入手,先看看DexPathList是如何处理libraryPath

final class DexPathList {
    private static final String DEX_SUFFIX = ".dex";/** List of native library directories. */
    private final File[] nativeLibraryDirectories;public DexPathList(ClassLoader definingContext, String dexPath,
            String libraryPath, File optimizedDirectory) {

        ......

        this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
    }

    private static File[] splitLibraryPath(String path) {
        // Native libraries may exist in both the system and
        // application library paths, and we use this search order:
        //
        //   1. this class loader's library path for application libraries
        //   2. the VM's library path from the system property for system libraries
        //
        // This order was reversed prior to Gingerbread; see http://b/2933456.
        ArrayList<File> result = splitPaths(path, System.getProperty("java.library.path"), true);
        return result.toArray(new File[result.size()]);
    }

    private static ArrayList<File> splitPaths(String path1, String path2,
            boolean wantDirectories) {
        ArrayList<File> result = new ArrayList<File>();

        splitAndAdd(path1, wantDirectories, result);
        splitAndAdd(path2, wantDirectories, result);
        return result;
    }

    private static void splitAndAdd(String searchPath, boolean directoriesOnly,
            ArrayList<File> resultList) {
        if (searchPath == null) {
            return;
        }
        for (String path : searchPath.split(":")) {
            try {
                StructStat sb = Libcore.os.stat(path);
                if (!directoriesOnly || S_ISDIR(sb.st_mode)) {
                    resultList.add(new File(path));
                }
            } catch (ErrnoException ignored) {
            }
        }
    }

}

从上面代码可以看到,系统会将用户传进的目录和默认的系统lib目录(System.getProperty("java.library.path"))通过“:”分割后,存进一个File数组中。

这就是DexClassLoader对libraryPath的所有操作,所以可以看到,和optimizePath不一样,并没有对so文件解压。

接下来,我们看看DexClassLoader初始化时候对optimizePath做了什么

 还是从DexPathList入手

final class DexPathList {

    /** class definition context */
    private final ClassLoader definingContext;

    private final Element[] dexElements;

    private final IOException[] dexElementsSuppressedExceptions;

    public DexPathList(ClassLoader definingContext, String dexPath,
            String libraryPath, File optimizedDirectory) {
        ......
        //check optimized directory
        ......

        this.definingContext = definingContext;
        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
                                           suppressedExceptions);
        //save Exceptions
        ......
        this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
    }

    private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory,
                                             ArrayList<IOException> suppressedExceptions) {
        ......

                if (name.endsWith(DEX_SUFFIX)) {
                    // Raw dex file (not inside a zip/jar).
                    try {
                        dex = loadDexFile(file, optimizedDirectory);
                    } catch (IOException ex) {
                        System.logE("Unable to load dex file: " + file, ex);
                    }
                } else {
                    zip = file;
                    try {
                        dex = loadDexFile(file, optimizedDirectory);
                    } catch (IOException suppressed) {
                        suppressedExceptions.add(suppressed);
                    }
                }
           ......

    private static DexFile loadDexFile(File file, File optimizedDirectory)
            throws IOException {
        if (optimizedDirectory == null) {
            return new DexFile(file);
        } else {
            String optimizedPath = optimizedPathFor(file, optimizedDirectory);
            return DexFile.loadDex(file.getPath(), optimizedPath, 0);
        }
    }

}    

除了一些目录检查,异常处理,从构造函数一步一步调用到了loadDexFile,从而进入到DexFile中,我们继续。

public final class DexFile {

    public DexFile(File file) throws IOException {
        this(file.getPath());
    }

    public DexFile(String fileName) throws IOException {
        mCookie = openDexFile(fileName, null, 0);
        mFileName = fileName;
        guard.open("close");
        //System.out.println("DEX FILE cookie is " + mCookie + " fileName=" + fileName);
    }

    private DexFile(String sourceName, String outputName, int flags) throws IOException {
        if (outputName != null) {
            try {
                String parent = new File(outputName).getParent();
                if (Libcore.os.getuid() != Libcore.os.stat(parent).st_uid) {
                    throw new IllegalArgumentException("Optimized data directory " + parent
                            + " is not owned by the current user. Shared storage cannot protect"
                            + " your application from code injection attacks.");
                }
            } catch (ErrnoException ignored) {
                // assume we'll fail with a more contextual error later
            }
        }

        mCookie = openDexFile(sourceName, outputName, flags);
        mFileName = sourceName;
        guard.open("close");
        //System.out.println("DEX FILE cookie is " + mCookie + " sourceName=" + sourceName + " outputName=" + outputName);
    }

    static public DexFile loadDex(String sourcePathName, String outputPathName,
        int flags) throws IOException {
        return new DexFile(sourcePathName, outputPathName, flags);
    }

    private static long openDexFile(String sourceName, String outputName, int flags) throws IOException {
        // Use absolute paths to enable the use of relative paths when testing on host.
        return openDexFileNative(new File(sourceName).getAbsolutePath(),
                                 (outputName == null) ? null : new File(outputName).getAbsolutePath(),
                                 flags);
    }
}

可以看到,最终走到openDexFileNative这个native方法中,这个方法的native实现在art/runtime/native/dalvik_system_DexFile.cc中,看代码:

static jlong DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSourceName, jstring javaOutputName, jint) {
  ......
  bool success = linker->OpenDexFilesFromOat(sourceName.c_str(), outputName.c_str(), &error_msgs,
                                             dex_files.get());
  ......
}

方法OpenDexFilesFromOat的实现在art/runtime/class_linker.cc中,这个方法太长,我先把全部内容贴上来,

  1 bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_location,
  2                                       std::vector<std::string>* error_msgs,
  3                                       std::vector<std::unique_ptr<const DexFile>>* dex_files) {
  4   // 1) Check whether we have an open oat file.
  5   // This requires a dex checksum, use the "primary" one.
  6   uint32_t dex_location_checksum;
  7   uint32_t* dex_location_checksum_pointer = &dex_location_checksum;
  8   bool have_checksum = true;
  9   std::string checksum_error_msg;
 10   if (!DexFile::GetChecksum(dex_location, dex_location_checksum_pointer, &checksum_error_msg)) {
 11     // This happens for pre-opted files since the corresponding dex files are no longer on disk.
 12     dex_location_checksum_pointer = nullptr;
 13     have_checksum = false;
 14   }
 15 
 16   bool needs_registering = false;
 17 
 18   const OatFile::OatDexFile* oat_dex_file = FindOpenedOatDexFile(oat_location, dex_location,
 19                                                                  dex_location_checksum_pointer);
 20   std::unique_ptr<const OatFile> open_oat_file(
 21       oat_dex_file != nullptr ? oat_dex_file->GetOatFile() : nullptr);
 22 
 23   // 2) If we do not have an open one, maybe there's one on disk already.
 24 
 25   // In case the oat file is not open, we play a locking game here so
 26   // that if two different processes race to load and register or generate
 27   // (or worse, one tries to open a partial generated file) we will be okay.
 28   // This is actually common with apps that use DexClassLoader to work
 29   // around the dex method reference limit and that have a background
 30   // service running in a separate process.
 31   ScopedFlock scoped_flock;
 32 
 33   if (open_oat_file.get() == nullptr) {
 34     if (oat_location != nullptr) {
 35       // Can only do this if we have a checksum, else error.
 36       if (!have_checksum) {
 37         error_msgs->push_back(checksum_error_msg);
 38         return false;
 39       }
 40 
 41       std::string error_msg;
 42 
 43       // We are loading or creating one in the future. Time to set up the file lock.
 44       if (!scoped_flock.Init(oat_location, &error_msg)) {
 45         error_msgs->push_back(error_msg);
 46         return false;
 47       }
 48 
 49       // TODO Caller specifically asks for this oat_location. We should honor it. Probably?
 50       open_oat_file.reset(FindOatFileInOatLocationForDexFile(dex_location, dex_location_checksum,
 51                                                              oat_location, &error_msg));
 52 
 53       if (open_oat_file.get() == nullptr) {
 54         std::string compound_msg = StringPrintf("Failed to find dex file '%s' in oat location '%s': %s",
 55                                                 dex_location, oat_location, error_msg.c_str());
 56         VLOG(class_linker) << compound_msg;
 57         error_msgs->push_back(compound_msg);
 58       }
 59     } else {
 60       // TODO: What to lock here?
 61       bool obsolete_file_cleanup_failed;
 62       open_oat_file.reset(FindOatFileContainingDexFileFromDexLocation(dex_location,
 63                                                                       dex_location_checksum_pointer,
 64                                                                       kRuntimeISA, error_msgs,
 65                                                                       &obsolete_file_cleanup_failed));
 66       // There's no point in going forward and eventually try to regenerate the
 67       // file if we couldn't remove the obsolete one. Mostly likely we will fail
 68       // with the same error when trying to write the new file.
 69       // TODO: should we maybe do this only when we get permission issues? (i.e. EACCESS).
 70       if (obsolete_file_cleanup_failed) {
 71         return false;
 72       }
 73     }
 74     needs_registering = true;
 75   }
 76 
 77   // 3) If we have an oat file, check all contained multidex files for our dex_location.
 78   // Note: LoadMultiDexFilesFromOatFile will check for nullptr in the first argument.
 79   bool success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location,
 80                                               dex_location_checksum_pointer,
 81                                               false, error_msgs, dex_files);
 82   if (success) {
 83     const OatFile* oat_file = open_oat_file.release();  // Avoid deleting it.
 84     if (needs_registering) {
 85       // We opened the oat file, so we must register it.
 86       RegisterOatFile(oat_file);
 87     }
 88     // If the file isn't executable we failed patchoat but did manage to get the dex files.
 89     return oat_file->IsExecutable();
 90   } else {
 91     if (needs_registering) {
 92       // We opened it, delete it.
 93       open_oat_file.reset();
 94     } else {
 95       open_oat_file.release();  // Do not delete open oat files.
 96     }
 97   }
 98 
 99   // 4) If it's not the case (either no oat file or mismatches), regenerate and load.
100 
101   // Need a checksum, fail else.
102   if (!have_checksum) {
103     error_msgs->push_back(checksum_error_msg);
104     return false;
105   }
106 
107   // Look in cache location if no oat_location is given.
108   std::string cache_location;
109   if (oat_location == nullptr) {
110     // Use the dalvik cache.
111     const std::string dalvik_cache(GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA)));
112     cache_location = GetDalvikCacheFilenameOrDie(dex_location, dalvik_cache.c_str());
113     oat_location = cache_location.c_str();
114   }
115 
116   bool has_flock = true;
117   // Definitely need to lock now.
118   if (!scoped_flock.HasFile()) {
119     std::string error_msg;
120     if (!scoped_flock.Init(oat_location, &error_msg)) {
121       error_msgs->push_back(error_msg);
122       has_flock = false;
123     }
124   }
125 
126   if (Runtime::Current()->IsDex2OatEnabled() && has_flock && scoped_flock.HasFile()) {
127     // Create the oat file.
128     open_oat_file.reset(CreateOatFileForDexLocation(dex_location, scoped_flock.GetFile()->Fd(),
129                                                     oat_location, error_msgs));
130   }
131 
132   // Failed, bail.
133   if (open_oat_file.get() == nullptr) {
134     std::string error_msg;
135     // dex2oat was disabled or crashed. Add the dex file in the list of dex_files to make progress.
136     DexFile::Open(dex_location, dex_location, &error_msg, dex_files);
137     error_msgs->push_back(error_msg);
138     return false;
139   }
140 
141   // Try to load again, but stronger checks.
142   success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location,
143                                          dex_location_checksum_pointer,
144                                          true, error_msgs, dex_files);
145   if (success) {
146     RegisterOatFile(open_oat_file.release());
147     return true;
148   } else {
149     return false;
150   }
151 }
class_linker.cc的方法ClassLinker::OpenDexFilesFromOat

相关文章:

  • 2021-05-02
  • 2021-08-05
  • 2021-12-23
  • 2021-10-06
  • 2022-01-04
  • 2022-12-23
  • 2021-08-16
猜你喜欢
  • 2021-05-10
  • 2022-01-20
  • 2022-12-23
  • 2022-12-23
  • 2022-01-12
  • 2021-10-02
  • 2021-08-28
相关资源
相似解决方案