为什么写
主要是周末闲的。之前公司的代码谈不上架构一说,因为基本都是直接在activity中进行操作,不管是网络请求还是数据库的操作,有时候一个activity甚至能写到好几千行,维护起来真的是。。。那酸爽。
前言
安卓目前的架构无非那几种:MVC 、MVP、MVVM。M和V一直存在,只是后面的不同。都是老生常谈的东西了,这里也就不多赘述了。
最开始学习安卓的时候,使用的是HttpClient、HttpConnection,之后开始使用OKHttp。后来Retrofit出来了,但我一直感觉和OKHttp差不多,尤其是底层也是OKHttp,这更令我丧失了学习的动力和欲望。昨天和今天闲来无事,想着用一下试试吧,用了之后,配合着RxJava和MVP,以及Kotlin优秀的语法糖,写出来的代码简介易懂了不少,下面开始一步一步来,文章结尾会放出源码。
虽然并不是写的View,但还是看一眼实现的效果吧(界面太丑,别嫌弃):
开始
1、添加依赖
-
// Retrofit
-
implementation
'com.squareup.retrofit2:retrofit:2.5.0'
-
// Retrofit和jxjava关联
-
implementation
'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
-
// Retrofit使用Gson转换
-
implementation
'com.squareup.retrofit2:converter-gson:2.4.0'
-
// RxJava
-
implementation
'io.reactivex.rxjava2:rxjava:2.2.13'
-
// RxAndroid
-
implementation
'io.reactivex.rxjava2:rxandroid:2.1.1'
2、搭建MVP
MVP,Model、View、Presenter。
一个一个来,首先是View,为了高度抽象,搞成了接口,其中写了错误信息的回调以及显示和关闭加载框。
-
/**
-
* 定义通用的接口方法
-
*/
-
-
interface BaseView {
-
-
// 出错信息的回调
-
fun onError(result: String)
-
-
// 显示进度框
-
fun showProgressDialog()
-
-
// 关闭进度框
-
fun hideProgressDialog()
-
}
接下来是Presenter,其中进行了View和Presenter的绑定和解绑,以及为了减小开销在每次网络访问之前初始化时进行添加Disposable,解绑View时关闭。
-
/**
-
* @author jiang zhu on 2019/11/23
-
*/
-
abstract
class BasePresenter<V : BaseView> {
-
-
//将所有正在处理的Subscription都添加到CompositeSubscription中。统一退出的时候注销观察
-
private
var mCompositeDisposable: CompositeDisposable? =
null
-
/**
-
* 获取View
-
* @return
-
*/
-
var mvpView: V? =
null
-
private
set
-
-
fun attachView(baseView: V) {
-
this.mvpView = baseView
-
}
-
-
/**
-
* 解绑View,该方法在BaseMvpActivity类中被调用
-
*/
-
fun detachView() {
-
mvpView =
null
-
// 在界面退出等需要解绑观察者的情况下调用此方法统一解绑,防止Rx造成的内存泄漏
-
if (mCompositeDisposable !=
null) {
-
mCompositeDisposable!!.dispose()
-
}
-
}
-
-
-
/**
-
* 将Disposable添加,
-
*
-
* @param subscription
-
*/
-
fun addDisposable(subscription: Disposable) {
-
//csb 如果解绑了的话添加 sb 需要新的实例否则绑定时无效的
-
if (mCompositeDisposable ==
null || mCompositeDisposable!!.isDisposed) {
-
mCompositeDisposable = CompositeDisposable()
-
}
-
mCompositeDisposable!!.add(subscription)
-
}
-
-
}
下面建立BaseActivity:
-
/**
-
* @author jiang zhu on 2019/11/23
-
*/
-
abstract
class BaseActivity : AppCompatActivity() {
-
-
// 设置布局
-
protected
abstract
val layoutId:
Int
-
-
override
fun onCreate(savedInstanceState: Bundle?) {
-
super.onCreate(savedInstanceState)
-
setContentView(layoutId)
-
-
initPresenter()
-
//初始化控件
-
initViews()
-
//获取数据
-
getDataFromServer()
-
}
-
-
// 初始化界面
-
protected
abstract
fun initViews()
-
-
// 获取数据
-
protected
fun getDataFromServer() {}
-
-
// 实例化presenter
-
protected
open
fun initPresenter() {}
-
-
}
接下来是BaseMvpActivity,这里来解释下为什么不把这两个合成一个,首先是可以留出一层,为了以后业务的修改;其次是并不是所有的活动都需要MVP,不能为了写MVP而写MVP,实在是没有必要,如果是简单的页面,只有一个网络请求或者根本没有网络请求和数据库的操作,那么写MVP的话就实在没有必要了,这时就可以继承BaseActivity。下面是BaseMvpActivity代码:
-
/**
-
* @author jiang zhu on 2019/11/23
-
*/
-
abstract
class BaseMvpActivity<V : BaseView, P : BasePresenter<V>> : BaseActivity() {
-
-
protected
var presenter: P? =
null
-
private
set
-
-
override
fun initPresenter() {
-
//实例化Presenter
-
presenter = createPresenter()
-
//绑定
-
if (presenter !=
null) {
-
@Suppress("UNCHECKED_CAST")
-
presenter!!.attachView(
this
as V)
-
}
-
}
-
-
// 初始化Presenter
-
protected
abstract
fun createPresenter(): P
-
-
-
override
fun onDestroy() {
-
//解绑
-
if (presenter !=
null) {
-
presenter!!.detachView()
-
}
-
super.onDestroy()
-
}
-
-
}
3、Retrofit
之前没用过,也不了解,这里两天用的感觉是:挺舒服,来吧,记录下怎么使用:
首先准备下网络请求的BaseUrl和网址吧:
-
/**
-
* @author jiang zhu on 2019/11/23
-
*/
-
internal
object UrlConstant {
-
-
//base
-
const val BASE_URL =
"http://192.168.3.37:8080/pet/"
-
-
//base DATA
-
const val BASE_DATA =
"data"
-
-
//登录接口
-
const val GET_LOGIN =
"user/getLogin"
-
-
//获取动态接口
-
const val GET_DYNAMIC =
"dynamic/getDynamics"
-
-
}
然后来写一个Retrofit的帮助类,由于该类会被经常调用,所以写成单例,里面并没有什么内容,只是将Retrofit进行了初始化:
-
/**
-
* @author jiang zhu on 2019/11/23
-
*/
-
class RetrofitHelper private constructor() {
-
private
val client = OkHttpClient()
-
// 声明Retrofit对象
-
private
var mRetrofit: Retrofit? =
null
-
-
internal
val server: RetrofitService
-
get() = mRetrofit!!.create(RetrofitService::
class.java)
-
-
init {
-
initRetrofit()
-
}
-
-
/**
-
* 初始化 retrofit
-
*/
-
private
fun initRetrofit() {
-
mRetrofit = Retrofit.Builder()
-
.baseUrl(UrlConstant.BASE_URL)
-
.client(client)
-
.addConverterFactory(GsonConverterFactory.create())
-
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
-
.build()
-
}
-
-
companion
object {
-
-
//单例模式
-
@Volatile
-
private
var instance: RetrofitHelper? =
null
-
-
fun getInstance(): RetrofitHelper? {
-
if (instance ==
null) {
-
synchronized(RetrofitHelper::
class.java) {
-
if (instance ==
null) {
-
instance = RetrofitHelper()
-
}
-
}
-
}
-
return instance
-
}
-
}
-
-
}
上面代码中的RetrofitService中定义了网络接口,返回值为Observable<T>,方便之后的数据操作:
-
/**
-
* @author jiang zhu on 2019/11/23
-
*/
-
interface RetrofitService {
-
-
@POST(UrlConstant.GET_LOGIN)
-
fun getLogin(@Query(UrlConstant.BASE_DATA) data: String): Observable<UserBean>
-
-
@GET(UrlConstant.GET_DYNAMIC)
-
fun getDynamic(@Query(UrlConstant.BASE_DATA) data: String): Observable<DynamicBean>
-
-
}
简单看一下支持的网络请求以及所有的注解:
之后定义DataManager,同样设置成单例,用来管理RetrofitService中定义的网络接口,当做Presenter和Retrofit的桥梁:
-
class DataManager private constructor() {
-
-
private
val mRetrofitService: RetrofitService = RetrofitHelper.getInstance()!!.server
-
-
-
// 将retrofit的业务方法映射到DataManager中,统一用该类来调用业务方法
-
fun getLogin(data: String): Observable<UserBean> {
-
return mRetrofitService.getLogin(
data)
-
}
-
-
fun getDynamic(data: String): Observable<DynamicBean> {
-
return mRetrofitService.getDynamic(
data)
-
}
-
-
companion
object {
-
-
//单例
-
@Volatile
-
private
var instance: DataManager? =
null
-
-
fun getInstance(): DataManager? {
-
if (instance ==
null) {
-
synchronized(DataManager::
class.java) {
-
if (instance ==
null) {
-
instance = DataManager()
-
}
-
}
-
}
-
return instance
-
}
-
}
-
}
4、使用样例
样例就以登录作为样例吧。首先来建立网络请求的实体类,由于这里为GsonFormat直接生成的代码,就不改Kotlin了,代码太多,get、set代码直接省略:
-
public
class UserBean {
-
-
/**
-
* msg : 查询成功
-
* success :
true
-
* rows : {
"uid":
"111111111",
"account":
"123456",
"password":
"123456",
"name":
"??"}
-
*/
-
-
private
String msg;
-
private boolean success;
-
private RowsBean rows;
-
-
-
public static
class RowsBean {
-
/**
-
* uid :
111111111
-
* account :
123456
-
* password :
123456
-
* name : 爱你
-
*/
-
-
private
String uid;
-
private
String account;
-
private
String password;
-
private
String name;
-
private
String photo;
-
-
}
-
-
-
}
下面是LoginView,直接继承BaseView,里面只定义了一个回调,之后如果有需要可以直接进行添加:
-
/**
-
* @author jiang zhu on 2019/11/23
-
*/
-
interface LoginView : BaseView {
-
// 当前页面比较简单仅仅是获取接口数据进行展示,
-
// 业务比较复杂的时候,可能一个页面需要不同的接口得到不同的数据类型
-
fun onSuccess(mUser: UserBean)
-
}
然后是LoginPresenter,继承自BasePresenter<BaseView>,里面直接对Observable进行解析,和LoginView以及BaseView中的接口进行关联,数据进行回调:
-
/**
-
* @author jiang zhu on 2019/11/23
-
*/
-
class LoginPresenter : BasePresenter<LoginView>() {
-
-
-
private
val dataManager: DataManager? = DataManager.getInstance()
-
private
var mUser: UserBean? =
null
-
-
/**
-
* 登录
-
* @param username 账号
-
* @param password 密码
-
*/
-
fun getLogin(username: String, password: String) {
-
if (mvpView !=
null) {
-
val hashMap = java.util.LinkedHashMap<String, String>()
-
hashMap[
"account"] = username
-
hashMap[
"password"] = password
-
val gao = Gson()
-
val
data = gao.toJson(hashMap)
-
// 进行网络请求
-
dataManager?.getLogin(
data)?.doOnSubscribe { disposable ->
-
//请求加入管理,统一管理订阅,防止内存泄露
-
addDisposable(disposable)
-
// 显示进度提示
-
mvpView!!.showProgressDialog()
-
}?.subscribeOn(Schedulers.io())?.observeOn(AndroidSchedulers.mainThread())?.subscribe(
object : Observer<UserBean> {
-
override
fun onSubscribe(d: Disposable) {
-
-
}
-
-
override
fun onNext(userBean: UserBean) {
-
mUser = userBean
-
}
-
-
override
fun onError(e: Throwable) {
-
// 在事件处理过程中出异常时,onError() 会被触发,同时队列自动终止,不允许再有事件发出
-
e.printStackTrace()
-
mvpView!!.onError(
"请求失败!!")
-
mvpView!!.hideProgressDialog()
-
}
-
-
override
fun onComplete() {
-
// onComplete方法和onError方法是互斥的,
-
// RxJava 规定,当不会再有新的 onNext() 发出时,需要触发 onCompleted() 方法作为标志。
-
if (mUser !=
null) {
-
mvpView!!.onSuccess(mUser!!)
-
}
-
// 隐藏进度
-
mvpView!!.hideProgressDialog()
-
}
-
})
-
}
-
}
-
-
-
}
直接在Activity中对Presenter进行调用,传入用户名密码:
-
private
fun submit() {
-
// validate
-
val username = loginEtUsername.text.toString().trim { it <=
' ' }
-
if (TextUtils.isEmpty(username)) {
-
Toast.makeText(
this,
"账号不能为空", Toast.LENGTH_SHORT).show()
-
return
-
}
-
-
val password = loginEtPassword.text.toString().trim { it <=
' ' }
-
if (TextUtils.isEmpty(password)) {
-
Toast.makeText(
this,
"密码不能为空", Toast.LENGTH_SHORT).show()
-
return
-
}
-
-
// 执行登录操作
-
presenter?.getLogin(username, password)
-
}
最后可以在显示隐藏等待框的回调中进行操作:
-
override
fun showProgressDialog() {
-
runOnUiThread {
-
loginBtnLoading.visibility = View.VISIBLE
-
}
-
}
-
-
override
fun hideProgressDialog() {
-
loginBtnLoading.visibility = View.GONE
-
}
总结
到这里本篇文章基本技术,总结下:周末两天摸鱼。。。。努力,共勉。
本文所写所有代码已上传到Github,https://github.com/zhujiang521/Retrofit
欢迎大家关注我的个人公众号,会定期发布安卓、Java学习及搞笑文章。
转载:https://blog.csdn.net/haojiagou/article/details/103222811