-
组件化?
组件化想要解决的问题:
1.实际业务变化非常快,但是工程之前的业务模块耦合度太高,牵一发而动全身.
2.对工程所做的任何修改都必须要编译整个工程
3.功能测试和系统测试每次都要进行.
4.团队协同开发存在较多的冲突.不得不花费更多的时间去沟通和协调,并且在
5.开发过程中,任何一位成员没办法专注于自己的功能点,影响开发效率.
6.不能灵活的对工程进行配置和组装.比如今天产品经理说加上这个功能,明天又说去掉,后天在加上.
(组件化 各个组件之间互相依赖,没办法并发开发)。
image.png
image.png
挂载到组件总线上的业务组件,都可以实现双向通信.而通信协议和HTTP通信协议类似,即基于URL的方式进行.
-
插件化?
Android文件在编译期会通过javac命令编译成.class文件,最后再把所有的.class文件编译成.dex文件放在.apk包里面。那么动态加载就是在运行时把插件apk直接加载到classloader里面的技术。
关于代码加载,系统提供了DexClassLoader来加载插件代码。开发者可以对每一个插件分配一个DexClassLoader(这是目前最常见的一种方式),也可以动态得把插件加载到当前运行环境的classloader中。
相对于组件化开发主要要解决的问题:
-
插件化的解决了什么问题?
1.宿主和插件分开编译
2.并发开发
3.动态更新插件
4.按需下载模块
5.方法数或变量数爆棚 -
实现插件化加载:
插件化加载 就是加载一个APK,将父类APP(宿主)的界面显示子类APP的界面,类似一个空的Activity给想要显示的其他子App的Activity预留的,实现逻辑:获取界面的所有包含Acivity的方法,并重写,因为子类APP未被安装所以没有Acitivity的引用。(宿主定义规则,子类APP去实现)
宿主APP重写ClassLoader为DexClassLoader和Resouce(AssestManager)和PackageManager去读取外部APK资源。在跳转一个显示子类APP界面的Activity,反射成Activity对象。然后做其他操作。
//Activitiy必须重写的规则接口类
public interface PayInterfaceActivity {
public void attach(Activity proxyActivity);
/**
* 生命周期
* @param savedInstanceState
*/
public void onCreate(Bundle savedInstanceState);
public void onStart();
public void onResume();
public void onPause();
public void onStop();
public void onDestroy();
public void onSaveInstanceState(Bundle outState);
public boolean onTouchEvent(MotionEvent event);
public void onBackPressed();
}
这个类用来读取外部资源的
public class PluginManager {
private PackageInfo packageInfo;
private Resources resources;
private Context context;
private DexClassLoader dexClassLoader;
private static final PluginManager ourInstance = new PluginManager();
public static PluginManager getInstance() {
return ourInstance;
}
private PluginManager() {
}
public void setContext(Context context) {
this.context = context;
}
public void loadPath(Context context) {
//获取APK路径
File filesDir = context.getDir("plugin", Context.MODE_PRIVATE);
String name = "pluginb.apk";
String path = new File(filesDir, name).getAbsolutePath();
//pakckageInfo是用来获取APK的包的activity
PackageManager packageManager=context.getPackageManager();
packageInfo=packageManager.getPackageArchiveInfo(path,PackageManager.GET_ACTIVITIES);
// activity 实例化dexClassLoader 用来加载外部布局的
File dexOutFile = context.getDir("dex", Context.MODE_PRIVATE);
dexClassLoader = new DexClassLoader(path, dexOutFile.getAbsolutePath()
, null, context.getClassLoader());
// resource 用来加载外部资源的
try {
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath=AssetManager.class.getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, path);
resources = new Resources(assetManager, context.getResources().getDisplayMetrics(), context.getResources().getConfiguration());
} catch ( Exception e) {
e.printStackTrace();
}
}
public Resources getResources() {
return resources;
}
public DexClassLoader getDexClassLoader() {
return dexClassLoader;
}
public PackageInfo getPackageInfo() {
return packageInfo;
}
}
加载插件的代码
private void loadPlugin() {
File filesDir = this.getDir("plugin", Context.MODE_PRIVATE);
String name = "pluginb.apk";
String filePath = new File(filesDir, name).getAbsolutePath();
File file = new File(filePath);
if (file.exists()) {
file.delete();
}
InputStream is = null;
FileOutputStream os = null;
try {
Log.i(TAG, "加载插件 " + new File(Environment.getExternalStorageDirectory(), name).getAbsolutePath());
is = new FileInputStream(new File(Environment.getExternalStorageDirectory(), name));
os = new FileOutputStream(filePath);
int len = 0;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
File f = new File(filePath);
if (f.exists()) {
Toast.makeText(this, "dex overwrite", Toast.LENGTH_SHORT).show();
}
PluginManager.getInstance().loadPath(this);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
os.close();
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
子类必须做的事情,将所包含Activity的引用方法,重写成使用父类Activity的引用
public class BaseActivity extends Activity implements PayInterfaceActivity {
protected Activity that;
@Override
public void attach(Activity proxyActivity) {
this.that = proxyActivity;
}
@Override
public void setContentView(View view) {
if (that != null) {
that.setContentView(view);
}else {
super.setContentView(view);
}
}
@Override
public void setContentView(int layoutResID) {
that.setContentView(layoutResID);
}
@Override
public ComponentName startService(Intent service) {
Intent m = new Intent();
m.putExtra("serviceName", service.getComponent().getClassName());
return that.startService(m);
}
@Override
public View findViewById(int id) {
return that.findViewById(id);
}
@Override
public Intent getIntent() {
if(that!=null){
return that.getIntent();
}
return super.getIntent();
}
@Override
public ClassLoader getClassLoader() {
return that.getClassLoader();
}
@Override
public void startActivity(Intent intent) {
// ProxyActivity --->className
Intent m = new Intent();
m.putExtra("className", intent.getComponent().getClassName());
that.startActivity(m);
}
@NonNull
@Override
public LayoutInflater getLayoutInflater() {
return that.getLayoutInflater();
}
@Override
public ApplicationInfo getApplicationInfo() {
return that.getApplicationInfo();
}
@Override
public Window getWindow() {
return that.getWindow();
}
@Override
public WindowManager getWindowManager() {
return that.getWindowManager();
}
@Override
public void onCreate(Bundle savedInstanceState) {
}
@Override
public void onStart() {
}
@Override
public void onResume() {
}
@Override
public void onPause() {
}
@Override
public void onStop() {
}
@Override
public void onDestroy() {
}
@Override
public void onSaveInstanceState(Bundle outState) {
}
接下来就是我们显示子界面的关键代码:
/**
* Created by Administrator on 2018/3/28.
* 举例 这个界面是要显示的插件界面
*/
public class ProxyActivity extends Activity {
// 需要加载淘票票的 类名
private String className;
PayInterfaceActivity payInterfaceActivity;
// com.dongnao.alvin.taopiaopiao.MainActivity
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
className = getIntent().getStringExtra("className");
// class 拿到类名 注意这里没办法是通过反射拿到全类名的因为 此插件未被打包至APK中
//想要加载 必须重写classLoader和getResurce 因为我们想要她能访问外部卡的ClassLoader和resourece不是对内而是对外
try {
Class activityClass = getClassLoader().loadClass(className);
Constructor constructor = activityClass.getConstructor(new Class[]{});
Object instance = constructor.newInstance(new Object[]{});
// 可以
payInterfaceActivity = (PayInterfaceActivity) instance;
payInterfaceActivity.attach(this);
Bundle bundle = new Bundle();
payInterfaceActivity.onCreate(bundle);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void startActivity(Intent intent) {
String className1 = intent.getStringExtra("className");
Intent intent1 = new Intent(this, ProxyActivity.class);
intent1.putExtra("className", className1);
super.startActivity(intent1);
}
@Override
public ComponentName startService(Intent service) {
String serviceName = service.getStringExtra("serviceName");
Intent intent1 = new Intent(this, ProxyService.class);
intent1.putExtra("serviceName", serviceName);
return super.startService(intent1);
}
@Override
public ClassLoader getClassLoader() {
return PluginManager.getInstance().getDexClassLoader();
}
@Override
public Resources getResources() {
return PluginManager.getInstance().getResources();
}
@Override
protected void onStart() {
super.onStart();
payInterfaceActivity.onStart();
}
@Override
protected void onDestroy() {
super.onDestroy();
payInterfaceActivity.onDestroy();
}
@Override
protected void onPause() {
super.onPause();
payInterfaceActivity.onPause();
}
OK,Demo地址:https://pan.baidu.com/s/1RNufM9C2MmmwhJdoCYgZow 密码:b4ak
网友评论