插件化的原理分析及实现

 学习插件化前需要了解类加载器、反射及动态代理等基本知识
 技术方案:
 1.宿主apk和插件apk都是使用PathClassLoader加载,合并宿主和插件的ClassLoader
 2.宿主apk资源和插件apk资源是隔离的,重写Activity的getResources和getAssets
 3.Hook IActivityManager.startActivity和ActivityThread.mH.mCallback来骗过AMS对Activity的检测
 支持范围:
 1.四大组件只支持Activity
    现在只对插件中的Activity做了支持
 2.Activity具有生命周期
    因为在new Activity之前就已经替换掉占坑Activity,之后的操作都是插件Activity的参与的,所以插件Activity具有生命周期
 3.Activity的launchMode只支持standard 
    本项目中只支持一个占坑的Activity,占坑的Activity的launchMode是什么,启动的插件Activity就是什么launchMode,如果需要支持四种launchMode,需要在manifest中分别注册四种启动模式的Activity用于占坑,具体实现可以参考VirtualAPK

插件化是将项目拆分成多个模块,分别对应一个宿主模块和多个插件模块,宿主和插件都是一个独立的工程,可以生成相应的apk,打包时可以将插件apk放入宿主包中也可以分开通过网络获取,插件化是用于加载插件的一种技术。插件化有助于减少宿主APP项目功能并减少宿主APK文件过大的问题。

要想实现插件化框架,就需要解决以下三个技术点
1.插件化框架如何实现插件APK的类加载
2.插件化框架如何实现插件化APK的资源加载
3.插件化框架如何实现对四大组件的支持

一.插件化框架如何实现插件APK的类加载 

通过context.getClassLoader()获取的是PathClassLoader(其实是通过LoadedApk获取),由类加载机制的『双亲委派』特性,只要有一个应用程序类由某一个ClassLoader加载,那么它引用到的别的类除非父加载器能加载,否则都是由这同一个加载器加载的(不遵循双亲委派模型的除外)。
PathClassLoader加载安装到系统中的apk文件中的class文件,DexClassLoader是加载未安装的apk,那么ClassLoader内部到底是怎么去加载类的呢,查看类加载器源码如下:

public abstract class ClassLoader {
    private final ClassLoader parent;

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }

    protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
    {
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                c = findClass(name);
            }
        }
        return c;
    }

    protected final Class<?> findLoadedClass(String name) {
        ClassLoader loader;
        if (this == BootClassLoader.getInstance())
            loader = null;
        else
            loader = this;
        return VMClassLoader.findLoadedClass(loader, name);
    }

    private Class<?> findBootstrapClassOrNull(String name) {
        return null;
    }

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }
}
//可以加载*.jar和*.apk中的类文件,可以加载并没有安装到系统中的class类,所以DexClassLoader是动态加载的核心
public class DexClassLoader extends BaseDexClassLoader {
   /**
	dexPath:指定要加载的dex文件路径
	optimizedDirectory:指定的dexPath文件要被拷贝到那个路径(应用程序内部路径)中,不能为空
	librarySearchPath :native库的路径,可为空
	parent:父类加载器
   */
    public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
        super((String)null, (File)null, (String)null, (ClassLoader)null);
    }
}
//只能加载已经安装到系统中的dex文件
public class PathClassLoader extends BaseDexClassLoader {
    public PathClassLoader(String dexPath, ClassLoader parent) {
        super((String)null, (File)null, (String)null, (ClassLoader)null);
    }
    /**
	//和DexClassLoader的构造比少了optimizedDirectory参数,正是因为此,PathClassLoader只能加载已经安装到系统中的apk中的dex文件(类)
	dexPath:指定要加载的dex文件路径
	librarySearchPath :native库的路径,可为空
	parent:父类加载器
    */
    public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
        super((String)null, (File)null, (String)null, (ClassLoader)null);
          }
}

public class BaseDexClassLoader extends ClassLoader {
     private final DexPathList pathList;
     @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
       List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
      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;
  }
}

final class DexPathList {
    private static final String DEX_SUFFIX = ".dex";
    private static final String zipSeparator = "!/";

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

    /**
     * List of dex/resource (class path) elements.
     * Should be called pathElements, but the Facebook app uses reflection
     * to modify 'dexElements' (http://b/7726934).
     */
    private Element[] dexElements;

    public DexPathList(ClassLoader definingContext, ByteBuffer[] dexFiles) {
        this.definingContext = definingContext;
        //...
        this.dexElements = makeInMemoryDexElements(dexFiles, suppressedExceptions);
        //...
    }

    private static Element[] makeInMemoryDexElements(ByteBuffer[] dexFiles,
                                                     List<IOException> suppressedExceptions) {
        Element[] elements = new Element[dexFiles.length];
        int elementPos = 0;
        for (ByteBuffer buf : dexFiles) {
            try {
                DexFile dex = new DexFile(buf);
                elements[elementPos++] = new Element(dex);
            } catch (IOException suppressed) {
                System.logE("Unable to load dex file: " + buf, suppressed);
                suppressedExceptions.add(suppressed);
            }
        }
        if (elementPos != elements.length) {
            elements = Arrays.copyOf(elements, elementPos);
        }
        return elements;
    }

    public Class<?> findClass(String name, List<Throwable> suppressed) {
        for (Element element : dexElements) {
            Class<?> clazz = element.findClass(name, definingContext, suppressed);
            if (clazz != null) {
                return clazz;
            }
        }

        if (dexElementsSuppressedExceptions != null) {
            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
        }
        return null;
    }

    static class Element {
        private final File path;

        private final DexFile dexFile;
        private ClassPathURLStreamHandler urlHandler;
        private boolean initialized;

        public Class<?> findClass(String name, ClassLoader definingContext,
                                  List<Throwable> suppressed) {
            return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
                    : null;
        }
    }
}

public final class DexFile {
    private Object mCookie;

    private Object mInternalCookie;
    private final String mFileName;

    public Class loadClass(String name, ClassLoader loader) {
        String slashName = name.replace('.', '/');
        return loadClassBinaryName(slashName, loader, null);
    }

    public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
        return defineClass(name, loader, mCookie, this, suppressed);
    }

    private static Class defineClass(String name, ClassLoader loader, Object cookie,
                                     DexFile dexFile, List<Throwable> suppressed) {
        Class result = null;
        try {
            result = defineClassNative(name, loader, cookie, dexFile);
        } catch (NoClassDefFoundError e) {
            if (suppressed != null) {
                suppressed.add(e);
            }
        } catch (ClassNotFoundException e) {
            if (suppressed != null) {
                suppressed.add(e);
            }
        }
        return result;
    }

    private static native Class defineClassNative(String name, ClassLoader loader, Object cookie,
                                                  DexFile dexFile)
            throws ClassNotFoundException, NoClassDefFoundError;
}

由此可以类加载流程:ClassLoader.findClass() -> BaseDexClassLoader.findClass() -> DexPathList.findClass() -> DexFile.loadClassBinaryName(),而DexPathList中的findClass()是通过类型是Element[]的字段dexElements类完成的,而每个Element对应一个dex或apk等。所以系统的类加载器PathClassLoader所能加载的类对应的dex都存储在Element中,所以能否加载某些类的关键在于Element。apk被安装之后,apk文件的代码以及资源会被系统存放在固定的目录(比如/data/app/package_name/base-1.apk )系统在进行类加载的时候,会自动去这一个或者几个特定的路径来寻找这个类。而插件apk系统类加载器PathClassLoader是不知道存在哪里,自然无法加载插件中的类或Activity。在Activity启动的时候,Activity的创建是由什么加载的,可以在Activity的启动流程代码中发现,如下:

java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);

可以发现activity 是使用ClassLoader对象cl把这个类加载进虚拟机,最后使用反射创建了这个Activity类的实例对象。而这个ClassLoader就是PathClassLoader,如果我们Hook ClassLoader,使其获取的ClassLoader是DexClassLoader,让其可以加载插件apk中的类,就可以解决无法加载插件类问题。

由此可以发现解决这个问题有两个思路,要么全盘接管这个类加载的过程;要么告知系统我们使用的插件存在于哪里,让系统帮忙加载。则:

方式一宿主和插件隔离,全盘接管,每个插件构造自己的ClassLoader
通过r.packageInfo.getClassLoader()可知,ClassLoader是由r.packageInfo返回的对象回去,跟踪代码发现r.packageInfo是LoadedApk类型,而LoadedApk对象是APK文件在内存中的表示,LoadedApk创建在

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

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) {
                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());
                }

                if (differentUser) {
                    // Caching not supported across users
                } else if (includeCode) {
                    mPackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                } else {
                    mResourcePackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                }
            }
            return packageInfo;
        }
    }

ClassLoader是通过LoadedApk获取的,LoadedApk是apk在内存的表示,所以我们就只需要将插件apk构造成LoadedApk,而通过该LoadedApk获取的ClassLoader对象用可以加载未安装apk的DexClassLoader来加载,然后将构造的LoadedApk存入WeakReference<LoadedApk> ref即可,当加载插件类时,使用插件LoadedApk获取的ClassLoader加载即可。这个实现过程很复杂,具体方案可查看DroidPlugin实现方案文档

方式二宿主和插件合并,系统帮忙,将插件apk添加进系统ClassLoader (我使用了这种,简单)

PathClassLoader是无法加载插件apk中的类的,通过上面分析可知,ClassLoader最终是通过Element中的DexFile加载类的,而Element对应一个dex或apk的存放路径,则我们可以插件apk的路径手动构造出Element或使用DexClassLoader加载插件apk后从DexClassLoader中取出Element,然后将插件对应的Element合并到系统加载器PathLoaderLoader中,从而使得PathClassLoader具有加载插件apk的能力。现在MultiDex和很多热修复框架都是通过改变DexPathList中的dexElements来实现的。

DexClassLoader如何加载插件apk,代码如下:

DexClassLoader dcl = new DexClassLoader("apk路径", getDir("opt", MODE_PRIVATE).getAbsolutePath(), null, getClassLoader());

Class<?> Cls = dcl.loadClass("com.cj.oneplugin.SecondActivity");

 如何合并DexClassLoader和PathClassLoader中的Element,代码如下:

//获取PathClassLoader(BaseDexClassLoader)的DexPathList对象变量pathList
Object pathList = ReflectUtil.getFieldValue(BaseDexClassLoader.class, pathClassLoaderClass, "pathList");
//获取DexPathList的Element[]对象变量dexElements
Object[] dexElements = (Object[]) ReflectUtil.getFieldValue(pathList.getClass(),pathList,"dexElements");

//获取DexClassLoader(BaseDexClassLoader)的DexPathList对象变量pathList
Object pathList = ReflectUtil.getFieldValue(BaseDexClassLoader.class, dexClassLoaderClass, "pathList");
//获取DexPathList的Element[]对象变量dexElements
Object[] dexElements = (Object[]) ReflectUtil.getFieldValue(pathList.getClass(),pathList,"dexElements");

//合并到PathClassLoader

本项目加载插件apk中的类使用方案:合并宿主和插件的ClassLoader (简单),插件中的所有类都使用PathClassLoader类加载

二.插件化框架如何实现插件化APK的资源加载

如果直接启动插件中的Activity,未对资源进行处理的话,会发现虽然加载的是插件中的Activity,但是Activity中的布局和资源都是加载的是宿主中的,就算把插件中的资源合并过来,也会产生资源冲突,具体分析如下:

1).资源ID冲突分析
    从打包流程中可知,Application Resources -> aapt -> R.java和resouces.arsc ,而资源文件都有一个唯一的ID,ID的生成规则是0x7f010000(PackageId(7f) + TypeId(01开始,有attr,color,string等) + ItemValue(0001开始)),因为插件apk也是一个apk,生成R.java的规则和宿主的R.java是一样的,所以就会导致插件的资源ID和宿主的资源ID重复,导致加载插件中的资源时,加载的确实宿主中同ID的资源文件。

加载资源的方式有两种 (我选了方式2,简单)

1).方式1,资源合并
    要想资源合并,就需要修改插件中资源ID生成的规则,则需要修改aapt工具源码,发现可以通过修改PackageId让宿主资源ID和插件资源ID予以区分,因为PackageId的只都是0x7f,插件中则可以修改成0x71,0x72等,具体修改方式见
    还可以干预编译过程,修改resouces.arsc和R.java

2).方式2,资源隔离
    应用程序的每一个Activity组件都关联有一个ContextImpl对象,这个ContextImpl对象就是用来描述Activity组件的运行上下文环境的。我们在Activity组件调用的大部分成员函数都是转发给与它所关联的一个ContextImpl对象的对应的成员函数来处理的,其中就包括用来访问应用程序资源的两个成员函数getResources和getAssets。所以我只需要让插件中的Activity继承BasePluginActivity,BasePluginActivity中重写getResources和getAssets,让其返回插件apk生成的Resources和AssetManager,读取插件apk中的资源信息。只有这样严格的隔离,才能防止插件和宿主、插件之间的资源id不会冲突。

/**
 * 插件中的Activity需要继承BasePluginActivity
 * 主要用于资源隔离
 */
public abstract class BasePluginActivity extends AppCompatActivity {

    private PluginInfo pluginInfo;

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
        pluginInfo = PluginManager.getInstance().getPluginInfo(getApkPackageName());
    }

    /**
     * 插件apk的包名
     * @return
     */
    public abstract String getApkPackageName();

    @Override
    public Resources getResources() {
        return (pluginInfo == null || pluginInfo.getResources() == null) ? super.getResources() :  pluginInfo.getResources();
    }

    @Override
    public AssetManager getAssets() {
        return (pluginInfo == null || pluginInfo.getAssetManager() == null) ? super.getAssets() : pluginInfo.getAssetManager();
    }

}

三.插件化框架如何实现对四大组件的支持

1).如何加载四大组件

    在没有处理类加载问题的时候,四大组件Activity在使用DexClassLoader加载时并且启动Activity时,会直接报类找不到异常而崩溃,因为在Activity的生命周期中,系统中Activity是使用系统类加载器PathClassLoader加载的。因为插件中的类并不是已经安装到系统dex中的类,未安装到系统中的类使用PathClassLoader加载是加载不到的。通过上面分析可以使用宿主和插件ClassLoader合并的方式来解决加载插件类问题,这样PathClassLoader就可以加载插件中的四大组件类了(热修复也是这种原理),这样加载插件中的四大组件就可以在应用中启动了。也就是合并宿主和插件的ClassLoader ,这样一来,加载插件中的类就可以像加载宿主中的类一样,但是需要注意插件中的类不可以和宿主重复,合并代码如下:

//获取PathClassLoader
ClassLoader pathClassLoaderClass = context.getClassLoader();
//获取PathClassLoader(BaseDexClassLoader)的DexPathList对象变量pathList
Object pathList = ReflectUtil.getFieldValue(BaseDexClassLoader.class, pathClassLoaderClass, "pathList");
//获取DexPathList的Element[]对象变量dexElements
Object[] dexElements = (Object[]) ReflectUtil.getFieldValue(pathList.getClass(),pathList,"dexElements");
//获得Element类型
Class<?> dexElementsType = dexElements.getClass().getComponentType();
//创建一个新的Element[], 将用于替换原始的数组
Object[] newdexElementList = (Object[]) Array.newInstance(dexElementsType, dexElements.length + 1);
//构造插件的Element
File apkFile = new File(apkPath);
File optDexFile = new File(apkPath.replace(".apk", ".dex"));
Class[] parameterTypes = {DexFile.class,File.class};
Object[] initargs = {DexFile.loadDex(apkPath, optDexFile.getAbsolutePath(), 0),apkFile};
Object pluginDexElements = ReflectUtil.newInstance(dexElementsType, parameterTypes, initargs);
Object[] pluginDexElementsList = new Object[]{pluginDexElements};
//把原来PathClassLoader中的elements复制进去新的Element[]中
System.arraycopy(dexElements, 0, newdexElementList, 0, dexElements.length);
//把插件的element复制进去新的Element[]中
System.arraycopy(pluginDexElementsList, 0, newdexElementList, dexElements.length, pluginDexElementsList.length);
//替换原来PathClassLoader中的dexElements值
ReflectUtil.setFieldValue(pathList.getClass(),pathList,"dexElements",newdexElementList);

2).如何绕过AMS检测

在启动Activity的时候,AMS会Activity是否在manifest中注册进行检测,如果没有注册就会报错。那么如果绕过AMS的检测呢?两种方案:(我选择了第二种)

方案一:Hook Instrumentation (简单一点)

public class Instrumentation {
    //启动Activity的时候,调用此方法时,替换调Intent
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {

    }

    //AMS检测后,创建Activity之前替换回Intent
    public Activity newActivity(ClassLoader cl, String className,
                                Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        
    }
}

方案二:Hook IActivityManager.startActivity和ActivityThread.mH.mCallback
     不管方案一和方案二都需要熟悉Activity的启动流程,这样才能在合适的地方绕过清单文件检测,下面是简单的Activity代码启动执行流程

//******************************************************************************
类的继承关系,方便阅读源码
SDK 28
class ActivityThread extends ClientTransactionHandler{
}
//可以放入对象池的所有生命周期项的基本接口。
interface ObjectPoolItem{
}
//从服务器到客户端的各个请求的基本接口。
//它们中的每一个都可以在调度之前准备好,并最终执行。
interface BaseClientRequest extends ObjectPoolItem{
}
//可以调度和执行的客户端回调消息。
//这些示例可能是Activity配置更改,多窗口模式更改,活动结果交付等
class ClientTransactionItem implements BaseClientRequest{
}
//请求Activity应达到的生命周期状态。
class ActivityLifecycleItem extends ClientTransactionItem{
}
//请求将Activity移至暂停状态。
class PauseActivityItem extends ActivityLifecycleItem{
}
//请求启动Activity
public class LaunchActivityItem extends ClientTransactionItem {
}

//*******************************************************************************
Activity的启动流程
SDK 28
Activity.java
startActivity() -> startActivity() -> startActivityForResult() -> mInstrumentation.execStartActivity()
Instrumentation.java
execStartActivity() -> ActivityManager.getService().startActivity()
ActivityManagerService.java
startActivity() -> startActivityAsUser() -> startActivityAsUser() -> mActivityStartController.obtainStarter().setMayWait(){mRequest.mayWait = true}.execute()
ActivityStarter.java
execute() -> mRequest.mayWait = true -> startActivityMayWait() -> startActivity() -> startActivity() -> startActivity() -> startActivityUnchecked() -> mSupervisor.resumeFocusedStackTopActivityLocked()
ActivityStackSupervisor.java
resumeFocusedStackTopActivityLocked() -> resumeFocusedStackTopActivityLocked() -> targetStack.resumeTopActivityUncheckedLocked() 
ActivityStack.java
resumeTopActivityUncheckedLocked() -> resumeTopActivityInnerLocked()
	//1.执行Pause
	1.startPausingLocked() -> mService.getLifecycleManager().scheduleTransaction(prev.app.thread, prev.appToken,PauseActivityItem.obtain(prev.finishing, userLeaving,prev.configChangeFlags, pauseImmediately))
	ClientLifecycleManager.java
	scheduleTransaction() ->{传递的ActivityLifecycleItem对象(PauseActivityItem)存储在ClientTransaction中的mLifecycleStateRequest字段} scheduleTransaction() -> transaction.schedule()
	ClientTransaction.java
	schedule() -> mClient.scheduleTransaction(this)
	ActivityThread.ApplicationThread.java
	scheduleTransaction() -> ActivityThread.this.scheduleTransaction(transaction)
	ClientTransactionHandler.java
	scheduleTransaction() -> sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction) -> 
	ActivityThread.java
	ActivityThread.H.EXECUTE_TRANSACTION -> mTransactionExecutor.execute(transaction)
	TransactionExecutor.java
	execute() -> 
		1. executeCallbacks(transaction) //现在还没有Callback
		2. executeLifecycleState(transaction) -> lifecycleItem.execute() -> {lifecycleItem是ClientTransaction中的mLifecycleStateRequest字段,即PauseActivityItem}
		PauseActivityItem.java
		execute() -> client.handlePauseActivity()
		ActivityThread:ClientTransactionHandler.java
		handlePauseActivity() -> performPauseActivity() -> {callActivityOnSaveInstanceState()} performPauseActivityIfNeeded() -> mInstrumentation.callActivityOnPause()
		Instrumentation.java
		callActivityOnPause() -> activity.performPause()
		Activity.java
		performPause() -> onPause() //onPause
	//onCreate
	2.mStackSupervisor.startSpecificActivityLocked()
	ActivityStackSupervisor.java
	startSpecificActivityLocked()
		1.if (app != null && app.thread != null)//App已启动
		realStartActivityLocked() -> goto realStartActivityLocked //启动activity
		2.//App未启动
		mService.startProcessLocked()
		ActivityManagerService.java
		startProcessLocked() -> startProcessLocked() -> startProcessLocked() -> startProcessLocked() -> startProcessLocked() -> startProcessLocked() -> startProcess() -> Process.start()
		Process.java
		Process.start() -> zygoteProcess.start()
		ZygoteProcess.java
		start() -> startViaZygote() -> zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote) -> openZygoteSocketIfNeeded(abi) -> ZygoteState.connect(mSocket)   //Zygote并通过socket通信的方式让Zygote进程fork出一个新的进程,并根据传递的”android.app.ActivityThread”字符串,反射出该对象并执行ActivityThread的main方法对其进行初始化
		ActivityThread.java
		main() -> thread.attach() -> mgr.attachApplication()
		ActivityManagerService.java
		attachApplication() -> attachApplicationLocked() -> mStackSupervisor.attachApplicationLocked(app)
		ActivityStackSupervisor.java
		attachApplicationLocked() -> realStartActivityLocked()  -> goto realStartActivityLocked //启动activity

rerealStartActivityLocked:
ActivityStackSupervisor.java
realStartActivityLocked() -> {clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent)...),lifecycleItem = ResumeActivityItem ,clientTransaction.setLifecycleStateRequest(lifecycleItem)} ->  mService.getLifecycleManager().scheduleTransaction(clientTransaction)  
ClientLifecycleManager.java
scheduleTransaction() -> {传递的ActivityLifecycleItem对象(ResumeActivityItem)存储在ClientTransaction中的mLifecycleStateRequest字段,mActivityCallbacks中存储的是LaunchActivityItem} transaction.schedule()
ClientTransaction.java
schedule() -> mClient.scheduleTransaction(this)
ActivityThread.ApplicationThread.java
scheduleTransaction() -> ActivityThread.this.scheduleTransaction(transaction)
ClientTransactionHandler.java
scheduleTransaction() -> sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction) -> 
ActivityThread.java
ActivityThread.H.EXECUTE_TRANSACTION -> mTransactionExecutor.execute(transaction)
TransactionExecutor.java
execute()
	1.执行LaunchActivityItem,mActivityCallbacks
	executeCallbacks(transaction) -> item.execute()
	LaunchActivityItem.java
	execute() -> handleLaunchActivity()
	ActivityThread.ApplicationThread.java
	handleLaunchActivity() -> performLaunchActivity() 
		1.mInstrumentation.newActivity()
		Instrumentation.java
		newActivity() -> getFactory(pkg).instantiateActivity(cl, className, intent)
		AppComponentFactory.java
		instantiateActivity() -> (Activity) cl.loadClass(className).newInstance() //反射创建Activity
		2.r.packageInfo.makeApplication()
		LoadedApk.java
		makeApplication() -> {if (mApplication != null) return mApplication} -> {ContextImpl.createAppContext()} -> mActivityThread.mInstrumentation.newApplication() {instrumentation.callApplicationOnCreate(app) -> app.onCreate()} //2.Application : onCreate
		Instrumentation.java
		newApplication() -> getFactory(context.getPackageName()).instantiateApplication(cl,className) {app.attach(context)}   //1.Application : attach
		AppComponentFactory.java
		(Application) cl.loadClass(className).newInstance()
		3.activity.attach()
		Activity.java
		attach() -> {mWindow = new PhoneWindow()} 
		4.activity.setTheme(theme)
		Activity.java
		setTheme() -> mWindow.setTheme(resid)
		5.mInstrumentation.callActivityOnCreate()
		Instrumentation.java
		callActivityOnCreate() -> activity.performCreate()
		Activity.java
		performCreate() -> performCreate() -> onCreate()  //onCreate
	2.执行ResumeActivityItem,mLifecycleStateRequest
	executeLifecycleState(transaction)
		1.cycleToPath()
		2.lifecycleItem.execute()  //lifecycleItem为ResumeActivityItem
		ResumeActivityItem.java
		execute() -> client.handleResumeActivity()
		ActivityThread.ApplicationThread.java
		handleResumeActivity()
			1.performResumeActivity()
				1.deliverNewIntents() -> mInstrumentation.callActivityOnNewIntent()
				Instrumentation.java
				callActivityOnNewIntent() -> activity.performNewIntent()
				Activity.java
				performNewIntent() -> onNewIntent(intent)  //onNewIntent
				2.deliverResults() -> r.activity.dispatchActivityResult()
				Activity.java
				dispatchActivityResult() -> onActivityResult()  //onActivityResult
				3.r.activity.performResume()
				Activity.java
				performResume()
					1.performRestart() 
						1 mInstrumentation.callActivityOnRestart()
						Instrumentation.java
						callActivityOnRestart() -> activity.onRestart()
						Activity.java
						onRestart()  //onRestart
						2.performStart(reason) -> mInstrumentation.callActivityOnStart(this)
						Instrumentation.java
						callActivityOnStart() -> activity.onStart()
						Activity.java
						onStart()  //onStart
					2.mInstrumentation.callActivityOnResume(this)
					Instrumentation.java
					callActivityOnResume() -> activity.onResume()
					Activity.java
					onResume()  //onResume
			2.Looper.myQueue().addIdleHandler(new Idler())
			ActivityThread.Idler.java
			queueIdle() -> am.activityIdle()
			ActivityManagerService.java
			activityIdle() -> mStackSupervisor.activityIdleInternalLocked()
			ActivityStackSupervisor.java
			activityIdleInternalLocked() -> stack.stopActivityLocked(r)
			ActivityStack.java
			stopActivityLocked() -> mService.getLifecycleManager().scheduleTransaction(r.app.thread,r.appToken,StopActivityItem.obtain(r.visible, r.configChangeFlags)) {StopActivityItem存在ClientTransaction的mLifecycleStateRequest中}
			ClientLifecycleManager.java
			scheduleTransaction() -> scheduleTransaction() -> transaction.schedule()
			ClientTransaction.java
			schedule() -> mClient.scheduleTransaction(this)
			ActivityThread.ApplicationThread.java
			scheduleTransaction() -> ActivityThread.this.scheduleTransaction()
			ClientTransactionHandler.java
			scheduleTransaction() -> sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction)
			ActivityThread.java
			ActivityThread.H.EXECUTE_TRANSACTION -> mTransactionExecutor.execute(transaction)
			TransactionExecutor.java
			execute() -> executeLifecycleState() -> lifecycleItem.execute()
			StopActivityItem.java
			execute() -> client.handleStopActivity()
			ActivityThread.ClientTransactionHandler.java
			handleStopActivity() -> performStopActivityInner() -> {performPauseActivityIfNeeded() -> if (r.paused) return } -> callActivityOnStop() -> r.activity.performStop()
			Activity.java
			performStop() -> mInstrumentation.callActivityOnStop(this)
			Instrumentation.java
			callActivityOnStop() -> activity.onStop()
			Activity.java
			onStop()    // onStop

从Activity的启动中可以发现,可以在ActivityManager.getService().startActivity()这个地方把Intent送AMS检测之前替换调Intent(或Intent中要启动Activity的className),和ClientTransaction.mActivityCallbacks,在ActivityThread.mH处理的msg.what是EXECUTE_TRANSACTION的时候,取出mActivityCallbacks中第一个是LaunchActivityItem的元素,获取LaunchActivityItem中的mIntent,替换mIntent中的占坑Activity,使用插件中的Activity。(这里是针对SDK 28的Hook流程,SDK  25、26Hook有所不同,项目源码中做了3个版本的兼容处理)
Hook StartActivity的部分代码如下:

Class<?> iActivityManagerClass = Class.forName("android.app.IActivityManager");
//获取Singleton实例
Class<?> activityManagerClass = Class.forName("android.app.ActivityManager");
Field iActivityManagerSingletonField = activityManagerClass.getDeclaredField("IActivityManagerSingleton");
iActivityManagerSingletonField.setAccessible(true);
Object singleton = iActivityManagerSingletonField.get(null);
//从Singleton实例中获取IActivityManager对象mInstance
Class<?> singletonClass = Class.forName("android.util.Singleton");
Field mInstanceField = singletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
Object am = mInstanceField.get(singleton);
Object amProxy = Proxy.newProxyInstance(
       HookStartActivityApi26.class.getClassLoader(),
       new Class[]{iActivityManagerClass},
       new StartActivityInvocationHandler(context, am, proxyClass)
);
//重新指定代理的IActivityManager实例
mInstanceField.set(singleton, amProxy);

Hook LauncherActivity的部分代码如下:

//先把相关@hide的类都建好
Class<?> ClientTransactionClz = Class.forName("android.app.servertransaction.ClientTransaction");
Class<?> LaunchActivityItemClz = Class.forName("android.app.servertransaction.LaunchActivityItem");

Field mActivityCallbacksField = ClientTransactionClz.getDeclaredField("mActivityCallbacks");//ClientTransaction的成员
mActivityCallbacksField.setAccessible(true);
//类型判定,好习惯
if (!ClientTransactionClz.isInstance(msg.obj)) return true;
Object mActivityCallbacksObj = mActivityCallbacksField.get(msg.obj);//根据源码,在这个分支里面,msg.obj就是 ClientTransaction类型,所以,直接用
//拿到了ClientTransaction的List<ClientTransactionItem> mActivityCallbacks;
List list = (List) mActivityCallbacksObj;

if (list.size() == 0) return true;
Object LaunchActivityItemObj = list.get(0);//所以这里直接就拿到第一个就好了

if (!LaunchActivityItemClz.isInstance(LaunchActivityItemObj)) return true;
//这里必须判定 LaunchActivityItemClz,
// 因为 最初的ActivityResultItem传进去之后都被转化成了这LaunchActivityItemClz的实例

Field mIntentField = LaunchActivityItemClz.getDeclaredField("mIntent");
mIntentField.setAccessible(true);
Intent mIntent = (Intent) mIntentField.get(LaunchActivityItemObj);
if(mIntent.hasExtra(EXTRA_ORIGIN_INTENT)) {
Intent oriIntent = (Intent) mIntent.getExtras().getParcelable(EXTRA_ORIGIN_INTENT);

//将占坑intent替换成插件中的intent
mIntentField.set(LaunchActivityItemObj, oriIntent);
//解决AppCompatActivity重新检测清单文件注册问题
handleAppCompatActivityCheck(mIntent);
}

以上就是四大组件之Activity绕过AMS检测的处理方式,绕过Service组件的原理和Activity类似。

出现的问题

1.在加载插件中的Activity的时候,如果Activity使用的是AppCompatActivity,就会报如下错误

2019-04-18 16:28:56.812 15184-15184/com.example.hookplugin E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.hookplugin, PID: 15184
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.hookplugin/com.example.hookplugin.SecondActivity}: java.lang.IllegalArgumentException: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{com.example.hookplugin/com.example.hookplugin.SecondActivity}
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3157)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3268)
        at android.app.ActivityThread.-wrap12(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1888)
        at android.os.Handler.dispatchMessage(Handler.java:109)
        at android.os.Looper.loop(Looper.java:166)
        at android.app.ActivityThread.main(ActivityThread.java:7367)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:469)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:963)
     Caused by: java.lang.IllegalArgumentException: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{com.example.hookplugin/com.example.hookplugin.SecondActivity}
        at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:222) // 再次检测,点击该错误位置,跟踪代码,分析出现该错误的原因
        at android.support.v7.app.AppCompatDelegateImplV9.onCreate(AppCompatDelegateImplV9.java:155)
        at android.support.v7.app.AppCompatDelegateImplV14.onCreate(AppCompatDelegateImplV14.java:59)
        at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:72)
        at com.example.hookplugin.SecondActivity.onCreate(SecondActivity.java:12)
        at android.app.Activity.performCreate(Activity.java:7337)
        at android.app.Activity.performCreate(Activity.java:7328)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1219)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3110)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3268)?
        at android.app.ActivityThread.-wrap12(Unknown Source:0)?
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1888)?
        at android.os.Handler.dispatchMessage(Handler.java:109)?
        at android.os.Looper.loop(Looper.java:166)?
        at android.app.ActivityThread.main(ActivityThread.java:7367)?
        at java.lang.reflect.Method.invoke(Native Method)?
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:469)?
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:963)?
     Caused by: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{com.example.hookplugin/com.example.hookplugin.SecondActivity}
        at android.app.ApplicationPackageManager.getActivityInfo(ApplicationPackageManager.java:430)
        at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:240)
        at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:219)
        at android.support.v7.app.AppCompatDelegateImplV9.onCreate(AppCompatDelegateImplV9.java:155)?
        at android.support.v7.app.AppCompatDelegateImplV14.onCreate(AppCompatDelegateImplV14.java:59)?
        at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:72)?
        at com.example.hookplugin.SecondActivity.onCreate(SecondActivity.java:12)?
        at android.app.Activity.performCreate(Activity.java:7337)?
        at android.app.Activity.performCreate(Activity.java:7328)?
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1219)?
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3110)?
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3268)?
        at android.app.ActivityThread.-wrap12(Unknown Source:0)?
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1888)?
        at android.os.Handler.dispatchMessage(Handler.java:109)?
        at android.os.Looper.loop(Looper.java:166)?
        at android.app.ActivityThread.main(ActivityThread.java:7367)?
        at java.lang.reflect.Method.invoke(Native Method)?
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:469)?
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:963)?

原因分析
出现的因为是AppConpatActivity中又去重新检测了该Activity是否在Manifest中注册,根据报错信息(入口位置NavUtils.getParentActivityName),可以分析出现的原因如下:

NavUtils.java
public static String getParentActivityName(@NonNull Activity sourceActivity) {
        try {
            return getParentActivityName(sourceActivity, sourceActivity.getComponentName());
        } catch (NameNotFoundException e) {
            // Component name of supplied activity does not exist...?
            throw new IllegalArgumentException(e);
        }
    }

@Nullable
    public static String getParentActivityName(@NonNull Context context,
            @NonNull ComponentName componentName)
            throws NameNotFoundException {
        PackageManager pm = context.getPackageManager(); //PackageManager 是ApplicationPackageManager对象
        ActivityInfo info = pm.getActivityInfo(componentName, PackageManager.GET_META_DATA);
        if (Build.VERSION.SDK_INT >= 16) {
            String result = info.parentActivityName;
            if (result != null) {
                return result;
            }
        }
        if (info.metaData == null) {
            return null;
        }
        String parentActivity = info.metaData.getString(PARENT_ACTIVITY);
        if (parentActivity == null) {
            return null;
        }
        if (parentActivity.charAt(0) == '.') {
            parentActivity = context.getPackageName() + parentActivity;
        }
        return parentActivity;
    }

ContextImpl.java
每个ContextImpl中只会存在一份mPackageManager ,IPackageManager是AIDL(IBinder),是在ActivityThread也只会存在一份,而返回的PackageManager是ApplicationPackageManager对象
@Override
    public PackageManager getPackageManager() {
        if (mPackageManager != null) {
            return mPackageManager;
        }

        IPackageManager pm = ActivityThread.getPackageManager();
        if (pm != null) {
            // Doesn't matter if we make more than one instance.
            return (mPackageManager = new ApplicationPackageManager(this, pm));
        }

        return null;
    }

ActivityThread.java
public static IPackageManager getPackageManager() {
        if (sPackageManager != null) {
            //Slog.v("PackageManager", "returning cur default = " + sPackageManager);
            return sPackageManager;
        }
        IBinder b = ServiceManager.getService("package");
        //Slog.v("PackageManager", "default service binder = " + b);
        sPackageManager = IPackageManager.Stub.asInterface(b);
        //Slog.v("PackageManager", "default service = " + sPackageManager);
        return sPackageManager;
    }

在ApplicationPackageManager中获取ActivityInfo
ApplicationPackageManager.java
@Override
    public ActivityInfo getActivityInfo(ComponentName className, int flags)
            throws NameNotFoundException {
        try {
            ActivityInfo ai = mPM.getActivityInfo(className, flags, mContext.getUserId());
            if (ai != null) {
                return ai;
            }
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }

        throw new NameNotFoundException(className.toString());
    }
ApplicationPackageManager中的mPM是IPackageManager,在ActivityThread中只存在一份,并且是interface,而且PackageManager的getActivityInfo也是调用IPackageManager中的getActivityInfo(className, flags, mContext.getUserId()),它是会重新去验证className对应的Activity是否在清单文件中注册,所以我们只需要hook IPackageManager中的getActivityInfo,替换第一个参数className,替换成清单文件中注册的Activity的className即可。

根据以上分析,只需要hook IPackageManager中的getActivityInfo,替换第一个参数className ,具体处理代码如下:

Class<?> atClass = Class.forName("android.app.ActivityThread");
                Method getPackageManagerMethod = atClass.getDeclaredMethod("getPackageManager");
                getPackageManagerMethod.setAccessible(true);
                Object ipm = getPackageManagerMethod.invoke(null);
                Object ipmProxy = Proxy.newProxyInstance(ipm.getClass().getClassLoader(),
                        ipm.getClass().getInterfaces(), new PackageManagerInvocationHandler(ipm,originIntent.getComponent(),intent.getComponent()));
                Field sPackageManagerField = atClass.getDeclaredField("sPackageManager");
                sPackageManagerField.setAccessible(true);
                sPackageManagerField.set(null,ipmProxy);

public class PackageManagerInvocationHandler implements InvocationHandler{
        private Object obj;
        private ComponentName proxyClass;

        public PackageManagerInvocationHandler(Object obj,ComponentName proxyClass) {
            this.obj = obj;
            this.proxyClass = proxyClass;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("getActivityInfo".equals(method.getName())) {
                args[0] = proxyClass;
            }
            Object invoke = method.invoke(obj, args);
            return invoke;
        }
    }

 

类和资源加载的技术方案
 DroidPluginVirtualAPKHookPlugin(我的Demo)
类加载隔离合并合并
资源加载隔离合并隔离

项目地址:https://github.com/xiaojinwei/HookPlugin

参考:https://blog.csdn.net/kc58236582/article/details/53187485

http://weishu.me/2016/04/05/understand-plugin-framework-classloader/

1.插件化框架无法加载aar,只能加载apk

插件化框架的原理是动态加载dex文件,而aar包里还没有dex文件,只有jar,并且这个jar也不是dex格式的,因为DexClassLoader只能加载dex,所以插件化框架无法加载aar。
可以使用:dx --dex --output=<target.jar> <origin.jar> (dx工具在build-tools文件夹下)命令将aar中的jar转成dex就可以使用DexClassLoader加载了。
DexFile.loadDex(apkPath, optDexFile.getAbsolutePath(), 0),DexFile对应一个x.dex文件路径,最后调用Native openDexFileNative()加载dex,如果非dex格式的文件会报如下错误:
java.io.IOException: No original dex files found for dex location /data/user/0/com.cj.plugin/files/twoplugin-debug.apk 
twoplugin-debug.apk其实是aar,只是将其后缀改成了apk

 

Activity的启动流程
SDK 27
private class ApplicationThread extends IApplicationThread.Stub {}
public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {}

Activity -> startActivityForResult() -> 
Instrumentation -> execStartActivity()
ActivityManager -> getService()
ActivityManagerService -> startActivity() -> startActivityAsUser() 
ActivityStarter -> startActivityMayWait()
	//ActivityStackSupervisor -> resolveIntent() -> resolveIntent() 
	//ActivityManagerService  -> getPackageManagerInternalLocked()
ActivityStarter -> startActivityMayWait() -> startActivityLocked() -> startActivityLocked() -> startActivity() -> startActivity() -> startActivityUnchecked()
ActivityStackSupervisor -> resumeFocusedStackTopActivityLocked() ->   resumeFocusedStackTopActivityLocked() ->     
ActivityStack -> resumeTopActivityUncheckedLocked() -> resumeTopActivityInnerLocked() -> startPausingLocked() -> prev.app.thread.schedulePauseActivity 
ActivityThread$ApplicationThread -> schedulePauseActivity() -> ActivityThread$H(Handler) -> PAUSE_ACTIVITY -> handlePauseActivity() -> performPauseActivity() -> performPauseActivity() -> performPauseActivityIfNeeded() 
Instrumentation -> callActivityOnPause()
Activity -> performPause() -> onPause()

ActivityStack -> resumeTopActivityUncheckedLocked() -> resumeTopActivityInnerLocked() -> mStackSupervisor.startSpecificActivityLocked  
ActivityStackSupervisor -> startSpecificActivityLocked() -> realStartActivityLocked() -> app.thread.scheduleLaunchActivity
ActivityThread$ApplicationThread -> scheduleLaunchActivity() -> ActivityThread$H.LAUNCH_ACTIVITY -> LAUNCH_ACTIVITY -> handleLaunchActivity() -> performLaunchActivity() -> 
       1.mInstrumentation.newActivity() 
   	Instrumentation  -> newActivity() -> (Activity)cl.loadClass(className).newInstance()
       2.activity.attach()
	Activity -> attach() 
		1).attachBaseContext(context)
		 2).mWindow = new PhoneWindow()
       3.activity.setTheme(theme)
       4.mInstrumentation.callActivityOnCreate
	Instrumentation  -> callActivityOnCreate() -> activity.performCreate()
	Activity -> performCreate() -> performCreate() -> onCreate()
       5.activity.performStart()
	Activity -> performStart() -> mInstrumentation.callActivityOnStart()
	Instrumentation  -> callActivityOnStart() ->  activity.onStart()
	Activity -> onStart()
        6.mInstrumentation.callActivityOnRestoreInstanceState
	Instrumentation  -> callActivityOnRestoreInstanceState() -> activity.performRestoreInstanceState
	Activity -> performRestoreInstanceState() -> onRestoreInstanceState()
        7.mInstrumentation.callActivityOnPostCreate
	Instrumentation  -> callActivityOnPostCreate() -> activity.onPostCreate()
	Activity -> onPostCreate() -> onPostCreate()

ActivityStack -> resumeTopActivityUncheckedLocked() -> resumeTopActivityInnerLocked() -> next.app.thread.scheduleResumeActivity
ActivityThread$ApplicationThread -> scheduleResumeActivity() -> ActivityThread$H.RESUME_ACTIVITY -> RESUME_ACTIVITY -> handleResumeActivity() 
        1.performResumeActivity()
	  1).deliverNewIntents() -> Instrumentation.callActivityOnNewIntent
	 	Instrumentation  -> callActivityOnNewIntent() -> activity.performNewIntent()
		 Activity  -> performNewIntent() -> onNewIntent()
        	  2).deliverResults(r, r.pendingResults);
	  2).r.activity.performResume()
		Activity -> performResume() 
		                1.performRestart() -> mInstrumentation.callActivityOnRestart()
				Instrumentation -> callActivityOnRestart() -> activity.onRestart()
				Activity -> onRestart()
		                2.mInstrumentation.callActivityOnResume
				Instrumentation -> callActivityOnResume() -> activity.onResume()
				Activity -> onResume()
			3.mFragments.dispatchResume()
         2.wm = a.getWindowManager() -> wm.addView(decor,l)
         3.performConfigurationChangedForActivity() -> activity.onConfigurationChanged()
	Activity -> onConfigurationChanged()

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值