插件化的原理分析及實現

 學習插件化前需要了解類加載器、反射及動態代理等基本知識
 技術方案:
 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;
        }
    }

 

類和資源加載的技術方案
  DroidPlugin VirtualAPK HookPlugin(我的Demo)
類加載 隔離 合併 合併
資源加載 隔離 合併 隔離

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

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

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

 

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()

 

發佈了43 篇原創文章 · 獲贊 17 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章