android插件开发——加载插件

在阅读本博文的时候,我假设你已经阅读了我之前写的几篇。猛击此处

通过前面的几篇博客,我们解决了如何启动一个并没有在ActivityManifest.xml中声明的activity。但是有很多细心的读者私信我说,我们所有的例子里,插件都是和主工程在一起的呀,我们如何从外部加载一个apk或者dex呢?

本节就是解决这个问题。
在学习本节之前,有一些非常重要的概念需要提一下。比如类加载器的概念。
我们知道在java里面,有很多种加载器,如果按层次划分的话,可以分为
这里写图片描述
在加载类的时候,他们采用委托机制,比如,我们自定义的ClassLoader要加载一个类,它首先会委托AppClassLoader去加载,AppClassLoader又会委托ExtClassLoader去加载,而ExtClassLoader呢又去委托BootStrap加载,如果BootStrap加载成功了,那就返回,否则会让ExtClassLoader加载,如果ExtClassLoader也没加载成功,那就让AppClassLoader加载,以此类推,如果到自定义ClassLoader都还没成功加载类,那么就会抛出ClassNotFound异常。这种机制可以很大程度的避免重复加载一个类——子加载器首先尝试让父加载器加载。因而我们不难得出,在自定义一个类加载器的时候,我们还要为其指定一个父类加载器。当然本文并不是讨论这个的。具体的读者可以参阅姜维前辈的博文:姜维

在android中,系统也提供了两个类加载器:DexClassLoader和PathClassLoader

PathClassLoader用于加载/data/app中的apk,也就是已经安装了的apk,所以它就成了系统的默认类加载器。

而对于DexClassLoader呢,他可以用来任意位置的apk/dex/jar文件。
我们看下源码:

/*
 * 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 {
   

    /**
     * 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) {
        ...
    }
    ...

由注释我们看出,第一个参数是,jar/file文件的位置
第二个参数指定存放dex文件的位置
第三个参数用于指定存放原生库的位置(so文件)
第四个参数就是制定一个父类加载器

很简单,但是由于篇幅限制,我们不打算做个demo,我们会把DexClassLoader的使用放到下面我们的例子里。由于不是很复杂,所以这么做也是合情合理

还记得之前的源码分析吗,当AMS做完一切准备工作,让UI线程开始启动一个新的activity之后,ActivityThread便开始加载一个新的activity
这里写图片描述

之后再handleLaunchActivity函数中:
这里写图片描述

调用mInstrumentation.newActivity方法,我们看下函数签名:
这里写图片描述
通过class加载一个类,并且实例化。而这个cl是什么呢,根据上面的代码我们可以知道是r.packageInfo.getClassLoader的返回值,而这个r.packageInfo是在H的handleMessage中被赋值的:
这里写图片描述
我们看下这个field

  static final class ActivityClientRecord {
        ...
        LoadedApk packageInfo;
        ...
}

而LoadedApk又是什么呢:

/**
 * Local state maintained about a currently loaded .apk.
 * @hide
 */
public final class LoadedApk {
   
    ...
}

它代表了一个apk所对应的内存表示,也就是apk被加载到内存后的信息,比如代码,资源等等。

那么到这里,我们要知道,如果我们要从外部加载一个apk,首先就要获得这个LoadApk对象,因为之后activity的实例化,都会用到LoadApk中的类加载器。因而我们首先要解决的事情就是如何产生一个LoadApk

我们要保证一切万无一失,最好就是模仿android系统的行为,如果我们能和android系统产生一个LoadApk的方式一样,那就做到了万无一失。

回溯上文,一个LoadApk的产生是通过:

   r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);

我们看下函数签名:


    public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
            CompatibilityInfo compatInfo) {
        return getPackageInfo(ai, compatInfo, null, false, true, false);
    }

函数调用了getPackageInfo:


    private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
            ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
            boolean registerPackage) {
        final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
        synchronized (mResourcesManager) {
            WeakReference<LoadedApk> ref;
            if (differentUser) {
                // Caching not supported across users
                ref = null;
            } else if (includeCode) {
                //includeCode的值为true 所以必定会调用这个函数
                //它的作用是,先从缓存中获取LoadApk
                ref = mPackages.get(aInfo.packageName);
            } else {
                ref = mResourcePackages.get(aInfo.packageName);
            }

            LoadedApk packageInfo = ref != null ? ref.get() : null;
            //如果并没有缓存 那就产生一个新的实例
            if (packageInfo == null || (packageInfo.mResources != null
                    && !packageInfo.mResources.getAssets().isUpToDate())) {
                if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
                        : "Loading resource-only package ") + aInfo.packageName
                        + " (in " + (mBoundApplication != null
                                ? mBoundApplication.processName : null)
                        + ")");
                //产生一个新的实例
                packageInfo =
                    new LoadedApk(this, aInfo, compatInfo, baseLoader,
                            securityViolation, includeCode &&
                            (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);

                if (mSystemThread && "android".equals(aInfo.packageName)) {
                    packageInfo.installSystemApplicationInfo(aInfo,
                            getSystemContext().mPackageInfo.getClassLoader());
                
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值