Android进阶——Small源码分析之启动流程详解
2018-01-30 20:16
666 查看
前言
插件化现在已经是Android工程师必备的技能之一,只是学会怎么使用是不行的,所以蹭有时间研究一下Small的源码。对于插件化主要解决的问题是四大组件的加载和资源的加载,读懂所有Small源码需要对插件化四大组件的Hook知识和资源加载要有了解,否则是无法看得懂里面的内容的。这篇文章只是对Small的阅读源码启动流程进行分析,详细的细节还是需要通过Debug在例子中去调试才能知道很多东西。在学习Small源码之前,我们可以通过编译Small的Sample,通过例子打断点调试方便查看运行流程。如果使用过Small的小伙伴可以忽略开头的过程。Sample
下载Small源码,导入Sample项目,这里主要使用v1.2.0-beta5为例子,1.3.0开始会有个Bug1、在Terminal中输入指令,编译公共库
gradlew buildLib -q
输出
Small building library 1of 4 - app (0x7f) Small building library 2 of 4 - lib.analytics (0x76) [lib.analytics] remove resources dir... [ OK ] [lib.analytics] remove resources.arsc... [ OK ] [lib.analytics] remove R.java... [ OK ] [lib.analytics] add flags: 1... [ OK ] -> armeabi/libnet_wequick_example_lib_analytics.so (129648 bytes = 126.6 KB) Small building library 3 of 4 - lib.style (0x79) [lib.style] split library res files... [ OK ] [lib.style] slice asset package and reset package id... [ OK ] [lib.style] split library R.java files... [ OK ] [lib.style] split R.class... [ OK ] -> armeabi/libcom_example_mysmall_lib_style.so (5603 bytes = 5.5 KB) Small building library 4 of 4 - lib.utils (0x73) [lib.utils] split library res files... [ OK ] [lib.utils] slice asset package and reset package id... [ OK ] [lib.utils] split library R.java files... [ OK ] [lib.utils] add flags: 10000... [ OK ] [lib.utils] split R.class... [ OK ] -> armeabi/libnet_wequick_example_small_lib_utils.so (7004 bytes = 6.8 KB)
2、在Terminal中输入指令,编译应用插件
gradlew buildBundle -q
输出
Small building bundle 1 of 6 - app.detail (0x67) [app.detail] split library res files... [ OK ] [app.detail] slice asset package and reset package id... [ OK ] [app.detail] split library R.java files... [ OK ] [app.detail] split R.class... [ OK ] -> armeabi/libnet_wequick_example_small_app_detail.so (7616 bytes = 7.4 KB) Small building bundle 2 of 6 - app.home (0x70) [app.home] split library res files... [ OK ] [app.home] slice asset package and reset package id... [ OK ] [app.home] split library R.java files... [ OK ] [app.home] split R.class... [ OK ] -> armeabi/libnet_wequick_example_small_app_home.so (11582 bytes = 11.3 KB) Small building bundle 3 of 6 - app.main (0x77) [app.main] split library res files... [ OK ] [app.main] slice asset package and reset package id... [ OK ] [app.main] split library R.java files... [ OK ] [app.main] add flags: 10000... [ OK ] [app.main] split R.class... [ OK ] -> armeabi/libnet_wequick_example_small_app_main.so (12013 bytes = 11.7 KB) Small building bundle 4 of 6 - app.mine (0x16) [app.mine] split library res files... [ OK ] [app.mine] slice asset package and reset package id... [ OK ] [app.mine] split library R.java files... [ OK ] [app.mine] add flags: 11111110... [ OK ] [app.mine] split R.class... [ OK ] -> armeabi/libnet_wequick_example_small_app_mine.so (48756 bytes = 47.6 KB) Small building bundle 5 of 6 - app.ok-if-stub (0x6a) [app.ok-if-stub] split library res files... [ OK ] [app.ok-if-stub] slice asset package and reset package id... [ OK ] [app.ok-if-stub] split library R.java files... [ OK ] [app.ok-if-stub] split R.class... [ OK ] -> armeabi/libnet_wequick_example_small_appok_if_stub.so (20189 bytes = 19.7 KB)
3、选择app运行项目
基本使用
通过查看Sample源码了解Small的基本使用1、在Application中注册
public Application() { Small.preSetUp(this); } @Override public void onCreate() { super.onCreate(); // Optional Small.setBaseUri("http://code.wequick.net/small-sample/"); Small.setWebViewClient(new MyWebViewClient()); Small.setLoadFromAssets(BuildConfig.LOAD_FROM_ASSETS); }
2、在LaunchActivity中通过Small的方法跳转
public class LaunchActivity extends Activity { private static final long MIN_INTRO_DISPLAY_TIME = 1000000000; // mμs -> 1.0s @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); } @Override protected void onStart() { super.onStart(); SharedPreferences sp = LaunchActivity.this.getSharedPreferences("profile", 0); final SharedPreferences.Editor se = sp.edit(); final long tStart = System.nanoTime(); se.putLong("setUpStart", tStart); Small.setUp(LaunchActivity.this, new net.wequick.small.Small.OnCompleteListener() { @Override public void onComplete() { long tEnd = System.nanoTime(); se.putLong("setUpFinish", tEnd).apply(); long offset = tEnd - tStart; if (offset < MIN_INTRO_DISPLAY_TIME) { // 这个延迟仅为了让 "Small Logo" 显示足够的时间, 实际应用中不需要 getWindow().getDecorView().postDelayed(new Runnable() { @Override public void run() { Small.openUri("main", LaunchActivity.this); finish(); } }, (MIN_INTRO_DISPLAY_TIME - offset) / 1000000); } else { Small.openUri("main", LaunchActivity.this); finish(); } } }); } }
3、启动过程
整个启动过程使用了两步过程
Small的预加载:Small.preSetUp()
Small的加载:Small.setUp()
BundleLauncher
在启动的过程中,什么最重要?那就是插件启动器最重要了。在Small的源码中涉及到三个比较重要的插件启动器ActivityLauncher:主要是负责宿主App的加载过程
ApkBundleLauncher:主要是负责插件App的加载过程
WebBundleLauncher:主要是负责加载网络插件的过程
它们的都继承自BundleLauncher,启动器抽象了每一步启动的执行流程,它们的执行顺序如下图
启动流程
一、preSetUppreSetUp的流程比较简单,主要是初始化BundleLauncher存放在List中,然后遍历BundleLauncher的onCreate()
public static void preSetUp(Application context) { if (sContext != null) { return; } sContext = context; // Register default bundle launchers registerLauncher(new ActivityLauncher()); registerLauncher(new ApkBundleLauncher()); registerLauncher(new WebBundleLauncher()); Bundle.onCreateLaunchers(context); } protected static void onCreateLaunchers(Application app) { if (sBundleLaunchers == null) return; // 由于只有ApkBundleLauncher复写了onCreate(),所以只走下面的逻辑 // 1、ActivityBundleLauncher.onCreate():未实现 // 2、ApkBundleLauncher.onCreate():占坑 // 3、WebBundleLauncher.onCreate():未实现 for (BundleLauncher launcher : sBundleLaunchers) { launcher.onCreate(app); } }
二、setUp
setUp主要是判断是否有listener参数,然后决定采取进行同步或者异步来执行加载Bundle
public static void setUp(Context context, OnCompleteListener listener) { if (sContext == null) { // Tips for CODE-BREAKING throw new UnsupportedOperationException( "Please call `Small.preSetUp' in your application first"); } if (sHasSetUp) { if (listener != null) { listener.onComplete(); } return; } Bundle.loadLaunchableBundles(listener); sHasSetUp = true; } protected static void loadLaunchableBundles(Small.OnCompleteListener listener) { Contextcontext = Small.getContext(); boolean synchronous = (listener == null); if (synchronous) { loadBundles(context); return; } // Asynchronous if (sThread == null) { sThread = new LoadBundleThread(context); sHandler = new LoadBundleHandler(listener); sThread.start(); } }
异步执行其实就是执行完成后通过Handler发送消息通知完成加载,然后释放无相关的变量
private static class LoadBundleThread extends Thread { Context mContext; public LoadBundleThread(Context context) { mContext = context; } @Override public void run() { loadBundles(mContext); sHandler.obtainMessage(MSG_COMPLETE).sendToTarget(); } } private static class LoadBundleHandler extends Handler { private Small.OnCompleteListener mListener; public LoadBundleHandler(Small.OnCompleteListener listener) { mListener = listener; } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_COMPLETE: if (mListener != null) { mListener.onComplete(); } mListener = null; sThread = null; sHandler = null; break; } } }
不管是异步还是同步,最后它们都是要执行loadBundles来加载的
private static void loadBundles(Context context) { JSONObject manifestData; try { //获取 /data/data/<application package>/files 目录下的 bundle.json File patchManifestFile = getPatchManifestFile(); //获取 SharedPreferences 存储的bundle.json文件 String manifestJson = getCacheManifest(); //情况一:如果插件信息有缓存,那么加载缓存的内容,并保存到patchManifestFile,清除缓存 if (manifestJson != null) { // 加载SharedPreferences中的缓存的文件并保存到patchManifestFile文件中 if (!patchManifestFile.exists()) patchManifestFile.createNewFile(); PrintWriter pw = new PrintWriter(new FileOutputStream(patchManifestFile)); pw.print(manifestJson); pw.flush(); pw.close(); // 清除SharedPreferences中的缓存 setCacheManifest(null); } else if (patchManifestFile.exists()) { //情况二:缓存过后以后就从patchManifestFile读取缓存的数据 BufferedReader br = new BufferedReader(new FileReader(patchManifestFile)); StringBuilder sb = new StringBuilder(); String line; while ((line = br.readLine()) != null) { sb.append(line); } br.close(); manifestJson = sb.toString(); } else { //情况三:第一次加载都是从assets目录下加载的 InputStream builtinManifestStream = context.getAssets().open(BUNDLE_MANIFEST_NAME); int builtinSize = builtinManifestStream.available(); byte[] buffer = new byte[builtinSize]; builtinManifestStream.read(buffer); builtinManifestStream.close(); manifestJson = new String(buffer, 0, builtinSize); } // Parse manifest file manifestData = new JSONObject(manifestJson); } catch (Exception e) { e.printStackTrace(); return; } // 解析数据,Manifest存放有所有插件的Bundle信息 Manifest manifest = parseManifest(manifestData); if (manifest == null) return; //遍历所有启动器的setUp方法 setupLaunchers(context); // 加载所有插件 loadBundles(manifest.bundles); } protected static void setupLaunchers(Context context) { if(sBundleLaunchers == null) return; for (BundleLauncher launcher : sBundleLaunchers) { // 1、ActivityBundleLauncher.setUp():将宿主的Activity保存到List中 // 2、ApkBundleLauncher.setUp():Hook 出 pending intent // 3、WebBundleLauncher.setUp():创建出WebView launcher.setUp(context); } }
这里会有三种方式进行读取bundle.json文件的方式,分别是
优先读取缓存中的bundle.json
从patch中读取bundle.json
从assets中读取bundle.json
private static void loadBundles(List<Bundle> bundles) { sPreloadBundles = bundles; // Prepare bundle // 遍历所有插件(Bundle)按顺序执行注册过的BundleLauncher的prepareForLaunch方法,即preloadBundle和loadBundle // 如果某一插件符合下面的判断要求,则会进行相对应的赋值并返回,赋值过后的启动器将会影响到后面的加载过程 // 1、ActivityBundleLauncher:判断当前的插件是否为宿主本身,如果是则赋值为ActivityBundleLauncher // 2、ApkBundleLauncher:判断当前的插件是否为app或者lib,如果是则赋值为ApkBundleLauncher // 3、WebBundleLauncher:不做处理,一般不会到这一步 // 由于ApkBundleLauncher没有复写prepareForLaunch方法,所以只能到父类的soBundleLauncher中实现 for (Bundle bundle : bundles) { bundle.prepareForLaunch(); } // Handle I/O if (sIOActions != null) { ExecutorService executor = Executors.newFixedThreadPool(sIOActions.size()); for (Runnable action : sIOActions) { executor.execute(action); } executor.shutdown(); try { if (!executor.awaitTermination(LOADING_TIMEOUT_MINUTES, TimeUnit.MINUTES)) { throw new RuntimeException("Failed to load bundles! (TIMEOUT > " + LOADING_TIMEOUT_MINUTES + "minutes)"); } } catch (InterruptedException e) { e.printStackTrace(); } sIOActions = null; } // Wait for the things to be done on UI thread before `postSetUp`, // as on 7.0+ we should wait a WebView been initialized. (#347) while (sRunningUIActionCount != 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } // Notify `postSetUp' to all launchers for (BundleLauncher launcher : sBundleLaunchers) { // 遍历所有BundleLauncher的postSetUp // 1、ActivityBundleLauncher.onCreate():未实现 // 2、ApkBundleLauncher.onCreate():开始加载并合并插件 // 3、WebBundleLauncher.onCreate():未实现 launcher.postSetUp(); } // Wait for the things to be done on UI thread after `postSetUp`, // like creating a bundle application. while (sRunningUIActionCount != 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } // Free all unused temporary variables // 释放所有插件的资源 for (Bundle bundle : bundles) { if (bundle.parser != null) { bundle.parser.close(); bundle.parser = null; } bundle.mBuiltinFile = null; bundle.mExtractPath = null; } }
到这里setUp就结束了,其余的就是每个启动器的执行,下面用图片总结下Small的setUp过程
ActivityLauncher
主要作用:解析宿主的Activity存储起来public class ActivityLauncher extends BundleLauncher { private static HashSet<String> sActivityClasses; protected static boolean containsActivity(String name) { return sActivityClasses != null && sActivityClasses.contains(name); } /** * 在宿主App里面注册的 Activity 添加到 sActivityClasses 中去(包含占坑的Activity) * * @param context */ @Override public void setUp(Context context) { super.setUp(context); // Read the registered classes in host's manifest file File sourceFile = new File(context.getApplicationInfo().sourceDir); BundleParser parser = BundleParser.parsePackage(sourceFile, context.getPackageName()); parser.collectActivities(); ActivityInfo[] as = parser.getPackageInfo().activities; if (as != null) { sActivityClasses = new HashSet<String>(); for (ActivityInfo ai : as) { sActivityClasses.add(ai.name); } } } /** * 如果是存在宿主或者插件则返回true,保证可以执行loadBundle * 如果是不存在则返回false,就不会去执行loadBundle * <p> * ActivityLauncher 是用来启动宿主 Activity 的 * * @param bundle * @return */ @Override public boolean preloadBundle(Bundle bundle) { if (sActivityClasses == null) return false; String pkg = bundle.getPackageName(); return (pkg == null || pkg.equals("main")); } /** * openUri执行第一时机 * * @param bundle */ @Override public void prelaunchBundle(Bundle bundle) { super.prelaunchBundle(bundle); //Bundle存储需要加载的Intent信息 //TODO: 后面会获取Intent进行跳转 Intent intent = new Intent(); bundle.setIntent(intent); // Intent extras - class // 获取bundle的入口Activity类名 String activityName = bundle.getActivityName(); // 判断是否为插件 if (!sActivityClasses.contains(activityName)) { if (activityName.endsWith("Activity")) { throw new ActivityNotFoundException("Unable to find explicit activity class " + "{ " + activityName + " }"); } String tempActivityName = activityName + "Activity"; if (!sActivityClasses.contains(tempActivityName)) { throw new ActivityNotFoundException("Unable to find explicit activity class " + "{ " + activityName + "(Activity) }"); } activityName = tempActivityName; } intent.setComponent(new ComponentName(Small.getContext(), activityName)); // Intent extras - params // Query参数:存储Uri跳转使用的 String query = bundle.getQuery(); if (query != null) { intent.putExtra(Small.KEY_QUERY, '?' + query); } } /** * openUri执行第二时机 * * @param bundle * @param context */ @Override public void launchBundle(Bundle bundle, Context context) { prelaunchBundle(bundle); super.launchBundle(bundle, context); } }
ApkBundleLauncher
启动器的执行流程我也在前面贴出来了,具体也按照这个流程一、onCreate
主要工作:反射四大组件的变量,等待占坑
@Override public void onCreate(Application app) { super.onCreate(app); Object/*ActivityThread*/ thread; List<ProviderInfo> providers; Instrumentation base; ApkBundleLauncher.InstrumentationWrapper wrapper; Field f; // Get activity thread // 反射获取ActivityThread thread = ReflectAccelerator.getActivityThread(app); // Replace instrumentation // 替换 mInstrumentation,作用:获取sHostInstrumentation,获取当前启动的Intent信息,并对Intent进行操作 try { f = thread.getClass().getDeclaredField("mInstrumentation"); f.setAccessible(true); base = (Instrumentation) f.get(thread); wrapper = new ApkBundleLauncher.InstrumentationWrapper(base); f.set(thread, wrapper); } catch (Exception e) { throw new RuntimeException("Failed to replace instrumentation for thread: " + thread); } // Inject message handler // 替换 mH和mCallback ,作用:拦截Activity的启动等事件 ensureInjectMessageHandler(thread); // Get providers // 获取该APP的 ProviderInfo 列表 try { f = thread.getClass().getDeclaredField("mBoundApplication"); f.setAccessible(true); Object/*AppBindData*/ data = f.get(thread); f = data.getClass().getDeclaredField("providers"); f.setAccessible(true); providers = (List<ProviderInfo>) f.get(data); } catch (Exception e) { throw new RuntimeException("Failed to get providers from thread: " + thread); } // 将这些变量保存起来 sActivityThread = thread; sProviders = providers; sHostInstrumentation = base; sBundleInstrumentation = wrapper; }
二、setUp
主要作用:动态代理创建pendingIntent,目前没看出什么作用
@Override public void setUp(Context context) { super.setUp(context); Field f; // AOP for pending intent // 通过TaskStackBuilder创建的pendingIntent try { f = TaskStackBuilder.class.getDeclaredField("IMPL"); f.setAccessible(true); final Object impl = f.get(TaskStackBuilder.class); InvocationHandler aop = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Intent[] intents = (Intent[]) args[1]; for (Intent intent : intents) { sBundleInstrumentation.wrapIntent(intent); intent.setAction(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); } return method.invoke(impl, args); } }; Object newImpl = Proxy.newProxyInstance(context.getClassLoader(), impl.getClass().getInterfaces(), aop); f.set(TaskStackBuilder.class, newImpl); } catch (Exception ignored) { Log.e(TAG, "Failed to hook TaskStackBuilder. \n" + "Please manually call `Small.wrapIntent` to ensure the notification intent can be opened. \n" + "See https://github.com/wequick/Small/issues/547 for details."); } }
三、preloadBundle
由于ApkBundleLauncher没有重写preloadBundle方法,所以从父类SoBundleLauncher中找
主要作用:插件类型的校验和版本校验,最后保存所需变量;当前方法是ApkBundleLauncher分配启动器的必经方法,如果返回true表示当前启动器分配成功
@Override public boolean preloadBundle(Bundle bundle) { String packageName = bundle.getPackageName(); if (packageName == null) return false; // Check if supporting // 获取支持的插件类型,ApkBundleLauncher 支持 `app` 和 `lib`,WebBundleLauncher 支持`web` String[] types = getSupportingTypes(); if (types == null) return false; //按支持的type与package名对比,快速判断此BundleLauncher能否解析此插件 boolean supporting = false; String bundleType = bundle.getType(); if (bundleType != null) { // Consider user-defined type in `bundle.json' // 如果在 `bundle.json' 中设置了type,就去根据type来找到合适的BundleLauncher for (String type : types) { if (type.equals(bundleType)) { supporting = true; break; } } } else { // Consider explicit type specify in package name as following: // - com.example.[type].any // - com.example.[type]any // 如果没有指定type,就尝试根据包名来判断,看里面是否包含app、lib或者web等 // - com.example.[type].any // - com.example.[type]any // TODO 这里可以打断点看看是否支持加载 String[] pkgs = packageName.split("\\."); int N = pkgs.length; String aloneType = N > 1 ? pkgs[N - 2] : null; String lastComponent = pkgs[N - 1]; for (String type : types) { if ((aloneType != null && aloneType.equals(type)) || lastComponent.startsWith(type)) { supporting = true; break; } } } //如果该BundleLauncher不支持该Bundle类型,直接返回 if (!supporting) return false; // Initialize the extract path // 获取提取路径,ApkBundleLauncher和AssetBundleLauncher分别有不同的定义 File extractPath = getExtractPath(bundle); if (extractPath != null) { if (!extractPath.exists()) { extractPath.mkdirs(); } bundle.setExtractPath(extractPath); } // Select the bundle entry-point, `built-in' or `patch' // 获取插件文件/data/data/<包名>/app_small_base/<包名>.apk文件 File plugin = bundle.getBuiltinFile(); // 解析AndroidManifest.xml文件,得到插件的版本,主题风格,Activity,收集intent-filter等 BundleParser parser = BundleParser.parsePackage(plugin, packageName); // 获取补丁文件/data/data/<包名>/app_small_patch/<包名>.apk文件 File patch = bundle.getPatchFile(); // 解析AndroidManifest.xml文件,得到补丁的版本,主题风格,Activity,收集intent-filter等 BundleParser patchParser = BundleParser.parsePackage(patch, packageName); // 如果插件为空 if (parser == null) { // 如果补丁为空 if (patchParser == null) { // 直接返回 return false; } else { // 如果插件为空,补丁不为空,则加补丁 parser = patchParser; // use patch plugin = patch; } } else if (patchParser != null) { // 又有补丁又有插件的时候 // 如果插件版本高于补丁版本,则删除插件,否则加载补丁 if (patchParser.getPackageInfo().versionCode <= parser.getPackageInfo().versionCode) { Log.d(TAG, "Patch file should be later than built-in!"); patch.delete(); } else { parser = patchParser; // use patch plugin = patch; } } bundle.setParser(parser); // Check if the plugin has not been modified // 检查插件或补丁是否被修改过 long lastModified = plugin.lastModified(); long savedLastModified = Small.getBundleLastModified(packageName); if (savedLastModified != lastModified) { // If modified, verify (and extract) each file entry for the bundle // 如果被修改过,校验插件或补丁签名是否合法来确定是否要解析次插件 if (!parser.verifyAndExtract(bundle, this)) { bundle.setEnabled(false); return true; // Got it, but disabled } Small.setBundleLastModified(packageName, lastModified); } // Record version code for upgrade // 保存插件或补丁的版本 PackageInfo pluginInfo = parser.getPackageInfo(); bundle.setVersionCode(pluginInfo.versionCode); bundle.setVersionName(pluginInfo.versionName); return true; }
四、loadBundle
主要作用:加载插件或补丁的Manifests.xml的所有信息,并存储到LoadApk,LoadApk则是保存着插件加载所需的实体
@Override public void loadBundle(Bundle bundle) { String packageName = bundle.getPackageName(); // 解析插件或补丁的Manifests.xml,这一步的是否插件或补丁来源于上一步的判断 BundleParser parser = bundle.getParser(); // 收集其Activity parser.collectActivities(); PackageInfo pluginInfo = parser.getPackageInfo(); // Load the bundle String apkPath = parser.getSourcePath(); if (sLoadedApks == null) sLoadedApks = new ConcurrentHashMap<String, LoadedApk>(); LoadedApk apk = sLoadedApks.get(packageName); if (apk == null) { apk = new LoadedApk(); apk.packageName = packageName; apk.path = apkPath; apk.nonResources = parser.isNonResources(); if (pluginInfo.applicationInfo != null) { apk.applicationName = pluginInfo.applicationInfo.className; } apk.packagePath = bundle.getExtractPath(); apk.optDexFile = new File(apk.packagePath, FILE_DEX); // Load dex final LoadedApk fApk = apk; Bundle.postIO(new Runnable() { @Override public void run() { try { fApk.dexFile = DexFile.loadDex(fApk.path, fApk.optDexFile.getPath(), 0); } catch (IOException e) { throw new RuntimeException(e); } } }); // Extract native libraries with specify ABI String libDir = parser.getLibraryDirectory(); if (libDir != null) { apk.libraryPath = new File(apk.packagePath, libDir); } sLoadedApks.put(packageName, apk); } if (pluginInfo.activities == null) { return; } // Record activities for intent redirection if (sLoadedActivities == null) sLoadedActivities = new ConcurrentHashMap<String, ActivityInfo>(); for (ActivityInfo ai : pluginInfo.activities) { //记录插件的所有Activity信息 sLoadedActivities.put(ai.name, ai); } // Record intent-filters for implicit action // 收集 intent-filters for implicit action ConcurrentHashMap<String, List<IntentFilter>> filters = parser.getIntentFilters(); if (filters != null) { if (sLoadedIntentFilters == null) { sLoadedIntentFilters = new ConcurrentHashMap<String, List<IntentFilter>>(); } sLoadedIntentFilters.putAll(filters); } // Set entrance activity // 设置该插件的manifest中定义的入口Activity bundle.setEntrance(parser.getDefaultActivityName()); }
五、postSetUp
主要作用:把所有Bundle中的Dex、Resource和NativeLib通过反射Merge到宿主中
@Override public void postSetUp() { super.postSetUp(); if (sLoadedApks == null) { Log.e(TAG, "Could not find any APK bundles!"); return; } // 取出需要加载的插件 LoadedApk Collection<LoadedApk> apks = sLoadedApks.values(); // Merge all the resources in bundles and replace the host one final Application app = Small.getContext(); String[] paths = new String[apks.size() + 1]; // 添加宿主app的资源路径 paths[0] = app.getPackageResourcePath(); // add host asset path int i = 1; // 添加各个插件的资源路径 for (LoadedApk apk : apks) { if (apk.nonResources) continue; // ignores the empty entry to fix #62 paths[i++] = apk.path; // add plugin asset path } if (i != paths.length) { paths = Arrays.copyOf(paths, i); } // 进行资源的合并 ReflectAccelerator.mergeResources(app, sActivityThread, paths); // Merge all the dex into host's class loader ClassLoader cl = app.getClassLoader(); i = 0; int N = apks.size(); String[] dexPaths = new String ; DexFile[] dexFiles = new DexFile ; for (LoadedApk apk : apks) { dexPaths[i] = apk.path; dexFiles[i] = apk.dexFile; if (Small.getBundleUpgraded(apk.packageName)) { // If upgraded, delete the opt dex file for recreating if (apk.optDexFile.exists()) apk.optDexFile.delete(); Small.setBundleUpgraded(apk.packageName, false); } i++; } ReflectAccelerator.expandDexPathList(cl, dexPaths, dexFiles); // 为宿主class loader扩展它的native library路径,这个路径包含了插件的native library路径 // Expand the native library directories for host class loader if plugin has any JNIs. (#79) List<File> libPathList = new ArrayList<File>(); for (LoadedApk apk : apks) { if (apk.libraryPath != null) { libPathList.add(apk.libraryPath); } } if (libPathList.size() > 0) { ReflectAccelerator.expandNativeLibraryDirectories(cl, libPathList); } // 调用所有插件Application的`onCreate' 方法 // Trigger all the bundle application `onCreate' event for (final LoadedApk apk : apks) { String bundleApplicationName = apk.applicationName; if (bundleApplicationName == null) continue; try { final Class applicationClass = Class.forName(bundleApplicationName); Bundle.postUI(new Runnable() { @Override public void run() { try { BundleApplicationContext appContext = new BundleApplicationContext(app, apk); Application bundleApplication = Instrumentation.newApplication( applicationClass, appContext); sHostInstrumentation.callApplicationOnCreate(bundleApplication); } catch (Exception e) { e.printStackTrace(); } } }); } catch (Exception e) { e.printStackTrace(); } } // Lazy init content providers if (mLazyInitProviders != null) { try { Method m = sActivityThread.getClass().getDeclaredMethod( "installContentProviders", Context.class, List.class); m.setAccessible(true); m.invoke(sActivityThread, app, mLazyInitProviders); } catch (Exception e) { throw new RuntimeException("Failed to lazy init content providers: " + mLazyInitProviders); } } // Free temporary variables sLoadedApks = null; sProviders = null; }
WebBundleLauncher
主要作用:负责加载Small中的WebActivitypublic class WebBundleLauncher extends AssetBundleLauncher { private static final String FD_BASE = "small_web"; private static final String FILE_INDEX = "index.html"; @Override protected String[] getSupportingTypes() { return new String[] {"web"}; } @Override protected String getBasePathName() { return FD_BASE; } @Override protected String getIndexFileName() { return FILE_INDEX; } @Override protected Class<? extends Activity> getActivityClass() { return WebActivity.class; } @Override public void setUp(Context context) { super.setUp(context); if (Build.VERSION.SDK_INT < 24) return; Bundle.postUI(new Runnable() { @Override public void run() { // In android 7.0+, on firstly create WebView, it will replace the application // assets with the one who has join the WebView asset path. // If this happens after our assets replacement, // what we have done would be come to naught! // So, we need to push it enOOOgh ahead! (#347) new android.webkit.WebView(Small.getContext()); } }); } }
结语
简单来说,Small启动流程如下图,下一步就是等跳转的时候,将我们的预先占好的坑交给它们去骗过系统的检测相关文章推荐
- Android进阶——Small源码分析之更新流程详解
- Android进阶——Small源码分析之跳转流程详解
- Android进阶系列之源码分析Activity的启动流程
- Android进阶系列之源码分析Activity的启动流程
- Android4.0源码Launcher启动流程分析【android源码Launcher系列一】
- android开发之源码级分析(系统启动流程 & Handler消息机制 & AsyncTask机制)
- Jvm(jdk8)源码分析1-java命令启动流程详解
- Android中ICS4.0源码Launcher启动流程分析【android源码Launcher系列一】
- Rocketmq之namesrv启动流程源码详解分析
- 源码角度分析Android启动流程
- Android中ICS4.0源码Launcher启动流程分析【android源码Launcher系列一】
- Android 6.0 应用启动全流程源码分析
- Android中ICS4.0源码Launcher启动流程分析【android源码Launcher系列一】
- 源码分析Android启动流程
- android 6.0 SystemUI源码分析(2)-SystemUI启动流程
- Android中ICS4.0源码Launcher启动流程分析【android源码Launcher系列一】
- 【进阶android】Volley源码分析——Volley的流程
- Android中ICS4.0源码Launcher启动流程分析【android源码Launcher系列一】
- 【源码分析】Android系统启动流程.
- Android中ICS4.0源码Launcher启动流程分析【android源码Launcher系列一】