ActivityThread

149 阅读5分钟
// ams和activiyThread binder通信的远程binder对象
final ApplicationThread mAppThread = new ApplicationThread();
// mAppThread接收到ams远程调用, 通过mH对象发送消息到主线程消息队列
final H mH = new H();
// performLaunchActivity
// 通过classLoader加载intent component中的class, 即activity
// 保存该activity实例到mActivities map中, 其他生命周期直接从该缓存中获取activity, onDestory生命周期之后移除该activity缓存, 垃圾回收该activity防止内存泄露
ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }
        ...
        mActivities.put(r.token, r);
// 非系统应用attach ams, 传输applicationThread binder代理对象到ams, ams保存processRecord
final IActivityManager mgr = ActivityManager.getService();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
// ams -> applicationThread -> H(handler)
ams通知应用创建application、执行application attach方法、onCreate方法
private void handleBindApplication(AppBindData data) {
	···
        // 获取应用LoadedApk对象(保护应用信息ApplicationInfo、兼容信息CompatibilityInfo, mBaseClassLoader, mClassLoader, ActivityThread引用, 包名)
    	data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
    ···
     // 创建系统上下文
      final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
      // 通过LoadedApk classLoader反射生成Instrumentation
      final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                    appContext.getClassLoader(), false, true, false);
            final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);

            try {
                final ClassLoader cl = instrContext.getClassLoader();
                mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
            } catch (Exception e) {
                throw new RuntimeException(
                    "Unable to instantiate instrumentation "
                    + data.instrumentationName + ": " + e.toString(), e);
            }
            
           ...
          
            try {
            java.lang.ClassLoader cl = getClassLoader();
            if (!mPackageName.equals("android")) {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                        "initializeJavaContextClassLoader");
                initializeJavaContextClassLoader();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            // classLoader反射生成Application, 并调用attach方法, 回调attachBaseContext
            //  Application app = (Application)clazz.newInstance();
        app.attach(context);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
        } catch (Exception e) {
            if (!mActivityThread.mInstrumentation.onException(app, e)) {
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                throw new RuntimeException(
                    "Unable to instantiate application " + appClass
                    + ": " + e.toString(), e);
            }
        }
        mActivityThread.mAllApplications.add(app);
        mApplication = app;
           ...
           // 执行application onCreate方法
           try {
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!mInstrumentation.onException(app, e)) {
                    throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                        + ": " + e.toString(), e);
                }
            }
            ...
}

private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
                                       String librarySearchPath, String libraryPermittedPath,
                                       ClassLoader parent, String cacheKey) {
        /*
         * This is the parent we use if they pass "null" in.  In theory
         * this should be the "system" class loader; in practice we
         * don't use that and can happily (and more efficiently) use the
         * bootstrap class loader.
         */
        ClassLoader baseParent = ClassLoader.getSystemClassLoader().getParent();

        synchronized (mLoaders) {
            if (parent == null) {
                parent = baseParent;
            }

            /*
             * If we're one step up from the base class loader, find
             * something in our cache.  Otherwise, we create a whole
             * new ClassLoader for the zip archive.
             */
            if (parent == baseParent) {
                ClassLoader loader = mLoaders.get(cacheKey);
                if (loader != null) {
                    return loader;
                }

                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);

                PathClassLoader pathClassloader = PathClassLoaderFactory.createClassLoader(
                                                      zip,
                                                      librarySearchPath,
                                                      libraryPermittedPath,
                                                      parent,
                                                      targetSdkVersion,
                                                      isBundled);

                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setupVulkanLayerPath");
                setupVulkanLayerPath(pathClassloader, librarySearchPath);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

                mLoaders.put(cacheKey, pathClassloader);
                return pathClassloader;
            }

            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
            PathClassLoader pathClassloader = new PathClassLoader(zip, parent);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            return pathClassloader;
        }
    }
    
    
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());
    }
/**
 * Creates path class loaders.
 *
 * @hide
 */
public class PathClassLoaderFactory {
    // Unconstructable
    private PathClassLoaderFactory() {}

    /**
     * Create a PathClassLoader and initialize a linker-namespace for it.
     *
     * @hide
     */
    public static PathClassLoader createClassLoader(String dexPath,
                                                    String librarySearchPath,
                                                    String libraryPermittedPath,
                                                    ClassLoader parent,
                                                    int targetSdkVersion,
                                                    boolean isNamespaceShared) {
        PathClassLoader pathClassloader = new PathClassLoader(dexPath, librarySearchPath, parent);

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "createClassloaderNamespace");
        String errorMessage = createClassloaderNamespace(pathClassloader,
                                                         targetSdkVersion,
                                                         librarySearchPath,
                                                         libraryPermittedPath,
                                                         isNamespaceShared);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

        if (errorMessage != null) {
            throw new UnsatisfiedLinkError("Unable to create namespace for the classloader " +
                                           pathClassloader + ": " + errorMessage);
        }

        return pathClassloader;
    }

    private static native String createClassloaderNamespace(ClassLoader classLoader,
                                                            int targetSdkVersion,
                                                            String librarySearchPath,
                                                            String libraryPermittedPath,
                                                            boolean isNamespaceShared);
}

classLoader都是来源于PathClassLoader PathClassLoader pathClassloader = new PathClassLoader(dexPath, librarySearchPath, parent);

public class PathClassLoader extends BaseDexClassLoader {
	public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }
}


public class BaseDexClassLoader extends ClassLoader {
	
    private final DexPathList pathList;
 
    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        this(dexPath, optimizedDirectory, librarySearchPath, parent, false);
    }
    
    
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
        // 通过pathList查找class
        Class c = pathList.findClass(name, suppressedExceptions);
        if (c == null) {
            ClassNotFoundException cnfe = new ClassNotFoundException(
                    "Didn't find class \"" + name + "\" on path: " + pathList);
            for (Throwable t : suppressedExceptions) {
                cnfe.addSuppressed(t);
            }
            throw cnfe;
        }
        return c;
    }
    
    public void addDexPath(String dexPath, boolean isTrusted) {
        pathList.addDexPath(dexPath, null /*optimizedDirectory*/, isTrusted);
    }
    }
/*package*/ final class DexPathList {
    private static final String DEX_SUFFIX = ".dex";
    private static final String zipSeparator = "!/";

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

    /**
     * 每个dex文件抽象成一个Element
     */
    private Element[] dexElements;
    }
    
    static class Element {
        // dex文件路径
        private final File path;
		// dex文件抽象DexFile
        private final DexFile dexFile;
        }
        
        // 将所有dex文件封装成DexFile、进一步封装成Element
        private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
            List<IOException> suppressedExceptions, ClassLoader loader, boolean isTrusted) {
      Element[] elements = new Element[files.size()];
      int elementsPos = 0;
      /*
       * Open all files and load the (direct or contained) dex files up front.
       */
      for (File file : files) {
          if (file.isDirectory()) {
              // We support directories for looking up resources. Looking up resources in
              // directories is useful for running libcore tests.
              elements[elementsPos++] = new Element(file);
          } else if (file.isFile()) {
              String name = file.getName();

              DexFile dex = null;
              if (name.endsWith(DEX_SUFFIX)) {
                  // Raw dex file (not inside a zip/jar).
                  try {
                      dex = loadDexFile(file, optimizedDirectory, loader, elements);
                      if (dex != null) {
                          elements[elementsPos++] = new Element(dex, null);
                      }
                  } catch (IOException suppressed) {
                      System.logE("Unable to load dex file: " + file, suppressed);
                      suppressedExceptions.add(suppressed);
                  }
              } else {
                  try {
                      dex = loadDexFile(file, optimizedDirectory, loader, elements);
                  } catch (IOException suppressed) {
                      /*
                       * IOException might get thrown "legitimately" by the DexFile constructor if
                       * the zip file turns out to be resource-only (that is, no classes.dex file
                       * in it).
                       * Let dex == null and hang on to the exception to add to the tea-leaves for
                       * when findClass returns null.
                       */
                      suppressedExceptions.add(suppressed);
                  }

                  if (dex == null) {
                      elements[elementsPos++] = new Element(file);
                  } else {
                      elements[elementsPos++] = new Element(dex, file);
                  }
              }
              if (dex != null && isTrusted) {
                dex.setTrusted();
              }
          } else {
              System.logW("ClassLoader referenced unknown path: " + file);
          }
      }
      if (elementsPos != elements.length) {
          elements = Arrays.copyOf(elements, elementsPos);
      }
      return elements;
    }

热修复 每个应用classLoader继承PathClassLoader, PathClassLoader继承BaseDexClassLoader, BaseDexClassLoader类加载功能通过DexPathList实现, DexPathList由Element[]数组构成, 每个Element代表一个dex文件, 每个dex文件封装成dexFile, 热修复原理将插件的Element数组插入到宿主的Element数组前面, 每次查找class都会从前面Element开始查找

public class DexUtils {

    public static void injectDexAtFirst(String dexPath, String defaultDexOptPath) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
        DexClassLoader dexClassLoader = new DexClassLoader(dexPath, defaultDexOptPath, dexPath, getPathClassLoader());
        Object baseDexElements = getDexElements(getPathList(getPathClassLoader()));
        Object newDexElements = getDexElements(getPathList(dexClassLoader));
        Object allDexElements = combineArray(newDexElements, baseDexElements);
        Object pathList = getPathList(getPathClassLoader());
        ReflectionUtils.setField(pathList, pathList.getClass(), "dexElements", allDexElements);
    }

    private static PathClassLoader getPathClassLoader() {
        PathClassLoader pathClassLoader = (PathClassLoader) DexUtils.class.getClassLoader();
        return pathClassLoader;
    }

    private static Object getDexElements(Object paramObject)
            throws IllegalArgumentException, NoSuchFieldException, IllegalAccessException {
        return ReflectionUtils.getField(paramObject, paramObject.getClass(), "dexElements");
    }

    private static Object getPathList(Object baseDexClassLoader)
            throws IllegalArgumentException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
        return ReflectionUtils.getField(baseDexClassLoader, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList");
    }

    private static Object combineArray(Object firstArray, Object secondArray) {
        Class<?> localClass = firstArray.getClass().getComponentType();
        int firstArrayLength = Array.getLength(firstArray);
        int allLength = firstArrayLength + Array.getLength(secondArray);
        Object result = Array.newInstance(localClass, allLength);
        for (int k = 0; k < allLength; ++k) {
            if (k < firstArrayLength) {
                Array.set(result, k, Array.get(firstArray, k));
            } else {
                Array.set(result, k, Array.get(secondArray, k - firstArrayLength));
            }
        }
        return result;
    }

}

activity插件化 分析得知actiivty最后是通过LoadedApk的mClassLoader类加载器加载生成实例,LoadedApk存在ActivityThread map缓存中, key为包名
final ArrayMap<String, WeakReference> mPackages = new ArrayMap<>();

两种插件化方案: 第一种:每个插件都有自己的classLoader, 核心思想, 构造插件的classLoader即PathClassLoader, 存放入ActivityThread mPackages map缓存中, 也就是需要构建一个属于插件的LoadedApk对象 第二种:所有插件共用一个classLoader, 也就是热修复方法, 将插件PathClassLoader继承类BaseDexClassLoader中的dexPathList中的Elements数组插入到宿主的Elements数组中

public static void hookLoadedApkInActivityThread(File apkFile) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException, InstantiationException {

    // 先获取到当前的ActivityThread对象
    Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
    Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
    currentActivityThreadMethod.setAccessible(true);
    Object currentActivityThread = currentActivityThreadMethod.invoke(null);

    // 获取到 mPackages 这个静态成员变量, 这里缓存了dex包的信息
    Field mPackagesField = activityThreadClass.getDeclaredField("mPackages");
    mPackagesField.setAccessible(true);
    Map mPackages = (Map) mPackagesField.get(currentActivityThread);

    // android.content.res.CompatibilityInfo
    Class<?> compatibilityInfoClass = Class.forName("android.content.res.CompatibilityInfo");
    Method getPackageInfoNoCheckMethod = activityThreadClass.getDeclaredMethod("getPackageInfoNoCheck", ApplicationInfo.class, compatibilityInfoClass);

    Field defaultCompatibilityInfoField = compatibilityInfoClass.getDeclaredField("DEFAULT_COMPATIBILITY_INFO");
    defaultCompatibilityInfoField.setAccessible(true);

    Object defaultCompatibilityInfo = defaultCompatibilityInfoField.get(null);
    ApplicationInfo applicationInfo = generateApplicationInfo(apkFile);

    Object loadedApk = getPackageInfoNoCheckMethod.invoke(currentActivityThread, applicationInfo, defaultCompatibilityInfo);

    String odexPath = Utils.getPluginOptDexDir(applicationInfo.packageName).getPath();
    String libDir = Utils.getPluginLibDir(applicationInfo.packageName).getPath();
    ClassLoader classLoader = new CustomClassLoader(apkFile.getPath(), odexPath, libDir, ClassLoader.getSystemClassLoader());
    Field mClassLoaderField = loadedApk.getClass().getDeclaredField("mClassLoader");
    mClassLoaderField.setAccessible(true);
    mClassLoaderField.set(loadedApk, classLoader);

    // 由于是弱引用, 因此我们必须在某个地方存一份, 不然容易被GC; 那么就前功尽弃了.
    sLoadedApk.put(applicationInfo.packageName, loadedApk);

    WeakReference weakReference = new WeakReference(loadedApk);
    mPackages.put(applicationInfo.packageName, weakReference);
}

/**
 * 这个方法的最终目的是调用
 * android.content.pm.PackageParser#generateActivityInfo(android.content.pm.PackageParser.Activity, int, android.content.pm.PackageUserState, int)
 */
public static ApplicationInfo generateApplicationInfo(File apkFile)
        throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {

    // 找出需要反射的核心类: android.content.pm.PackageParser
    Class<?> packageParserClass = Class.forName("android.content.pm.PackageParser");

    // 我们的终极目标: android.content.pm.PackageParser#generateApplicationInfo(android.content.pm.PackageParser.Package,
    // int, android.content.pm.PackageUserState)
    // 要调用这个方法, 需要做很多准备工作; 考验反射技术的时候到了 - -!
    // 下面, 我们开始这场Hack之旅吧!

    // 首先拿到我们得终极目标: generateApplicationInfo方法
    // API 23 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    // public static ApplicationInfo generateApplicationInfo(Package p, int flags,
    //    PackageUserState state) {
    // 其他Android版本不保证也是如此.
    Class<?> packageParser$PackageClass = Class.forName("android.content.pm.PackageParser$Package");
    Class<?> packageUserStateClass = Class.forName("android.content.pm.PackageUserState");
    Method generateApplicationInfoMethod = packageParserClass.getDeclaredMethod("generateApplicationInfo",
            packageParser$PackageClass,
            int.class,
            packageUserStateClass);

    // 接下来构建需要得参数

    // 首先, 我们得创建出一个Package对象出来供这个方法调用
    // 而这个需要得对象可以通过 android.content.pm.PackageParser#parsePackage 这个方法返回得 Package对象得字段获取得到
    // 创建出一个PackageParser对象供使用
    Object packageParser = packageParserClass.newInstance();
    // 调用 PackageParser.parsePackage 解析apk的信息
    Method parsePackageMethod = packageParserClass.getDeclaredMethod("parsePackage", File.class, int.class);

    // 实际上是一个 android.content.pm.PackageParser.Package 对象
    Object packageObj = parsePackageMethod.invoke(packageParser, apkFile, 0);

    // 第三个参数 mDefaultPackageUserState 我们直接使用默认构造函数构造一个出来即可
    Object defaultPackageUserState = packageUserStateClass.newInstance();

    // 万事具备!!!!!!!!!!!!!!
    ApplicationInfo applicationInfo = (ApplicationInfo) generateApplicationInfoMethod.invoke(packageParser,
            packageObj, 0, defaultPackageUserState);
    String apkPath = apkFile.getPath();

    applicationInfo.sourceDir = apkPath;
    applicationInfo.publicSourceDir = apkPath;

    return applicationInfo;
}