Android动态加载Activity原理
activity的启动流程
加载一个Activity肯定不会像加载一般的类那样,因为activity作为系统的组件有自己的生命周期,有系统的很多回调控制,所以自定义一个DexClassLoader类加载器来加载插件中的Activity肯定是不可以的。
首先不得不了解一下activity的启动流程,当然只是简单的看一下,太详细的话很难研究清楚。
通过startActivity启动后,最终通过AMS进行跨进程回调到ApplicationThread的scheduleLaunchActivity,这时会创建一个ActivityClientRecord对象,这个对象表示一个Acticity以及他的相关信息,比如activityInfo字段包括了启动模式等,还有loadedApk,顾名思义指的是加载过了的APK,他会被放在一个Map中,应用包名到LoadedApk的键值对,包含了一个应用的相关信息。然后通过Handler切换到主线程执performLaunchActivity
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ActivityInfo aInfo = r.activityInfo; // 1.创建ActivityClientRecord对象时没有对他的packageInfo赋值,所以它是null if (r.packageInfo == null) { r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo, Context.CONTEXT_INCLUDE_CODE); } // ... Activity activity = null; try { // 2.非常重要!!这个ClassLoader保存于LoadedApk对象中,它是用来加载我们写的activity的加载器 java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); // 3.用加载器来加载activity类,这个会根据不同的intent加载匹配的activity activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl); if (r.state != null) { r.state.setClassLoader(cl); } } catch (Exception e) { // 4.这里的异常也是非常非常重要的!!!后面就根据这个提示找到突破口。。。 if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to instantiate activity " + component + ": " + e.toString(), e); } } if (activity != null) { Context appContext = createBaseContextForActivity(r, activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); // 从这里就会执行到我们通常看到的activity的生命周期的onCreate里面 mInstrumentation.callActivityOnCreate(activity, r.state); // 省略的是根据不同的状态执行生命周期 } r.paused = true; mActivities.put(r.token, r); } catch (SuperNotCalledException e) { throw e; } catch (Exception e) { // ... } return activity; }
1.getPackageInfo方法最终返回一个LoadedApk对象,它会从一个HashMap的数据结构中取,mPackages维护了包名和LoadedApk的对应关系,即每一个应用有一个键值对对应。如果为null,就新创建一个LoadedApk对象,并将其添加到Map中,重点是这个对象的ClassLoader字段为null!
public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo, int flags) { // 为true boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0; boolean securityViolation = includeCode && ai.uid != 0 && ai.uid != Process.SYSTEM_UID && (mBoundApplication != null ? !UserHandle.isSameApp(ai.uid, mBoundApplication.appInfo.uid) : true); // ... // includeCode为true // classloader为null!!! return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode); } private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo, ClassLoader baseLoader, boolean securityViolation, boolean includeCode) { synchronized (mPackages) { WeakReferenceref; if (includeCode) { // includeCode为true 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) // ... // packageInfo为null,创建一个LoadedApk,并且添加到mPackages里面 packageInfo = new LoadedApk(this, aInfo, compatInfo, this, baseLoader, securityViolation, includeCode && (aInfo.flags&ApplicationInfo. ) != 0); if (includeCode) { mPackages.put(aInfo.packageName, new WeakReference (packageInfo)); } else { mResourcePackages.put(aInfo.packageName, new WeakReference (packageInfo)); } } return packageInfo; } }
2.获取这个activity对应的类加载器,由于上面说过,mClassLoader为null,那么就会执行到ApplicationLoaders#getClassLoader(zip, libraryPath, mBaseClassLoader)方法。
public ClassLoader getClassLoader() { synchronized (this) { if (mClassLoader != null) { return mClassLoader; } // ... // 创建加载器,创建默认的加载器 // zip为Apk的路径,libraryPath也就是JNI的路径 mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, libraryPath, mBaseClassLoader); initializeJavaContextClassLoader(); StrictMode.setThreadPolicy(oldPolicy); } else { if (mBaseClassLoader == null) { mClassLoader = ClassLoader.getSystemClassLoader(); } else { mClassLoader = mBaseClassLoader; } } return mClassLoader; } }ApplicationLoaders使用单例它的getClassLoader方法根据传入的zip路径事实上也就是Apk的路径来创建加载器,返回的是一个PathClassLoader。并且PathClassLoader只能加载安装过的APK。这个加载器创建的时候传入的是当前应用APK的路径,理所应当的,想加载其他的APK就构造一个传递其他APK的类加载器。
3.用该类加载器加载我们要启动的activity,并反射创建一个activity实例
public Activity newActivity(ClassLoader cl, String className,Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException { return (Activity)cl.loadClass(className).newInstance(); }
总结一下上面的思路就是,当我们启动一个activity时,通过系统默认的PathClassLoader来加载这个activity,当然默认情况下只能加载本应用里面的activity,然后就由系统调用到这个activity的生命周期中。
4.这个地方的异常在后面的示例中会出现,到时候分析到原因后就可以找出我们动态加载Activity的思路了。
动态加载Activity:修改系统类加载器
按照这个思路,做这样的一个示例,按下按钮,打开插件中的Activity。
插件项目
plugin.dl.pluginactivity
|--MainActivity.java
内容很简单,就是一个布局上面写了这是插件中的Activity!并重写了他的onStart和onDestroy方法。
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 加载到宿主程序中之后,这个R.layout.activity_main就是宿主程序中的R.layout.activity_main了 setContentView(R.layout.activity_main); } @Override protected void onStart() { super.onStart(); Toast.makeText(this,"onStart", 0).show(); } @Override protected void onDestroy() { super.onDestroy(); Toast.makeText(this,"onDestroy", 0).show(); } }
宿主项目
host.dl.hostactivity
|--MainActivity.java
包括