上一篇《淘宝Atlas框架初探atlas-core(一)》我们研究了淘宝模块化apk:bundle体系,还有bundle体系配套的hook体系。
本篇我们继续研究一下atlas框架的四大组件插件化。
第一篇的分析中我们了解到bundle体系已经为组件的插件化做了准备。下面我们看atlas如何运用bundle机制动态完成bundle的加载,我们看AtlasDemo MainActivity怎么做的。
private ActivityGroupDelegate mActivityDelegate;
private ViewGroup mActivityGroupContainer;
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
switchToActivity("home","com.taobao.firstbundle.FirstBundleActivity");
return true;
case R.id.navigation_dashboard:
switchToActivity("second","com.taobao.secondbundle.SecondBundleActivity");
return true;
case R.id.navigation_notifications:
return true;
}
return false;
}
};
public void switchToActivity(String key,String activityName){
Intent intent = new Intent();
intent.setClassName(getBaseContext(),activityName);
mActivityDelegate.startChildActivity(mActivityGroupContainer,key,intent);
}
MainActivity通过ActivityGroupDelegate的startChildActivity实现Bundle的切换
public void startChildActivity(ViewGroup container, String key, Intent intent){
//移除内容部分全部的View
container.removeAllViews();
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
Activity contentActivity = mLocalActivityManager.getActivity(key);
if(contentActivity!=null) {
container.addView(
mLocalActivityManager.getActivity(key)
.getWindow().getDecorView(),
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
mLocalActivityManager.switchToChildActivity(key);
}else{
execStartChildActivityInternal(container, key, intent);
}
}
我们找到了关键的LocalActivityManager,从功能上看和正常的ActivityManager是相同的,
它管理了所有Activity的所有状态,控制Activity之间的切换。将正常的远程Binder调用变成了本地管理。
public void execStartChildActivityInternal(ViewGroup container,String key, Intent intent){
String packageName = null;
String componentName = null ;
Context context = container.getContext();
if (intent.getComponent() != null) {
packageName = intent.getComponent().getPackageName();
componentName = intent.getComponent().getClassName();
} else {
ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(intent, 0);
if (resolveInfo != null && resolveInfo.activityInfo != null) {
packageName = resolveInfo.activityInfo.packageName;
componentName = resolveInfo.activityInfo.name;
}
}
if (componentName == null){
Log.e("ActivityGroupDelegate","can not find componentName");
}
if (!StringUtils.equals(context.getPackageName(), packageName)) {
Log.e("ActivityGroupDelegate","childActivity can not be external Activity");
}
String bundleName = AtlasBundleInfoManager.instance().getBundleForComponet(componentName);
if(!TextUtils.isEmpty(bundleName)){
BundleImpl impl = (BundleImpl) Atlas.getInstance().getBundle(bundleName);
if(impl!=null&&impl.checkValidate()) {
performLaunchChildActivity(container,key,intent);
}else {
if(ActivityTaskMgr.getInstance().peekTopActivity()!=null && Looper.getMainLooper().getThread().getId()==Thread.currentThread().getId()) {
asyncStartActivity(container,key,bundleName,intent);
}else{
performLaunchChildActivity(container,key,intent);
}
}
}else{
// Try to get class from system Classloader
try {
Class<?> clazz = null;
clazz = Framework.getSystemClassLoader().loadClass(componentName);
if (clazz != null) {
performLaunchChildActivity(container,key,intent);
}
} catch (ClassNotFoundException e) {
Log.e("ActivityGroupDelegate",e.getCause().toString());
}
}
}
第一次进入Activity的时候先走execStartChildActivityInternal(container, key, intent);
public String getBundleForComponet(String componentName){
InitBundleInfoByVersionIfNeed();
if (mCurrentBundleListing == null ||
mCurrentBundleListing.getBundles() == null ||
mCurrentBundleListing.getBundles().size() == 0){
return findBundleByComponentName(componentName);
}
......
}
/**
* 根据版本载入清单
*/
private synchronized void InitBundleInfoByVersionIfNeed(){
if(mCurrentBundleListing==null){
String bundleInfoStr = null;
try {
Field field = FrameworkProperties.class.getDeclaredField("bundleInfo");
field.setAccessible(true);
bundleInfoStr = (String)field.get(FrameworkProperties.class);
}catch(Throwable e){
e.printStackTrace();
}
if(!TextUtils.isEmpty(bundleInfoStr)) {
try {
LinkedHashMap<String,BundleListing.BundleInfo> infos = BundleListingUtil.parseArray(bundleInfoStr);
BundleListing listing = new BundleListing();
listing.setBundles(infos);
mCurrentBundleListing = listing;
}catch(Throwable e){
e.printStackTrace();
}
}else{
throw new RuntimeException("read bundleInfo failed");
}
}
}
根据版本载入不同的清单,可以在FrameworkProperties.class中设置大版本的Bundle信息
如果BundleList为空,则进入AtlasBundleInfoManager 的getBundleForComponet
private String findBundleByComponentName(String componentClassName){
getComponentInfoFromManifestIfNeed();
ComponentName componentName = new ComponentName(RuntimeVariables.androidApplication.getPackageName(),componentClassName);
if(activityInfos!=null){
ActivityInfo info = activityInfos.get(componentClassName);
if(info!=null){
if(info.metaData!=null){
return info.metaData.getString("bundleLocation");
}else {
try {
ActivityInfo detailInfo = RuntimeVariables.androidApplication.getPackageManager().getActivityInfo(componentName, PackageManager.GET_META_DATA);
if (detailInfo != null && detailInfo.metaData != null) {
info.metaData = detailInfo.metaData;
return detailInfo.metaData.getString("bundleLocation");
} else {
return null;
}
} catch (Throwable e) {
}
}
}
}
......
}
getComponemntInfoFromManifestIfNeed获取所有对应包名的Manifest里面四大组件的信息,然后保存到AtlasBundleInfoManager本地,日后提供给ActivityGroupDelegate调用,我们继续看对应Activity的manifest。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.taobao.firstbundle">
<application
android:allowBackup="true"
android:supportsRtl="true">
<activity
android:name=".FirstBundleActivity"
android:theme="@style/AppTheme.NoActionBar" />
<service
android:name=".FirstBundleService"
android:enabled="true"
android:exported="true"></service>
</application>
</manifest>
demo里面并没有“bundleLocation”的meta信息,
if(activityInfos!=null){
ActivityInfo info = activityInfos.get(componentClassName);
if(info!=null){
if(info.metaData!=null){
return info.metaData.getString("bundleLocation");
}else {
try {
ActivityInfo detailInfo = RuntimeVariables.androidApplication.getPackageManager().getActivityInfo(componentName, PackageManager.GET_META_DATA);
if (detailInfo != null && detailInfo.metaData != null) {
info.metaData = detailInfo.metaData;
return detailInfo.metaData.getString("bundleLocation");
} else {
return null;
}
} catch (Throwable e) {
}
}
}
}
看流程,没有meta值bundleLocation,bundle的activity就启动不了,所以这个bundle值可能在编译的时候动态设置的,这个bundleLocation我们在update篇在继续分析,这里我们先继续。我们回到ActivityGroupDelegate的execStartChildActivityInternal方法
public void execStartChildActivityInternal(ViewGroup container,String key, Intent intent){
......
String bundleName = AtlasBundleInfoManager.instance().getBundleForComponet(componentName);
if(!TextUtils.isEmpty(bundleName)){
BundleImpl impl = (BundleImpl) Atlas.getInstance().getBundle(bundleName);
if(impl!=null&&impl.checkValidate()) {
performLaunchChildActivity(container,key,intent);
}else {
if(ActivityTaskMgr.getInstance().peekTopActivity()!=null && Looper.getMainLooper().getThread().getId()==Thread.currentThread().getId()) {
asyncStartActivity(container,key,bundleName,intent);
}else{
performLaunchChildActivity(container,key,intent);
}
}
}else{
// Try to get class from system Classloader
try {
Class<?> clazz = null;
clazz = Framework.getSystemClassLoader().loadClass(componentName);
if (clazz != null) {
performLaunchChildActivity(container,key,intent);
}
} catch (ClassNotFoundException e) {
Log.e("ActivityGroupDelegate",e.getCause().toString());
}
}
}
若bundleName为空,用system Classloader获取class,执行performLaunchChildActivity
若bundleName不为空,根据bundleName获取到BundleImpl,执行performLaunchChildActivity private void performLaunchChildActivity(ViewGroup container,String key,Intent intent ){
if(intent==null){
Log.e("ActivityGroupDelegate","intent is null stop performLaunchChildActivity");
return ;
}
mLocalActivityManager.startActivity(key,intent);
container.addView(
mLocalActivityManager.getActivity(key)
.getWindow().getDecorView(),
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
}
performLaunchChildActivity方法很简单,ActivityManager启动Activity,然后将视图添加到ViewGroup
atlas组件化比较彻底,和360的框架一样,建立了自己的组件的体系,并兼容源生的Activity组件。