带实际例子的Android架构MVP简述「Kotlin,MVP,Retrofit,RxJava」

由于鸿洋大佬开源接口停用(其他人滥用导致),该教程接口不再可用,有空再重写,抱歉

带实际例子的Android架构MVP简述一「Kotlin,MVP,Retrofit,RxJava」

谷歌推荐的MVP架构,是带有Model层的。但是也有人提出省略Model层的方式,本篇讲解的就是无Model层的实现方式。

本篇文章会使用Retrofit和Rxkotlin请求一个网络学生信息接口,并将请求到的学生数据展示到界面的文本上

学生数据.png

教程使用的是kotlin语言,如果对kotlin语言不熟悉的,可以查看我的教程推荐,传送门

本篇文章也有相应的Java版本,传送门,文章源代码已经上传到仓库的kotlinMVP1

为什么要有这个东西

原生的架构是把所有的操作,如网络请求,数据库请求都直接放在Activity里面。这样做的好处是写的时候比较简单,在操作少的时候,这样做是比较方便

但是当操作比较多的时,一个类的代码太多就会使得我们整理起来很费劲,不容易读。比如第一行代码里面最后的天气项目的WeatherActivity就是我们原生的做法,把网络请求直接到Activity里面进行(郭神那时候可能是有其他的考虑,这里不是说这样做就绝对不对)

WeatherActivity.png

而MVP架构要做的,就是把Activity中过多的代码,网络请求,数据库请求抽离出来,这体现的其实是一种封装的思想,如果你学习过设计模式,就会发现这种思想在设计模式中随处可见

简而言之,MVP架构就是在一个类的代码太多的时候,把这个类中的代码抽出一部分,放在另外一个,或多个类中,而同时又能不能影响程序原本的功能运行的一种代码整理方式

项目架构的搭建

创建项目,并进行分包

带实际例子的Android架构MVP简述「Kotlin,MVP,Retrofit,RxJava」_第1张图片
项目分包.png

module包是业务包,里面可以有很多不同功能的包,比如这里我们的Info包就是为了获取学生的信息。

bean包用来存放网络请求实体类,net包用来放我们网络请求需要用到的类

另外我们还重新创建了InfoActivity,并且在AndroidManifest.xml中将InfoActivity设置为程序的第一启动项,接下来书写xml布局




    

接下来开始MVP的代码书写了,我们到presenter包下创建InfoPresenter类,接下来从Activity里面抽取出来的代码就是写在该类里的

/**
 * 获取学生信息P层类
 */
class InfoPresenter {

    fun getData() {
        //在此处进行网络请求,并将请求结果返回给Activity,这里假设我们已经请求成功,想要返回学生数据
    }
}

这里思考一下,我们想要在InfoPresenter类里的getData方法执行网络请求方法,然后将请求成功或失败的回调返回给Activity进行展示。

但是我们该怎么做呢?给getData方法一个返回值?这样做事不行的,因为在我们后续的网络请求的方法中,是要结合RxKotlin的方法进行异步请求的,所以我们是没有办法直接返回的(直接报错,不能在方法中返回上一级方法的返回值),如下图

带实际例子的Android架构MVP简述「Kotlin,MVP,Retrofit,RxJava」_第2张图片
方法.png

所以靠返回值来和Actvity进行交互是不行了,那我们就得采用另外一种方法,绑定View

我们在view包下创建Info接口,在该类下创建show()方法,该方法就是用于我们上面说到的将P层中网络请求到的数据返回给Activity

/**
 * 用于在P层将数据返回给Activity
 */
interface InfoView {
    //展示数据
    fun show(msg: String)
}

然后回到P层,看看View层在P层中我们该如何操作

/**
 * 获取学生信息P层类
 */
class InfoPresenter {

    //View接口
    private var infoView: InfoView? = null

    //绑定View
    fun attachView(view: InfoView) {
        this.infoView = view
    }

    //销毁View对象
    fun detechView() {
        infoView = null
    }

    fun getData() {
        //在此处进行网络请求,并将请求结果返回给Activity,这里假设我们已经请求成功,想要返回学生数据
        infoView?.show("数据请求成功")
    }
}

我们声明了一个infoView变量,使用attachView方法中完成对view接口的绑定,意思就是当进行该操作后,infoView变量就可以调用Activity里面的show方法来对文本进行赋值。

至于为什么infoView变量能调用Activity的show()方法请接着看下面的InfoActivity

/**
 * 获取学生信息Activity
 */
class InfoActivity : AppCompatActivity(), InfoView {

    //声明p层接口
    private var infoPresenter = InfoPresenter()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_info)
        //完成对view接口的绑定
        infoPresenter.attachView(this)
        btnSend.setOnClickListener {
            infoPresenter.getData()
        }
    }

    override fun show(msg: String) {
        //将请求到的数据设置到TextView上
        txtStudentInfo.text = msg
    }

    override fun onDestroy() {
        super.onDestroy()
        infoPresenter.detechView()
    }
}

我们将InfoActivity继承于InfoView接口。

调用attachView(this)方法,传入this,相当于把Actiivty本身传递了进去,之后InfoPresenter的InfoView变量调用的show方法就是Actiivty的show方法啦(因为Actiivty继承了InfoView接口)

我们通过InfoView这个接口作为桥梁,通过attachView精心赋值,使得P层里的infoview变量调用的方法就是Activity的方法

运行程序,在按钮点击事件里面调用P层的getData()方法,P层的getData()方法获取到数据后会调用InfoView接口的show()方法,也就是InfoActivity的show()方法,然后在TextView中显示我们获取到的数据。

Screenshot_1534047603.png

detechView方法用来销毁掉View,因为P层中进行的是网络,数据库请求,如果不在onDestroy中把view销毁。当遇到这种情况,Activity已经被销毁,但P层中的请求仍在进行的时候,App就有可能发生内存泄漏,为了避免这种情况,我们就在Activity销毁时,一起把p层给销毁掉

网络请求

添加异步请求框架Rxkotlin,RxAndroid,网络请求库okhttp,retrofit框架依赖

// Retrofit库
compile 'com.squareup.retrofit2:retrofit:2.4.0'  
compile 'com.squareup.retrofit2:converter-gson:2.4.0'    // 支持Gson解析
compile 'com.squareup.okhttp3:logging-interceptor:3.6.0'
compile 'com.squareup.okhttp3:okhttp:3.11.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'

// RxKotlin and RxAndroid
compile "io.reactivex:rxkotlin:1.0.0"
compile "io.reactivex:rxandroid:1.2.1"

在实际接入之前,我们先用后端接口调试工具Postman,测试一下我们的等会要请求的学生数据信息接口,接入后端网络接口之前先使用调试工具调试,这是个好习惯

带实际例子的Android架构MVP简述「Kotlin,MVP,Retrofit,RxJava」_第3张图片
调试接口.png

我们先在module/info业务包下得bean实体类接口中创建网络请求实体类

//加入data后该类为数据类,会为我们自动生成toString等方法
data class Student(var name: String, var age: String)

除了toString方法外,关于数据类data为我们生成的其他方法,可以参考官方文档中的讲解

因为要进行网络请求,所以要在AndroidManifest.xml中声明我们网络权限


然后我们在net包下创建Api接口,RetrofitFactory类,RetrofitFactory用来存放我们Retrofit框架的一些配置,我们需要关注的,只是.baseUrl()方法的参数,该参数就是我们接口的前半段

import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit


/**
 * Created by tonjies on 2018/10/20.
 */
class RetrofitFactory private constructor() {
    companion object {
        val instance: RetrofitFactory by lazy {
            RetrofitFactory()
        }
    }

    private val retrofit: Retrofit
    private val interceptor: Interceptor

    init {
        interceptor = Interceptor { chain ->
            val request = chain.request()
                    .newBuilder()
                    .addHeader("Content-Type", "application/json")
                    .addHeader("charset", "utf-8")
                    .build();
            chain.proceed(request)
        }
        retrofit = Retrofit.Builder()
                //接口地址
                .baseUrl("http://www.wanandroid.com/tools/mockapi/")
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .client(initClient())
                .build()
    }

    private fun initClient(): OkHttpClient? {
        return OkHttpClient.Builder()
                .addInterceptor(interceptor)
                .addInterceptor(initLogInterceptor())
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.SECONDS)
                .build()
    }

    private fun initLogInterceptor(): Interceptor? {
        var interceptor: HttpLoggingInterceptor = HttpLoggingInterceptor()
        interceptor.level = HttpLoggingInterceptor.Level.BODY
        return interceptor
    }

    fun  create(server: Class): T {
        return retrofit.create(server)
    }

}

接下来是我们Api接口,该接口负责声明我们具体要调用的接口地址

interface Api {
    //获取学生信息
    //查询天气信息
    @GET("2872/student")//接口的后段部分
    fun getData(): Observable
}

最后我们回到我们的P层,进行数据请求

/**
 * 获取学生信息P层类
 */
class InfoPresenter {

    //View接口
    private var infoView: InfoView? = null

    //声明Api接口api
    private var api: Api? = null

    constructor() {
        //初始化RetrofitFactory
        api = RetrofitFactory.instance.create(Api::class.java);
    }


    //绑定View
    fun attachView(view: InfoView) {
        this.infoView = view
    }

    //销毁View对象
    fun detechView() {
        infoView = null
    }

    fun getData() {
        //在此处进行网络请求,并将请求结果返回给Activity,这里假设我们已经请求成功,想要返回学生数据
//        infoView?.show("数据请求成功")
        
        //进行网络请求
        api!!.getData()
                .observeOn(AndroidSchedulers.mainThread()) //
                .subscribeOn(Schedulers.io())
                .subscribe(object : Observer {
                    override fun onError(e: Throwable) {
                        Log.d("tonjies", e.message)
                    }

                    override fun onNext(t: Student) {
                        var studentName = t.name //学生姓名
                        var studentAge = t.age //学生年龄
                        infoView!!.show("学生的姓名是:" + studentName + "学生的年龄是:" + studentAge)
                    }

                    override fun onCompleted() {
                    }
                })
    }
}

我们声明了Api接口变量,利用之前写好的RetrofitFactory进行初始化,最后使用Rxkotlin进行请求,把拿到的具体数据显示到界面上

运行程序,验证我们的代码

学生数据.png

ok,这一小节就到这里啦,文章还是有些不足的,对于Retrofit和Rxkotlin的介绍略显不足,所以如果你想要更加详细的学习这两个,这里我推荐以下几个教程

  • 给初学者的RxJava https://www.jianshu.com/p/464fa025229e
  • 这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解) https://blog.csdn.net/carson_ho/article/details/73732076
  • Android Rxjava:这是一篇 清晰 & 易懂的Rxjava 入门教程 https://www.jianshu.com/p/a406b94f3188
  • Retrofit2 和 RxJava 配合使用时的错误处理 https://juejin.im/entry/572fec5471cfe4006cac80e7#comment

当然不代表本篇文章没有意义,当我最初看到其他文章复杂的图示和理论时,都很头疼,想着还不如给我一个简单的例子,本篇文章正是因此而存在的

如果你喜欢本篇文章,希望能给我一个喜欢,这对我来说是很好的鼓励

你可能感兴趣的:(带实际例子的Android架构MVP简述「Kotlin,MVP,Retrofit,RxJava」)