詳解Android封裝一個全局的BaseActivity
1.前言
- 對于一個Android開發(fā)者來說,每一個頁面都繼承一個單獨的系統(tǒng)Activity,有時候會帶來很多不必要的困擾。比如:每一個頁面會有重復(fù)的代碼,閱讀起來麻煩;每一次寫新的頁面功能總要打開原來的頁面代碼拷貝一部分過來;有時候代碼調(diào)試排查問題也不方便等等。
- 如果你的項目里面沒有將Activity都繼承自一個自己封裝的BaseActivity、或者針對自己封裝的BaseActivity覺得還不夠完善的,這篇博客可能會對你有幫助!
2.特點
- 封裝:將所有Activity都用到的一部分代碼封裝到一個統(tǒng)一管理的Activity類(后面全部起名叫BaseActivity),然后由這個BaseActivity繼承自Android系統(tǒng)的AppCompatActivity(一般是這個)。
- 繼承:頁面上用到的Activity都繼承自我們的自己BaseActivity,BaseActivity封裝的方法在Activity內(nèi)直接調(diào)用。
3.代碼及說明
3.1.優(yōu)缺點
- 優(yōu)點:減少了代碼的重復(fù),提高了寫代碼的效率、以及提高了代碼的維護(hù)性
- 缺點:不要任何代碼都放在BaseActivity,那樣可能會導(dǎo)致BaseActivity過于臃腫,不利于代碼的閱讀和維護(hù),甚至出現(xiàn)App奔潰
下面會討論哪些代碼應(yīng)該放在BaseActivity里面,哪些需要謹(jǐn)慎
3.2.代碼
下面我貼一份我自己封裝的BaseActivity,在代碼中和代碼下面做了解釋:
public abstract class BaseActivity extends AppCompatActivity {
public Activity mActivity;
private Unbinder mUnbinder;
private static float sNoncompatDensity;
private static float sNoncompatScaledDensity;
private MaterialDialog mDialog;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
onAdjustLayout();
setContentView(setContentLayout());
//這里的是初始化綁定ButterKnife,在onDestory做了銷毀
mUnbinder = ButterKnife.bind(this);
this.mActivity = this;
//統(tǒng)一將一個activity添加到一個集合里面
AppManager.getInstance().addActivity(mActivity);
initToolBar();
initPresenter();
initData(savedInstanceState);
Log.e("app", this.getClass().getSimpleName() + "------onCreate");
}
@Override
protected void onStart() {
super.onStart();
Log.e("app", this.getClass().getSimpleName() + "------onStart");
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.e("app", this.getClass().getSimpleName() + "------onRestoreInstanceState");
}
@Override
protected void onRestart() {
super.onRestart();
Log.e("app", this.getClass().getSimpleName() + "------onRestart");
}
@Override
protected void onResume() {
super.onResume();
Log.e("app", this.getClass().getSimpleName() + "------onResume");
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.e("app", this.getClass().getSimpleName() + "------onSaveInstanceState");
}
@Override
protected void onPause() {
super.onPause();
Log.e("app", this.getClass().getSimpleName() + "------onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.e("app", this.getClass().getSimpleName() + "------onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
onDestroyActivity();
mUnbinder.unbind();
Log.e("app", this.getClass().getSimpleName() + "------onDestroy");
}
/**
* 顯示一個Fragment
*/
public void showFragment(Fragment fragment) {
if (fragment != null && fragment.isHidden()) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.show(fragment);
fragmentTransaction.commit();
}
}
/**
* 隱藏一個Fragment
*/
public void hideFragment(Fragment fragment) {
if (fragment != null && !fragment.isHidden()) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.hide(fragment);
fragmentTransaction.commit();
}
}
//這是一個設(shè)置toolbar標(biāo)題欄的方法,ToolBarOptions類主要是持有一些id
public void setToolBar(int toolBarId, ToolBarOptions options) {
Toolbar toolbar = findViewById(toolBarId);
if (options.titleId != 0) {
toolbar.setTitle(options.titleId);
} else {
toolbar.setTitle("");
}
if (!TextUtils.isEmpty(options.titleString)) {
toolbar.setTitle(options.titleString);
}
if (options.backgroundColor != 0) {
toolbar.setBackgroundResource(options.backgroundColor);
}
if (options.logoId != 0) {
toolbar.setLogo(options.logoId);
}
setSupportActionBar(toolbar);
if (options.isNeedNavigate) {
toolbar.setNavigationIcon(options.navigateId);
toolbar.setContentInsetStartWithNavigation(0);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!AppUtils.isNotFastClick()) {
return;
}
onNavigateUpClicked();
}
});
}
}
//子類直接調(diào)用展示toast
public void showToast(String s) {
ToastUtil.showToast(this, s);
}
//給子類提供一個獲取activity對象的方式
public Activity getActivity() {
return this;
}
//一個彈窗l(fā)oading庫 github地址:
//implementation 'com.afollestad.material-dialogs:core:0.9.6.0'
public void showLoading(String loadDesc) {
mDialog = new MaterialDialog.Builder(this)
.progress(true, -1)
.content(loadDesc)
.canceledOnTouchOutside(false)
.cancelable(false)
.show();
}
public void showLoading(int resId) {
mDialog = new MaterialDialog.Builder(this)
.progress(true, -1)
.content(getString(resId))
.canceledOnTouchOutside(false)
.cancelable(false)
.show();
}
public void showLoading() {
mDialog = new MaterialDialog.Builder(this)
.progress(true, -1)
.content("加載中...")
.canceledOnTouchOutside(false)
.cancelable(false)
.show();
}
public void hideLoading() {
if (mDialog != null) {
mDialog.dismiss();
}
}
//這里是退出app相關(guān)的邏輯,可以根據(jù)自己的退出做具體的處理
public void exitLogin() {
SharedPreferenceUtils.getInstance(mActivity).put(Constant.KEY_LOGIN_TOKEN, "");
if (mDialog != null) {
mDialog.hide();
mDialog = null;
}
mDialog = new MaterialDialog.Builder(this)
.canceledOnTouchOutside(false)
.title("提示")
.content("賬號已在其他地方登錄,請退出重新登錄!")
.positiveText("確定")
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
AppManager.getInstance().finishAllActivity();
Intent intent = new Intent(mActivity, LoginActivity.class);
startActivity(intent);
finish();
}
}).show();
}
private void onNavigateUpClicked() {
onBackPressed();
}
//開始contentLayout前調(diào)整布局(子類若有需要可以單獨復(fù)寫)
public void onAdjustLayout() {
}
//下面這5個方法是子類必須實現(xiàn)的,分別是layout布局、toolbar、mvp的persenter初始化、
//onCreate內(nèi)的initData、以及頁面銷毀的onDestroyActivity(可以根據(jù)自己的需要添加)
public abstract int setContentLayout();
public abstract void initToolBar();
public abstract void initPresenter();
public abstract void initData(Bundle savedInstanceState);
public abstract void onDestroyActivity();
}
3.3.注意點
- 在BaseActivity的每個生命周期內(nèi)都有l(wèi)og日志,這里是方便觀察執(zhí)行到activity的哪個生命周期,logcat也可以簡單封裝一下,統(tǒng)一控制日志是否打印。
- BaseActivity并不適合每一個頁面的Activity,比如進(jìn)入應(yīng)用的閃屏頁面,就可以考慮不繼承BaseActivity,因為這個頁面通常不需要寫太多代碼?;蛘哌€有其他特殊的業(yè)務(wù)場景下。
- 需要注意一個Dialog彈窗問題,在BaseActivity里面,每次show一個dialog的時候我都是創(chuàng)建一個新的對象,那么就要注意dialog在未關(guān)閉之前不能再去show,否則可能會導(dǎo)致dialog出現(xiàn)異常。但是不要在onDestory方法里面去隱藏dialog彈窗,因為在A頁面進(jìn)入B頁面的時候,會先執(zhí)行到B頁面生命周期的onCreate、onStart、onResume三個方法,然后再執(zhí)行A頁面的onStop可能還有onDestory方法,所以等B頁面加載完成再去銷毀A頁面是錯誤的。
- 有時候為了方便可能有人會把請求Android中權(quán)限檢測的方法放在BaseActivity里面,這樣并不是特別合適,因為所有繼承自BaseActivity的頁面都會去檢測權(quán)限,這樣會導(dǎo)致用戶體驗差,所以建議用到權(quán)限的地方再去請求,最好自己封裝一個工具類,用起來方便一點。
- BaseActivity的封裝并不強(qiáng)求子類必須實現(xiàn)activity生命周期相關(guān)的方法,除了幾個抽象方法(我認(rèn)為子類需要復(fù)寫的,可以根據(jù)業(yè)務(wù)自己定),必要的話可以自己復(fù)寫。
4.總結(jié)
不是很復(fù)雜,寫的也比較詳細(xì),也基本適用于絕大部分的場景。可能還有其他需要注意的細(xì)節(jié)回頭想起來再補(bǔ)上。
以上就是詳解Android封裝一個全局的BaseActivity的詳細(xì)內(nèi)容,更多關(guān)于Android封裝BaseActivity的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android列表實現(xiàn)(3)_自定義列表適配器思路及實現(xiàn)代碼
Android 自定義列表適配器會提供很多的便利;下面的例子為使用自定義的列表適配器來顯示列表,感興趣的朋友可以研究下2012-12-12
Android實現(xiàn)Toast提示框圖文并存的方法
這篇文章主要介紹了Android實現(xiàn)Toast提示框圖文并存的方法,實例分析了Toast提示框的參數(shù)設(shè)置及圖文調(diào)用的相關(guān)技巧,需要的朋友可以參考下2016-01-01
Android開發(fā)之Sqliteopenhelper用法實例分析
這篇文章主要介紹了Android開發(fā)之Sqliteopenhelper用法,實例分析了SQLiteOpenHelper類操作數(shù)據(jù)庫的相關(guān)技巧,需要的朋友可以參考下2015-05-05
Android利用Hero實現(xiàn)列表與詳情頁無縫切換動畫
本文我們將利用Hero動畫實現(xiàn)一個簡單案例:實現(xiàn)列表與詳情頁無縫切換動畫,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以動手嘗試一下2022-06-06
Android使用文件進(jìn)行數(shù)據(jù)存儲的方法
這篇文章主要介紹了Android使用文件進(jìn)行數(shù)據(jù)存儲的方法,較為詳細(xì)的分析了Android基于文件實現(xiàn)數(shù)據(jù)存儲所涉及的相關(guān)概念與使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-09-09

