組件化與插件化相同之處: 是將一個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資源文件不能重複