最近項目忙完了漓柑,開始有一定的時間優(yōu)化自己的架構(gòu)教硫,我一直寫代碼都有一種感覺,每次寫完一個項目辆布,然后開始優(yōu)化瞬矩,等優(yōu)化完,再看看自己寫的代碼锋玲,就發(fā)現(xiàn)我封裝的框架真的有點辣雞景用,然后又開始寫個基礎(chǔ)的lib進(jìn)行架構(gòu)的優(yōu)化
簡介
關(guān)于Android程序的構(gòu)架,主要有MVC,MVP和MVVM惭蹂。MVC相對于較為落后伞插,耦合度太高、職責(zé)不明確盾碗;MVVM其實就是在mvp的基礎(chǔ)上采用DataBind媚污,普及性不如MVP,況且Google官方提供了Sample代碼來展示MVP模式的用法廷雅,所以目前大部分項目采用的還是MVP耗美,當(dāng)然根據(jù)項目的情況以及項目的大小來采用合適的結(jié)構(gòu)才是合理的。
Kotlin是由JetBrains創(chuàng)建的基于JVM的編程語言航缀,IntelliJ正是JetBrains的杰作商架,而Android Studio是基于IntelliJ修改而來的。Kotlin是一門包含很多函數(shù)式編程思想的面向?qū)ο缶幊陶Z言芥玉。Kotlin生來就是為了彌補Java缺失的現(xiàn)代語言的特性蛇摸,并極大的簡化了代碼,使得開發(fā)者可以編寫盡量少的樣板代碼灿巧。所以目前來說kotlin的Android開發(fā)者中的普及率越來越大赶袄,這應(yīng)該是一個很大的趨勢诬烹。所以學(xué)習(xí)和使用kotlin是一個Android開發(fā)者必備的技能
Retrofit: Retrofit是Square 公司開發(fā)的一款正對Android 網(wǎng)絡(luò)請求的框架。底層基于OkHttp 實現(xiàn)弃鸦。
RxJava:RxJava 在 GitHub 主頁上的自我介紹是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一個在 Java VM 上使用可觀測的序列來組成異步的绞吁、基于事件的程序的庫)。這就是 RxJava 唬格,概括得非常精準(zhǔn)家破。總之就是讓異步操作變得非常簡單购岗。
各自的職責(zé):Retrofit 負(fù)責(zé)請求的數(shù)據(jù)和請求的結(jié)果汰聋,使用接口的方式呈現(xiàn),OkHttp 負(fù)責(zé)請求的過程喊积,RxJava 負(fù)責(zé)異步烹困,各種線程之間的切換。
RxJava + Retrofit 已成為當(dāng)前Android 網(wǎng)絡(luò)請求最流行的方式乾吻。
MVP具體實現(xiàn)
首先既然采用了MVP髓梅,肯定必不可少具M(jìn)層,V層绎签,P層的基礎(chǔ)接口枯饿,封裝一個公共的操作,看一下具體實現(xiàn)
我把頂級的接口分成了兩層诡必,這樣有利于在寫泛型的時候沒那么麻煩
先看下目錄結(jié)構(gòu)
- 頂級接口
第一層
interface ITopView : LifecycleOwner {
fun getCtx(): Context?
fun inited()
fun finish(resultCode: Int = Activity.RESULT_CANCELED)
fun showLoading(@NotNull msg: String)
fun showLoading(@StringRes srtResId: Int)
fun dismissLoading()
fun showToast(@StringRes srtResId: Int)
fun showToast(@NotNull message: String)
}
interface ITopPresenter : LifecycleObserver {
fun attachView(view: ITopView)
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun detachView()
}
interface ITopModel {
fun onDetach()
}
第二層
interface IView<P : ITopPresenter> : ITopView {
var mPresenter: P
override fun inited() {
mPresenter.attachView(this)
}
}
interface IPresenter<V : ITopView, M : IModel> : ITopPresenter {
var mView: V?
var mModel: M?
fun getContext() = mView?.getCtx()
@Suppress("UNCHECKED_CAST")
override fun attachView(view: ITopView) {
mView = view as V
mView?.lifecycle?.addObserver(this)
}
override fun detachView() {
mModel?.onDetach()
mModel = null
mView = null
}
//判斷是否初始化View
private val isViewAttached: Boolean
get() = mView != null
fun checkViewAttached() {
if (!isViewAttached) throw MvpViewNotAttachedException()
}
private class MvpViewNotAttachedException internal constructor() : RuntimeException("Please call IPresenter.attachView(IBaseView) before" + " requesting data to the IPresenter")
}
interface IModel : ITopModel {
val mDisposablePool: CompositeDisposable
fun addDisposable(disposable: Disposable) {
mDisposablePool.add(disposable)
}
override fun onDetach() {
if (!mDisposablePool.isDisposed) {
mDisposablePool.clear()
}
}
}
還有額外的一個列表的V層奢方,主要是對列表界面數(shù)據(jù)統(tǒng)一處理
interface IListView<P : ITopPresenter> :IView<P>{
val mRecyclerView: RecyclerView?
val mStateView: IStateView?
val mRefreshLayout:SmartRefreshLayout
fun loadMoreFail(isRefresh: Boolean)
}
然后M的基類
open class BaseModelKt {
val mDisposablePool: CompositeDisposable by lazy { CompositeDisposable() }
}
然后P的基類
open class BasePresenterKt<V : ITopView> {
var mView: V? = null
}
-
Activity和Fragment的封裝
首先的MVPActivity的實現(xiàn)
abstract class BaseMvpActivity<V : ITopView, P : ITopPresenter> : BaseActivity(), IView<P> {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
inited()
}
override fun getCtx() = this
override fun showLoading(msg: String) {
progressDialog?.showProgressDialogWithText(msg)
}
override fun finish(resultCode: Int) {
finish()
}
override fun showLoading(srtResId: Int) {
progressDialog?.showProgressDialogWithText(resources.getString(srtResId))
}
override fun dismissLoading() {
progressDialog?.dismissProgressDialog()
}
override fun showToast(message: String) {
showToastBottom(message)
}
override fun showToast(srtResId: Int) {
showToast(resources.getString(srtResId))
}
}
然后MVPFragment的實現(xiàn)
abstract class BaseMvpFragment<V : ITopView, P : ITopPresenter> : BaseFragment(), IView<P> {
override fun getCtx() = context
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
inited()
}
override fun finish(resultCode: Int) {
}
override fun showToast(message: String) {
showToastBottom(message)
}
override fun showToast(srtResId: Int) {
showToast(resources.getString(srtResId))
}
override fun showLoading(msg: String) {
showProgressDialog(msg)
}
override fun showLoading(srtResId: Int) {
showProgressDialog(resources.getString(srtResId))
}
override fun dismissLoading() {
dismissProgressDialog()
}
}
然后封裝一個帶toolBar的MVPTitleActivity,它是MVPActivity的子類
abstract class BaseMvpTitleActivity<V : ITopView, P : ITopPresenter> : BaseMvpActivity<V, P>() {
private var rightMenuTexts: String? = null
private var rightMenuIcons: Int? = null
private var titleTv: TextView? = null
@LayoutRes
protected abstract fun childView(): Int
override fun getContentView() = R.layout.activtiy_base_title
override fun initView() {
val container = this.findViewById<FrameLayout>(R.id.base_container)
container.addView(layoutInflater.inflate(childView(), null))
val toolbar = this.findViewById<Toolbar>(R.id.base_toolbar)
titleTv = this.findViewById(R.id.base_title_tv)
toolbar.title = ""
setSupportActionBar(toolbar)
if (hasBackIcon()) {
toolbar.setNavigationIcon(R.drawable.return_icon)
toolbar.setNavigationOnClickListener { finish() }
}
}
open fun hasBackIcon() = true
override fun onCreateOptionsMenu(menu: Menu): Boolean {
rightMenuIcons?.let {
val item = menu.add(0, 0, 0, "")
item.icon = ContextCompat.getDrawable(this, it)
item.setShowAsAction(Menu.FLAG_ALWAYS_PERFORM_CLOSE)
}
rightMenuTexts?.let {
val item = menu.add(0, 0, 0, "")
item.title = it
item.setShowAsAction(Menu.FLAG_ALWAYS_PERFORM_CLOSE)
}
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
onRightMenuClick(item.itemId)
return false
}
/**
* 設(shè)置toolbar右邊的文字
*/
fun setRightMenuTexts(rightMenuText: String) {
this.rightMenuTexts = rightMenuText
}
/**
* 設(shè)置toolbar右邊的icon
*/
fun setRightMenuIcons(@DrawableRes rightIconResId: Int) {
this.rightMenuIcons = rightIconResId
}
/**
* 當(dāng)toolbar右邊的icon爸舒,被點擊蟋字,數(shù)據(jù)0,1,2,3
*/
open fun onRightMenuClick(itemId: Int) {
}
/**
* 設(shè)置中間的title
*/
protected fun setActivityTitle(@StringRes strResId: Int) {
titleTv?.setText(strResId)
}
protected fun setActivityTitle(text: String) {
titleTv?.text = text
}
/**
* 設(shè)置中間title的顏色
*/
fun setActivityTitleColor(@ColorRes colorId: Int) {
titleTv?.setTextColor(resources.getColor(colorId))
}
}
這樣基本的封裝基本就結(jié)束了
但是還可以對列表進(jìn)行封裝,封裝了視圖狀態(tài)扭勉,下拉刷新
來看看MVPListActivity,MVPListFragment,MvpTitleListAcitivty的封裝
abstract class BaseMvpListActivity<V : ITopView, P : ITopPresenter> : BaseMvpActivity<V, P>(), IListView<P> {
override fun getContentView() = R.layout.layout_list
override val mStateView: IStateView by lazy { list_sv }
override val mRecyclerView: RecyclerView by lazy { list_rv }
override val mRefreshLayout: SmartRefreshLayout by lazy { refreshLayout }
override fun initView() {
//設(shè)置列表背景色
list_rv.setBackgroundColor(ContextCompat.getColor(this, setRecyclerViewBgColor))
//重試
list_sv.onRetry = { onRetry() }
//刷新
refreshLayout.setOnRefreshListener { onRefresh() }
//設(shè)置下拉刷新是否可用
refreshLayout.isEnabled = setRefreshEnable
}
abstract fun onRefresh()
abstract fun onRetry()
open val setRecyclerViewBgColor = R.color.white
open val setRefreshEnable = true
}
abstract class BaseMvpListFragment<V : ITopView, P : ITopPresenter> : BaseMvpFragment<V, P>(), IListView<P> {
override fun getContentView() = R.layout.layout_list
override val mStateView: IStateView by lazy { list_sv }
override val mRecyclerView: RecyclerView by lazy { list_rv }
override val mRefreshLayout: SmartRefreshLayout by lazy { refreshLayout }
override fun initData() {
//設(shè)置背景色
context?.let { list_rv.setBackgroundColor(ContextCompat.getColor(it, setRecyclerViewBgColor)) }
//重試
list_sv.onRetry = { onRetry() }
//刷新
refreshLayout.setOnRefreshListener { onRefresh() }
//設(shè)置下拉刷新是否可用
refreshLayout.isEnabled = setRefreshEnable
}
abstract fun onRefresh()
abstract fun onRetry()
open val setRecyclerViewBgColor = R.color.white
open val setRefreshEnable = true
}
abstract class BaseMvpTitleListActivity<V : ITopView, P : ITopPresenter> : BaseMvpTitleActivity<V, P>(), IListView<P> {
override fun childView()= R.layout.layout_list
override val mStateView: IStateView by lazy { list_sv }
override val mRecyclerView: RecyclerView by lazy { list_rv }
override val mRefreshLayout: SmartRefreshLayout by lazy { refreshLayout }
override fun initView() {
super.initView()
//設(shè)置背景色
list_rv.setBackgroundColor(ContextCompat.getColor(this, setRecyclerViewBgColor))
//重試
list_sv.onRetry = { onRetry() }
//刷新
refreshLayout.setOnRefreshListener { onRefresh() }
//設(shè)置下拉刷新是否可用
refreshLayout.isEnabled = setRefreshEnable
}
abstract fun onRefresh()
abstract fun onRetry()
open val setRecyclerViewBgColor = R.color.white
open val setRefreshEnable = true
}
這樣MVP的大致架構(gòu)基本已經(jīng)封裝好了
網(wǎng)絡(luò)框架的具體實現(xiàn)
- retrofit的封裝
這個apiService我才用泛型回調(diào)鹊奖,這樣可以根據(jù)不同的模塊創(chuàng)建不同的retrofit工廠類,這個也有利用模塊化開發(fā)
abstract class RetrofitFactory<T> {
private val time_out: Long = 15//超時時間
var apiService: T
init {
val httpClient = OkHttpClient.Builder()
.addInterceptor { chain ->
val builder = chain.request().newBuilder()
// 添加請求頭header
if (getToken().isNotEmpty()) {
builder.header("userToken", getToken())
}
val build = builder.build()
chain.proceed(build)
}
.addInterceptor(HttpLoggingInterceptor(HttpLoggingInterceptor.Logger { message ->
if (message.contains("{")||message.contains("=")||message.contains("http")
||message.contains("userToken")){
Logger.e("${message}")
}
}).setLevel(HttpLoggingInterceptor.Level.BODY))//設(shè)置打印得日志內(nèi)容
.connectTimeout(time_out, TimeUnit.SECONDS)
.readTimeout(time_out, TimeUnit.SECONDS)
.build()
apiService = Retrofit.Builder()
.baseUrl(URLConstant.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(buildGson())) // 添加Gson轉(zhuǎn)換器
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 添加Retrofit到RxJava的轉(zhuǎn)換器
.client(httpClient)
.build()
.create(getApiService())
}
abstract fun getApiService(): Class<T>
abstract fun getToken(): String
private fun buildGson(): Gson {
return GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.create()
}
fun getService(): T {
return apiService
}
}
- Rxjava+Retrofit的封裝
這個部分是網(wǎng)絡(luò)請求的部分剖效,我是封裝在kotlin的拓展方法里面嫉入,這樣就可以使用lambda表達(dá)式進(jìn)行網(wǎng)絡(luò)請求焰盗,代碼量賊少璧尸,用起來賊舒服,一行代碼一個請求
具體使用例子
PersonRetrofit.apiService.getIdentityCode(phone).mySubscribe(view, "正在獲取驗證碼...") {
view.getCodeSuccess()
}
是不是賊簡單熬拒,賊方便爷光,這個得感謝我的同學(xué)大強哥,這招都是他教我的澎粟,把lambda用到極致蛀序;
再來看看kotlin的拓展內(nèi)部實現(xiàn)欢瞪;
fun <T : BaseBean, P : ITopPresenter> Observable<T>.mSubscribe(
iBaseView: IView<P>? = null
, iModel: IModel? = null
, msg: String = ""
, onSuccess: (T) -> Unit) {
this.compose(SchedulerUtils.ioToMain())
.subscribe(object : Observer<T> {
override fun onComplete() {
iBaseView?.dismissLoading()
}
override fun onSubscribe(d: Disposable) {
iModel?.addDisposable(d)
iBaseView?.showLoading(if (msg.isEmpty()) "請求中..." else msg)
if (!NetworkUtils.isConnected()) {
showToastBottom("連接失敗,請檢查網(wǎng)絡(luò)狀況!")
onComplete()
}
}
override fun onNext(t: T) {
if (t.code == CodeStatus.SUCCESS) {
onSuccess.invoke(t)
} else if (t.code == CodeStatus.LOGIN_OUT) {//重新登錄
// val currentActivity = ActivityUtils.currentActivity()
// UserManager.getInstance().clear()
// EMClient.getInstance().logout(true)
// showToastBottom("登錄過期,請重新登錄")
// val intent = Intent(currentActivity, LoginActivity::class.java)
// intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
// currentActivity?.startActivity(intent)
} else {
if (!t.msg.isNullOrEmpty()) {
t.msg?.let { showToastBottom(it) }
} else {
showToastBottom("請求失敗")
}
}
}
override fun onError(e: Throwable) {
iBaseView?.dismissLoading()
if (e is SocketTimeoutException || e is ConnectException) {
showToastBottom("連接失敗,請檢查網(wǎng)絡(luò)狀況!")
} else if (e is JsonParseException) {
showToastBottom("數(shù)據(jù)解析失敗")
} else {
showToastBottom("請求失敗")
}
}
})
}
fun <T : BaseBean, P : ITopPresenter> Observable<T>.listSubcribe(
iBaseView: IListView<P>? = null
, iModel: IModel? = null
, isRefresh: Boolean
, isLoadMore: Boolean
, onSuccess: (T) -> Unit) {
this.compose(SchedulerUtils.ioToMain())
.subscribe(object : Observer<T> {
override fun onComplete() {}
override fun onSubscribe(d: Disposable) {
iModel?.addDisposable(d)
if (!isRefresh && !isLoadMore) {
iBaseView?.mStateView?.showLoading()
}
}
override fun onNext(t: T) {
if (t.code == CodeStatus.SUCCESS) {
iBaseView?.mStateView?.showSuccess()
onSuccess.invoke(t)
} else if (t.code == CodeStatus.LOGIN_OUT) {//重新登錄
// UserManager.getInstance().clear()
// showToastBottom("登錄過期徐裸,請重新登錄")
// EMClient.getInstance().logout(true)
// val intent = Intent(currentActivity, LoginActivity::class.java)
// intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
// currentActivity.startActivity(intent)
} else {
iBaseView?.mStateView?.showError()
}
}
override fun onError(e: Throwable) {
if (!isLoadMore) {
iBaseView?.mStateView?.showError()
} else {
iBaseView?.loadMoreFail(isRefresh)
}
}
})
}
配合插件使用遣鼓,快速開發(fā)必備
這里我推薦一個我同學(xué)的插件,結(jié)合這種lib使用賊方便
插件的名字叫MvpAutoCodePlus重贺,github地址 插件地址骑祟,,气笙,這個low比名字還是我?guī)退〉摹?br>
具體使用
這樣就生成了次企,真的很方便
最后我寫了一個demo放在github上面 項目地址
原文地址
歡迎大家掃描關(guān)注作者公眾號,長期推送Android技術(shù)干貨潜圃,感謝大家支持: