组件化与插件化相同之处: 是将一个APP拆分为多个模块去开发.
插件化: 如果我们把一个APP分为多个模块来开发的化,我们最终打包的时候永远只需要打包当前的主apk就可以,其它的功能模块可以不管,它们可以当作是一个独立的apk来单独运行,甚至单独发布,单独给用户用.当我们项目达到一定体积时,一般都会用插件化开发
不仅仅是apk也可以是libralaly(dex)
好处: 1,提高编译速度,节省开发时间.
2,业务模块解藕,便于后期维护.
3,团队开发便利.
4,动态更新插件,按需下载模块.
原理: 当我们点击相应的模块时,就会从服务器下载相应的插件APK或者Library,存放到本地指定目录下, 下载完之后我们就会去动态加载这个apk里面的 资源对象(我们需要在apk里面用到这些资源) 以及 类加载器(第三方插件apk是不具备生命周期以及没有上下文),通过插件apk的类加载器获取到插件化apk中的Activity的类对象,然后进行跳转.
主要涉及到的类有:
DexClassLoader:加载第三方插件apk的类加载器
Context:APP的上下文
PackageInfo:插件apk的包信息类,因为我们需要根据包信息名字获取Activity的名字
Resources:加载第三方插件apk的资源对象
代码好下:
File fileDir = mContext.getDir("odex", Context.MODE_PRIVATE);
//只有一个classloader optimizedDire:当前应用的私有存储路径
mClassLoader = new DexClassLoader(path, fileDir.getAbsolutePath(), null, mContext.getClassLoader());
//获取到包管理器(packageManager) 整个系统有只有一个
PackageManager packageManager = mContext.getPackageManager();
//通过包管理器获取到传进来的这个路径下的dex文件下的包信息类
mPackageInfo = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
try {
AssetManager assetManager = AssetManager.class.newInstance();
//反射获取方法
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, path);
mResources = new Resources(assetManager, mContext.getResources().getDisplayMetrics(), mContext.getResources().getConfiguration());
} catch (Exception e) {
e.printStackTrace();
}
因为是加载第三方插件apk,而第三方插件apk是没有生命周期的,所以我们需要帮其注入生命周期,这时就需要一个代码的Activity我们这命名为ProxyActiviy ,所有的绑定生命周期的逻辑都是在ProxyActiviy操作.
核心代码如下:
private PluginInterface mPluginInterface;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//获取到真正的目的地的Activity的名字
String activityName = getIntent().getStringExtra("activityName");
try {
Class<?> aClass = PluginManager.getInstance().getClassLoader().loadClass(activityName);
//实例化插件化中的Activity
Object activity = aClass.newInstance();
//判断这个对象是否按照我们的标准来
if (activity instanceof PluginInterface) {
mPluginInterface = (PluginInterface) activity;
Bundle bundle = new Bundle();
mPluginInterface.attach(this);
mPluginInterface.onCreate(bundle);
}
} catch (Exception e) {
e.printStackTrace();
}
}
从上面代码可以看出,我们创建了一个第三方插件里面的Activity类,然后将执行其attacth,和onCreate也就是将代码Activity的一下文传进去,然后再执行其的onCreate,就是把第三方插件里面的Activity当作一个普通的类来使用,我们再来看一个第三方Activity里的代码
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mTextView = findViewById(R.id.taskview);
Button button = findViewById(R.id.btn);
mTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mActivity, "我开始要工作了", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(mActivity, SecendActivity.class);
startActivity(intent);
}
});
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setting(v);
}
});
}
再把BaseActivit代码贴出来
public class BaseActivity extends AppCompatActivity implements PluginInterface {
protected Activity mActivity;
@Override
public void attach(Activity alipayActivity) {
mActivity = alipayActivity;
}
public void setContentView(View view) {
if (mActivity == null) {
super.setContentView(view);
} else {
mActivity.setContentView(view);
}
}
@Override
public void setContentView(int layoutResID) {
mActivity.setContentView(layoutResID);
}
@Override
public Intent getIntent() {
return mActivity.getIntent();
}
@Override
public ClassLoader getClassLoader() {
return mActivity.getClassLoader();
}
@Override
public <T extends View> T findViewById(int id) {
return mActivity.findViewById(id);
}
@NonNull
@Override
public LayoutInflater getLayoutInflater() {
return mActivity.getLayoutInflater();
}
@Override
public void startActivity(Intent intent) {
Intent m = new Intent();
m.putExtra("activityName", intent.getComponent().getClassName());
mActivity.startActivity(m);
}
@Override
public ApplicationInfo getApplicationInfo() {
return mActivity.getApplicationInfo();
}
@Override
public Window getWindow() {
return mActivity.getWindow();
}
@Override
public WindowManager getWindowManager() {
return mActivity.getWindowManager();
}
@SuppressLint("MissingSuperCall")
@Override
public void onCreate(Bundle savedInstanceState) {
}
@SuppressLint("MissingSuperCall")
@Override
public void onStart() {
}
@SuppressLint("MissingSuperCall")
@Override
public void onResume() {
}
@SuppressLint("MissingSuperCall")
@Override
public void onPause() {
}
@SuppressLint("MissingSuperCall")
@Override
public void onStop() {
}
@Override
public void onDestory() {
}
@SuppressLint("MissingSuperCall")
@Override
public void onSaveInstanceState(Bundle outState) {
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return false;
}
@Override
public void onBackPressed() {
}
}
从上面二大段代码可以得出,在第三方Actitivy里面里的setContentView,其实调用的就是代码类ProxyActivity执行setConttentView,里面所有涉及到的操作上下文的操作都是代码类来完成的.
组件化:将一个app分为多个模块开发之后,最终还是会打包成一个apk.解决不了包大小问题(按照功能模块单独开发)
为什么选择组件化: 1,单一模块不行于团队开发
2,单一模块对项目任一修改都要编译整个工程
3,单一模块功能复用不是很强(比如一个登陆,多个APP都可以用,整个模块移走)
4,单一模块之间业务耦合非常严重
组件化解决的问题: 1,提高编译速度,节省开发时间
2,业务模块解耦,便于后期维护
3,团队开发方便
4 使得功能复用更简单
组件化框架注意点: 1,类名and资源文件不能重复