Android中插件开发篇之----类加载器

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

[java]  view plain  copy
  1. /* 
  2.  * Copyright (C) 2008 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package dalvik.system;  
  18.   
  19. import java.io.File;  
  20. import java.io.IOException;  
  21. import java.net.MalformedURLException;  
  22. import java.net.URL;  
  23. import java.util.zip.ZipFile;  
  24.   
  25. /** 
  26.  * Provides a simple {@link ClassLoader} implementation that operates on a 
  27.  * list of jar/apk files with classes.dex entries.  The directory that 
  28.  * holds the optimized form of the files is specified explicitly.  This 
  29.  * can be used to execute code not installed as part of an application. 
  30.  * 
  31.  * The best place to put the optimized DEX files is in app-specific 
  32.  * storage, so that removal of the app will automatically remove the 
  33.  * optimized DEX files.  If other storage is used (e.g. /sdcard), the 
  34.  * app may not have an opportunity to remove them. 
  35.  */  
  36. public class DexClassLoader extends ClassLoader {  
  37.   
  38.     private static final boolean VERBOSE_DEBUG = false;  
  39.   
  40.     /* constructor args, held for init */  
  41.     private final String mRawDexPath;  
  42.     private final String mRawLibPath;  
  43.     private final String mDexOutputPath;  
  44.   
  45.     /* 
  46.      * Parallel arrays for jar/apk files. 
  47.      * 
  48.      * (could stuff these into an object and have a single array; 
  49.      * improves clarity but adds overhead) 
  50.      */  
  51.     private final File[] mFiles;         // source file Files, for rsrc URLs  
  52.     private final ZipFile[] mZips;       // source zip files, with resources  
  53.     private final DexFile[] mDexs;       // opened, prepped DEX files  
  54.   
  55.     /** 
  56.      * Native library path. 
  57.      */  
  58.     private final String[] mLibPaths;  
  59.   
  60.     /** 
  61.      * Creates a {@code DexClassLoader} that finds interpreted and native 
  62.      * code.  Interpreted classes are found in a set of DEX files contained 
  63.      * in Jar or APK files. 
  64.      * 
  65.      * The path lists are separated using the character specified by 
  66.      * the "path.separator" system property, which defaults to ":". 
  67.      * 
  68.      * @param dexPath 
  69.      *  the list of jar/apk files containing classes and resources 
  70.      * @param dexOutputDir 
  71.      *  directory where optimized DEX files should be written 
  72.      * @param libPath 
  73.      *  the list of directories containing native libraries; may be null 
  74.      * @param parent 
  75.      *  the parent class loader 
  76.      */  
  77.     public DexClassLoader(String dexPath, String dexOutputDir, String libPath,  
  78.         ClassLoader parent) {  
  79.   
  80.         super(parent);  
  81. ......  
我们看到,他是继承了ClassLoader类的,ClassLoader是类加载器的鼻祖类。同时我们也会发现DexClassLoader只有一个构造函数,而且这个构造函数是:dexPath、dexOutDir、libPath、parent

dexPath:是加载apk/dex/jar的路径

dexOutDir:是dex的输出路径(因为加载apk/jar的时候会解压除dex文件,这个路径就是保存dex文件的)

libPath:是加载的时候需要用到的lib库,这个一般不用

parent:给DexClassLoader指定父加载器


我们在来看一下PathClassLoader的源码

PathClassLoader.java

[java]  view plain  copy
  1. /* 
  2.  * Copyright (C) 2007 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package dalvik.system;  
  18.   
  19. import java.io.ByteArrayOutputStream;  
  20. import java.io.File;  
  21. import java.io.FileNotFoundException;  
  22. import java.io.IOException;  
  23. import java.io.InputStream;  
  24. import java.io.RandomAccessFile;  
  25. import java.net.MalformedURLException;  
  26. import java.net.URL;  
  27. import java.util.ArrayList;  
  28. import java.util.Enumeration;  
  29. import java.util.List;  
  30. import java.util.NoSuchElementException;  
  31. import java.util.zip.ZipEntry;  
  32. import java.util.zip.ZipFile;  
  33.   
  34. /** 
  35.  * Provides a simple {@link ClassLoader} implementation that operates on a list 
  36.  * of files and directories in the local file system, but does not attempt to 
  37.  * load classes from the network. Android uses this class for its system class 
  38.  * loader and for its application class loader(s). 
  39.  */  
  40. public class PathClassLoader extends ClassLoader {  
  41.   
  42.     private final String path;  
  43.     private final String libPath;  
  44.   
  45.     /* 
  46.      * Parallel arrays for jar/apk files. 
  47.      * 
  48.      * (could stuff these into an object and have a single array; 
  49.      * improves clarity but adds overhead) 
  50.      */  
  51.     private final String[] mPaths;  
  52.     private final File[] mFiles;  
  53.     private final ZipFile[] mZips;  
  54.     private final DexFile[] mDexs;  
  55.   
  56.     /** 
  57.      * Native library path. 
  58.      */  
  59.     private final List<String> libraryPathElements;  
  60.   
  61.     /** 
  62.      * Creates a {@code PathClassLoader} that operates on a given list of files 
  63.      * and directories. This method is equivalent to calling 
  64.      * {@link #PathClassLoader(String, String, ClassLoader)} with a 
  65.      * {@code null} value for the second argument (see description there). 
  66.      * 
  67.      * @param path 
  68.      *            the list of files and directories 
  69.      * 
  70.      * @param parent 
  71.      *            the parent class loader 
  72.      */  
  73.     public PathClassLoader(String path, ClassLoader parent) {  
  74.         this(path, null, parent);  
  75.     }  
  76.   
  77.     /** 
  78.      * Creates a {@code PathClassLoader} that operates on two given 
  79.      * lists of files and directories. The entries of the first list 
  80.      * should be one of the following: 
  81.      * 
  82.      * <ul> 
  83.      * <li>Directories containing classes or resources. 
  84.      * <li>JAR/ZIP/APK files, possibly containing a "classes.dex" file. 
  85.      * <li>"classes.dex" files. 
  86.      * </ul> 
  87.      * 
  88.      * The entries of the second list should be directories containing 
  89.      * native library files. Both lists are separated using the 
  90.      * character specified by the "path.separator" system property, 
  91.      * which, on Android, defaults to ":". 
  92.      * 
  93.      * @param path 
  94.      *            the list of files and directories containing classes and 
  95.      *            resources 
  96.      * 
  97.      * @param libPath 
  98.      *            the list of directories containing native libraries 
  99.      * 
  100.      * @param parent 
  101.      *            the parent class loader 
  102.      */  
  103.     public PathClassLoader(String path, String libPath, ClassLoader parent) {  
  104.         super(parent);  
  105. ....  
看到了PathClassLoader类也是继承了ClassLoader的,但是他的构造函数和DexClassLoader有点区别就是,少了一个dexOutDir,这个原因也是很简单,因为PathClassLoader是加载/data/app中的apk,而这部分的apk都会解压释放dex到指定的目录:

/data/dalvik-cache


这个释放解压操作是系统做的。所以PathClassLoader可以不需要这个参数的。


上面看了他们两的区别,下面在来看一下Android中的各种类加载器分别加载哪些类:

[java]  view plain  copy
  1. package com.example.androiddemo;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.Context;  
  5. import android.os.Bundle;  
  6. import android.util.Log;  
  7. import android.widget.ListView;  
  8.   
  9. public class MainActivity extends Activity {  
  10.   
  11.     @Override  
  12.     protected void onCreate(Bundle savedInstanceState) {  
  13.         super.onCreate(savedInstanceState);  
  14.         setContentView(R.layout.activity_main);  
  15.           
  16.         Log.i("DEMO""Context的类加载加载器:"+Context.class.getClassLoader());  
  17.         Log.i("DEMO""ListView的类加载器:"+ListView.class.getClassLoader());  
  18.         Log.i("DEMO""应用程序默认加载器:"+getClassLoader());  
  19.         Log.i("DEMO""系统类加载器:"+ClassLoader.getSystemClassLoader());  
  20.         Log.i("DEMO""系统类加载器和Context的类加载器是否相等:"+(Context.class.getClassLoader()==ClassLoader.getSystemClassLoader()));  
  21.         Log.i("DEMO""系统类加载器和应用程序默认加载器是否相等:"+(getClassLoader()==ClassLoader.getSystemClassLoader()));  
  22.           
  23.         Log.i("DEMO","打印应用程序默认加载器的委派机制:");  
  24.         ClassLoader classLoader = getClassLoader();  
  25.         while(classLoader != null){  
  26.             Log.i("DEMO""类加载器:"+classLoader);  
  27.             classLoader = classLoader.getParent();  
  28.         }  
  29.           
  30.         Log.i("DEMO","打印系统加载器的委派机制:");  
  31.         classLoader = ClassLoader.getSystemClassLoader();  
  32.         while(classLoader != null){  
  33.             Log.i("DEMO""类加载器:"+classLoader);  
  34.             classLoader = classLoader.getParent();  
  35.         }  
  36.           
  37.     }  
  38.   
  39. }  

运行结果:



依次来看一下

1) 系统类的加载器

[java]  view plain  copy
  1. Log.i("DEMO""Context的类加载加载器:"+Context.class.getClassLoader());  
  2. Log.i("DEMO""ListView的类加载器:"+ListView.class.getClassLoader());  
从结果看到他们的加载器是:BootClassLoader,关于他源码我没有找到,只找到了class文件(用jd-gui查看):


看到他也是继承了ClassLoader类。


2) 应用程序的默认加载器

[java]  view plain  copy
  1. Log.i("DEMO""应用程序默认加载器:"+getClassLoader());  
运行结果:


默认类加载器是PathClassLoader,同时可以看到加载的apk路径,libPath(一般包括/vendor/lib和/system/lib)


3) 系统类加载器

[java]  view plain  copy
  1. Log.i("DEMO""系统类加载器:"+ClassLoader.getSystemClassLoader());  
运行结果:


系统类加载器其实还是PathClassLoader,只是加载的apk路径不是/data/app/xxx.apk了,而是系统apk的路径:/system/app/xxx.apk


4) 默认加载器的委派机制关系

[java]  view plain  copy
  1. Log.i("DEMO","打印应用程序默认加载器的委派机制:");  
  2. ClassLoader classLoader = getClassLoader();  
  3. while(classLoader != null){  
  4.     Log.i("DEMO""类加载器:"+classLoader);  
  5.     classLoader = classLoader.getParent();  
  6. }  
打印结果:


默认加载器PathClassLoader的父亲是BootClassLoader

5) 系统加载器的委派机制关系

[java]  view plain  copy
  1. Log.i("DEMO","打印系统加载器的委派机制:");  
  2. classLoader = ClassLoader.getSystemClassLoader();  
  3. while(classLoader != null){  
  4.     Log.i("DEMO""类加载器:"+classLoader);  
  5.     classLoader = classLoader.getParent();  
  6. }  
运行结果:


可以看到系统加载器的父亲也是BootClassLoader


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值