https://blog.csdn.net/jiangwei0910410003/article/details/41384667
前言
关于插件,已经在各大平台上出现过很多,eclipse插件、chrome插件、3dmax插件,所有这些插件大概都为了在一个主程序中实现比较通用的功能,把业务相关或者让可以让用户自定义扩展的功能不附加在主程序中,主程序可在运行时安装和卸载。在android如何实现插件也已经被广泛传播,实现的原理都是实现一套插件接口,把插件实现编成apk或者dex,然后在运行时使用DexClassLoader动态加载进来,不过在这个开发过程中会遇到很多的问题,所以这一片就先不介绍如何开发插件,而是先解决一下开发过程中会遇到的问题,这里主要就是介绍DexClassLoader这个类使用的过程中出现的错误
导读
Java中的类加载器:http://blog.csdn.net/jiangwei0910410003/article/details/17733153
Android中的动态加载机制:http://blog.csdn.net/jiangwei0910410003/article/details/17679823
System.loadLibrary的执行过程:http://blog.csdn.net/jiangwei0910410003/article/details/41490133
一、预备知识
Android中的各种加载器介绍
插件开发的过程中DexClassLoader和PathClassLoader这两个类加载器了是很重要的,但是他们也是有区别的,而且我们也知道PathClassLoader是Android应用中的默认加载器。他们的区别是:
DexClassLoader可以加载任何路径的apk/dex/jar
PathClassLoader只能加载/data/app中的apk,也就是已经安装到手机中的apk。这个也是PathClassLoader作为默认的类加载器的原因,因为一般程序都是安装了,在打开,这时候PathClassLoader就去加载指定的apk(解压成dex,然后在优化成odex)就可以了。
我们可以看一下他们的源码:
DexClassLoader.java
- /*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package dalvik.system;
- import java.io.File;
- import java.io.IOException;
- import java.net.MalformedURLException;
- import java.net.URL;
- import java.util.zip.ZipFile;
- /**
- * Provides a simple {@link ClassLoader} implementation that operates on a
- * list of jar/apk files with classes.dex entries. The directory that
- * holds the optimized form of the files is specified explicitly. This
- * can be used to execute code not installed as part of an application.
- *
- * The best place to put the optimized DEX files is in app-specific
- * storage, so that removal of the app will automatically remove the
- * optimized DEX files. If other storage is used (e.g. /sdcard), the
- * app may not have an opportunity to remove them.
- */
- public class DexClassLoader extends ClassLoader {
- private static final boolean VERBOSE_DEBUG = false;
- /* constructor args, held for init */
- private final String mRawDexPath;
- private final String mRawLibPath;
- private final String mDexOutputPath;
- /*
- * Parallel arrays for jar/apk files.
- *
- * (could stuff these into an object and have a single array;
- * improves clarity but adds overhead)
- */
- private final File[] mFiles; // source file Files, for rsrc URLs
- private final ZipFile[] mZips; // source zip files, with resources
- private final DexFile[] mDexs; // opened, prepped DEX files
- /**
- * Native library path.
- */
- private final String[] mLibPaths;
- /**
- * Creates a {@code DexClassLoader} that finds interpreted and native
- * code. Interpreted classes are found in a set of DEX files contained
- * in Jar or APK files.
- *
- * The path lists are separated using the character specified by
- * the "path.separator" system property, which defaults to ":".
- *
- * @param dexPath
- * the list of jar/apk files containing classes and resources
- * @param dexOutputDir
- * directory where optimized DEX files should be written
- * @param libPath
- * the list of directories containing native libraries; may be null
- * @param parent
- * the parent class loader
- */
- public DexClassLoader(String dexPath, String dexOutputDir, String libPath,
- ClassLoader parent) {
- super(parent);
- ......
dexPath:是加载apk/dex/jar的路径
dexOutDir:是dex的输出路径(因为加载apk/jar的时候会解压除dex文件,这个路径就是保存dex文件的)
libPath:是加载的时候需要用到的lib库,这个一般不用
parent:给DexClassLoader指定父加载器
我们在来看一下PathClassLoader的源码
PathClassLoader.java
- /*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package dalvik.system;
- import java.io.ByteArrayOutputStream;
- import java.io.File;
- import java.io.FileNotFoundException;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.RandomAccessFile;
- import java.net.MalformedURLException;
- import java.net.URL;
- import java.util.ArrayList;
- import java.util.Enumeration;
- import java.util.List;
- import java.util.NoSuchElementException;
- import java.util.zip.ZipEntry;
- import java.util.zip.ZipFile;
- /**
- * Provides a simple {@link ClassLoader} implementation that operates on a list
- * of files and directories in the local file system, but does not attempt to
- * load classes from the network. Android uses this class for its system class
- * loader and for its application class loader(s).
- */
- public class PathClassLoader extends ClassLoader {
- private final String path;
- private final String libPath;
- /*
- * Parallel arrays for jar/apk files.
- *
- * (could stuff these into an object and have a single array;
- * improves clarity but adds overhead)
- */
- private final String[] mPaths;
- private final File[] mFiles;
- private final ZipFile[] mZips;
- private final DexFile[] mDexs;
- /**
- * Native library path.
- */
- private final List<String> libraryPathElements;
- /**
- * Creates a {@code PathClassLoader} that operates on a given list of files
- * and directories. This method is equivalent to calling
- * {@link #PathClassLoader(String, String, ClassLoader)} with a
- * {@code null} value for the second argument (see description there).
- *
- * @param path
- * the list of files and directories
- *
- * @param parent
- * the parent class loader
- */
- public PathClassLoader(String path, ClassLoader parent) {
- this(path, null, parent);
- }
- /**
- * Creates a {@code PathClassLoader} that operates on two given
- * lists of files and directories. The entries of the first list
- * should be one of the following:
- *
- * <ul>
- * <li>Directories containing classes or resources.
- * <li>JAR/ZIP/APK files, possibly containing a "classes.dex" file.
- * <li>"classes.dex" files.
- * </ul>
- *
- * The entries of the second list should be directories containing
- * native library files. Both lists are separated using the
- * character specified by the "path.separator" system property,
- * which, on Android, defaults to ":".
- *
- * @param path
- * the list of files and directories containing classes and
- * resources
- *
- * @param libPath
- * the list of directories containing native libraries
- *
- * @param parent
- * the parent class loader
- */
- public PathClassLoader(String path, String libPath, ClassLoader parent) {
- super(parent);
- ....
/data/dalvik-cache
这个释放解压操作是系统做的。所以PathClassLoader可以不需要这个参数的。
上面看了他们两的区别,下面在来看一下Android中的各种类加载器分别加载哪些类:
- package com.example.androiddemo;
- import android.app.Activity;
- import android.content.Context;
- import android.os.Bundle;
- import android.util.Log;
- import android.widget.ListView;
- public class MainActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- Log.i("DEMO", "Context的类加载加载器:"+Context.class.getClassLoader());
- Log.i("DEMO", "ListView的类加载器:"+ListView.class.getClassLoader());
- Log.i("DEMO", "应用程序默认加载器:"+getClassLoader());
- Log.i("DEMO", "系统类加载器:"+ClassLoader.getSystemClassLoader());
- Log.i("DEMO", "系统类加载器和Context的类加载器是否相等:"+(Context.class.getClassLoader()==ClassLoader.getSystemClassLoader()));
- Log.i("DEMO", "系统类加载器和应用程序默认加载器是否相等:"+(getClassLoader()==ClassLoader.getSystemClassLoader()));
- Log.i("DEMO","打印应用程序默认加载器的委派机制:");
- ClassLoader classLoader = getClassLoader();
- while(classLoader != null){
- Log.i("DEMO", "类加载器:"+classLoader);
- classLoader = classLoader.getParent();
- }
- Log.i("DEMO","打印系统加载器的委派机制:");
- classLoader = ClassLoader.getSystemClassLoader();
- while(classLoader != null){
- Log.i("DEMO", "类加载器:"+classLoader);
- classLoader = classLoader.getParent();
- }
- }
- }
依次来看一下
1) 系统类的加载器
- Log.i("DEMO", "Context的类加载加载器:"+Context.class.getClassLoader());
- Log.i("DEMO", "ListView的类加载器:"+ListView.class.getClassLoader());
看到他也是继承了ClassLoader类。
2) 应用程序的默认加载器
- Log.i("DEMO", "应用程序默认加载器:"+getClassLoader());
默认类加载器是PathClassLoader,同时可以看到加载的apk路径,libPath(一般包括/vendor/lib和/system/lib)
3) 系统类加载器
- Log.i("DEMO", "系统类加载器:"+ClassLoader.getSystemClassLoader());
4) 默认加载器的委派机制关系
- Log.i("DEMO","打印应用程序默认加载器的委派机制:");
- ClassLoader classLoader = getClassLoader();
- while(classLoader != null){
- Log.i("DEMO", "类加载器:"+classLoader);
- classLoader = classLoader.getParent();
- }
默认加载器PathClassLoader的父亲是BootClassLoader
5) 系统加载器的委派机制关系
- Log.i("DEMO","打印系统加载器的委派机制:");
- classLoader = ClassLoader.getSystemClassLoader();
- while(classLoader != null){
- Log.i("DEMO", "类加载器:"+classLoader);
- classLoader = classLoader.getParent();
- }
可以看到系统加载器的父亲也是BootClassLoader