安卓目前的架构无非那几种: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 {
//将所有正在处理的Subscription都添加到CompositeSubscription中。统一退出的时候注销观察
private var mCompositeDisposable: CompositeDisposable? = null
/**
fun attachView(baseView: V) {
this.mvpView = baseView
}
/**
/**
}
下面建立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
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()
}
/**
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,方便之后的数据操作:
/**
@author jiang zhu on 2019/11/23
*/
interface RetrofitService {
@POST(UrlConstant.GET_LOGIN)
fun getLogin(@Query(UrlConstant.BASE_DATA) data: String): Observable
@GET(UrlConstant.GET_DYNAMIC)
fun getDynamic(@Query(UrlConstant.BASE_DATA) data: String): Observable
}
简单看一下支持的网络请求以及所有的注解:
之后定义DataManager,同样设置成单例,用来管理RetrofitService中定义的网络接口,当做Presenter和Retrofit的桥梁:
class DataManager private constructor() {
private val mRetrofitService: RetrofitService = RetrofitHelper.getInstance()!!.server
// 将retrofit的业务方法映射到DataManager中,统一用该类来调用业务方法
fun getLogin(data: String): Observable {
return mRetrofitService.getLogin(data)
}
fun getDynamic(data: String): Observable {
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
*/
class LoginPresenter : BasePresenter() {
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
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 {
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学习及搞笑文章。