Android使用Kotlin+Retrofit+RxJava+MVP实现简单的网络请求封装

上一篇博客我们讲了kotlin+retrofit+rxjava实现网络请求的简单实现,本篇博客将实现之前的基础上实现kotlin+retrofit+rxjava+mvp的简单封装实现网络请求.

请求的url为:const val BASE_SERVER_URL = "https://www.wanandroid.com"

1.BaseActivity代码如下:

/**
 * @作者: njb
 * @时间: 2020/12/3 17:33
 * @描述:
 */
abstract class BaseActivity<P : BasePresenter<*>> : AppCompatActivity(), BaseView {
    lateinit var context: Context
    protected var presenter: P? = null
    protected var unbinder: Unbinder? = null

    private var waitDialog: LoadingDialog? = null

    protected abstract val layoutId: Int

    protected abstract fun createPresenter(): P

    protected abstract fun initView()

    protected abstract fun addListener()


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (layoutId != 0) {
            setContentView(layoutId)
        }
        unbinder = ButterKnife.bind(this)
        context = this
        presenter = createPresenter()
        initView()
        addListener()
    }


    /**
     * 打开Activity
     *
     * @param cls
     */
    fun startA(cls: Class<*>) {
        val intent = Intent(context, cls)
        startActivity(intent)
    }

    public override fun onPause() {
        super.onPause()
    }

    override fun onDestroy() {
        super.onDestroy()
        presenter!!.detachView()
        unbinder!!.unbind()
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            android.R.id.home -> {
                onBackPressed()
            }
        }
        return super.onOptionsItemSelected(item)
    }

    override fun onBackPressed() {
        super.onBackPressed()
        // Fragment 逐个出栈
        val count = supportFragmentManager.backStackEntryCount
        if (count == 0) {
            super.onBackPressed()
        } else {
            supportFragmentManager.popBackStack()
        }
    }


    /**
     * @param s
     */
    fun showtoast(s: String) {
        Toast.makeText(context, s, Toast.LENGTH_SHORT).show()
    }

    fun showFileDialog() {}

    fun hideFileDialog() {}

    /**
     * 显示加载框
     *
     * @param textRes
     * @param color
     */
    fun showWaitDialog(textRes: String, color: Int) {
        if (null == waitDialog) {
            waitDialog = LoadingDialog(this, textRes, color)
        } else {
            waitDialog!!.setShowText(textRes)
            if (!waitDialog!!.isShown) {
                waitDialog!!.showUp()
            }
        }
    }

    /**
     * 显示加载框
     */
    fun showWaitDialog() {
        if (null == waitDialog) {
            waitDialog = LoadingDialog(this)
        } else {
            waitDialog!!.setShowText("")
            if (!waitDialog!!.isShown) {
                waitDialog!!.showUp()
            }
        }
    }

    /**
     * 显示progressBar
     *
     * @param textRes 需要提示的字
     */
    fun showWaitDialog(textRes: Int) {
        if (null == waitDialog) {
            waitDialog = LoadingDialog(this, textRes)
        } else {
            waitDialog!!.setShowText(textRes)
            if (!waitDialog!!.isShown) {
                waitDialog!!.showUp()
            }
        }
    }

    /**
     * 销毁progressBar
     */
    fun dismissWaitDialog() {
        if (null != waitDialog && waitDialog!!.isShown) {
            waitDialog!!.dismiss()
        }
    }


    /**
     * 通过资源res获得view
     *
     * @param res
     * @return
     */
    fun getViewByRes(@LayoutRes res: Int): View {
        return LayoutInflater.from(context).inflate(res, null)
    }

    /**
     * 获得TextView 的文本
     *
     * @param tv
     * @return
     */
    fun getTV(tv: TextView?): String {
        return tv?.text?.toString()?.trim { it <= ' ' } ?: ""
    }

    override fun showError(msg: String) {
        showtoast(msg)
    }

    override fun onErrorCode(model: BaseResult<Any>) {
        when {
            model.errorCode < 0.toString() -> showtoast(model.errorMsg)
        }
    }

    override fun showLoadingFileDialog() {
        showWaitDialog()
    }

    override fun hideLoadingFileDialog() {
        dismissWaitDialog()
    }

    override fun onProgress(totalSize: Long, downSize: Long) {}

    fun addFragmentToActivity(
        fragmentManager: FragmentManager,
        fragment: Fragment, frameId: Int
    ) {
        val transaction = fragmentManager.beginTransaction()
        if (!fragment.isAdded)
            transaction.add(frameId, fragment)
        fragmentManager.fragments.filter { it.id == fragment.id }.map { transaction.hide(it) }
        transaction.show(fragment)
        transaction.commit()
    }

2.BasePresenter代码如下:

public  class BasePresenter<V extends BaseView> {

    private CompositeDisposable compositeDisposable;
    public V baseView;

    protected ApiServer apiServer = ApiRetrofit.getInstance().getApiService();

    public BasePresenter(V baseView) {
        this.baseView = baseView;
    }

    /**
     * 解除绑定
     */
    public void detachView() {
        baseView = null;
        removeDisposable();
    }

    /**
     * 返回 view
     *
     * @return
     */
    public V getBaseView() {
        return baseView;
    }


    public void addDisposable(Observable<?> observable, DisposableObserver observer) {
        if (compositeDisposable == null) {
            compositeDisposable = new CompositeDisposable();
        }
        compositeDisposable.add(
                observable.subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribeWith(observer));
    }

    public void removeDisposable() {
        if (compositeDisposable != null) {
            compositeDisposable.dispose();
        }
    }
}

3.BaseFragment代码如下:

/**
 * @作者: njb
 * @时间: 2020/12/3 17:38
 * @描述:
 */
abstract class BaseFragment<P : BasePresenter<*>> : Fragment(), BaseView {

    internal var context: Context? = null
    private var dialog: ProgressDialog? = null

    // 控件是否初始化完成
    private var isViewCreated: Boolean = false

    // 当前fragment是否加载过数据,如加载过数据,则不再加载
    private var isLoadCompleted: Boolean = false
    //是不是可见
    private var isUIVisible: Boolean = false


    protected var presenter: P? = null

    /**
     * 加载布局
     */
    @LayoutRes
    abstract fun getLayoutId(): Int


    // 懒加载,强制子类重写
    abstract fun loadData()

    abstract fun initView()

    abstract fun addListener()

    abstract fun createPresenter(): P

    abstract fun setTitle()


    override fun setUserVisibleHint(isVisibleToUser: Boolean) {
        super.setUserVisibleHint(isVisibleToUser)
        isUIVisible = isVisibleToUser
        if (isVisibleToUser && isViewCreated && isUIVisible && !isLoadCompleted) {
            isLoadCompleted = true
            loadData()
        }
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        if (isViewCreated && isUIVisible) {

            loadData()
            isLoadCompleted = true
        }
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val rootView = inflater.inflate(getLayoutId(), container, false)
/*        presenter = createPresenter()
        isViewCreated = true

        initView()
        addListener()*/
        return rootView
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        presenter = createPresenter()
        isViewCreated = true
        initView()
        addListener()
    }

    override fun onHiddenChanged(hidden: Boolean) {
        super.onHiddenChanged(hidden)
        isUIVisible = !hidden
        isLoadCompleted = !hidden
    }


    override fun onAttach(context: Context) {
        super.onAttach(context)
        this.context = context
    }

    override fun onResume() {
        super.onResume()

    }

    override fun onPause() {
        super.onPause()
    }

    override fun onDestroyView() {
        super.onDestroyView()

        if (presenter != null) {
            presenter!!.detachView()
        }
    }

    /**
     * 打开指定的activity
     *
     * @param cls
     */
    fun startA(cls: Class<*>) {
        val intent = Intent(context, cls)
        startActivity(intent)
    }

    fun setStatusBar(view: View?) {
        if (view == null) {
            return
        }

    }


    /**
     * toast
     *
     * @param msg
     */
    fun showtoast(msg: String) {
        Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
    }

    /**
     * 显示加载动画
     */
    fun showLoadingDialog() {
        if (dialog != null && dialog!!.isShowing) {
            return
        }
        if (null == dialog) {
            dialog = ProgressDialog(context, R.style.AlertDialogStyle)
        }
        dialog!!.setCancelable(false)
        dialog!!.show()

    }

    /**
     * 隐藏加载动画
     */
    fun closeLoadingDialog() {
        if (dialog != null && dialog!!.isShowing) {
            dialog!!.dismiss()
        }
    }

    /**
     * 通过资源res获得view
     *
     * @param res
     * @return
     */
    fun getViewByRes(@LayoutRes res: Int): View {
        return LayoutInflater.from(context).inflate(res, null)
    }


    /**
     * 获得TextView 的文本
     *
     * @param tv
     * @return
     */
    fun getTV(tv: TextView?): String {
        return tv?.text?.toString()?.trim { it <= ' ' } ?: ""
    }


    private fun showFileDialog() {}

    private fun hideFileDialog() {}


    override fun hideLoading() {
        closeLoadingDialog()
    }

    override fun showError(msg: String) {
        showtoast(msg)
    }

    override fun onErrorCode(model: BaseResult<Any>) {
        when {
            model.errorCode < 0.toString() -> showtoast(model.errorMsg!!)
        }
    }

    override fun showLoadingFileDialog() {
        showFileDialog()
    }

    override fun hideLoadingFileDialog() {
        hideFileDialog()
    }

    override fun onProgress(totalSize: Long, downSize: Long) {}

}

4.BaseObserver代码如下:

/**
 * @作者: njb
 * @时间: 2020/12/3 17:29
 * @描述:
 */
abstract class BaseObserver <T> : DisposableObserver<T?>{
    private var view: BaseView?
    private var isShowDialog = false

    constructor(view: BaseView?) {
        this.view = view
    }

    constructor(view: BaseView?, isShowDialog: Boolean) {
        this.view = view
        this.isShowDialog = isShowDialog
    }

    override fun onStart() {
        if (isShowDialog) {
            view!!.showLoading()
        }
    }

    override fun onNext(o: T) {
        onSuccess(o)
    }

    override fun onError(e: Throwable) {
        if (isShowDialog) {
            view!!.hideLoading()
        }
        val be: BaseException
        onError("出现错误")
    }

    override fun onComplete() {
        if (isShowDialog) {
            view!!.hideLoading()
        }
    }

    abstract fun onSuccess(o: T)
    abstract fun onError(msg: String?)
}

5.BaseView代码如下:

/**
 * @作者: njb
 * @时间: 2020/12/3 15:36
 * @描述:
 */
interface BaseView {
    /**
     * 显示dialog
     */
    fun showLoading()

    /**
     * 显示下载文件dialog
     */

    fun showLoadingFileDialog()

    /**
     * 隐藏下载文件dialog
     */

    fun hideLoadingFileDialog()

    /**
     * 下载进度
     * @param totalSize
     * @param downSize
     */

    fun onProgress(totalSize: Long, downSize: Long)

    /**
     * 隐藏 dialog
     */

    fun hideLoading()

    /**
     * 显示错误信息
     * @param msg
     */
    fun showError(msg: String)

    /**
     * 错误码
     */
    fun onErrorCode(baseResult: BaseResult<Any>)
}

6.ApiRetrofit代码如下:

/**
 * 作者: njb
 * 时间: 2016/12/27.13:56
 * 描述:
 * 来源:
 */
class ApiRetrofit {
    private val retrofit: Retrofit
    private val client: OkHttpClient
    val apiService: ApiServer
    private val TAG = "ApiRetrofit"

    /**
     * 请求访问quest
     * response拦截器
     */
    private val interceptor = Interceptor { chain ->
        val request = chain.request()
        val startTime = System.currentTimeMillis()
        val response = chain.proceed(chain.request())
        val endTime = System.currentTimeMillis()
        val duration = endTime - startTime
        val mediaType = response.body!!.contentType()
        val content = response.body!!.string()
        Log.e(TAG, "| Response:$content")
        response.newBuilder()
                .body(ResponseBody.create(mediaType, content))
                .build()
    }
    private val headInterceptor = Interceptor { chain ->
        var request = chain.request()

        //获取到方法
        val method = request.method
        if (method == "GET") {
            val httpUrlurl = request.url
            val url = httpUrlurl.toString()
            val index = url.indexOf("?")
            if (index > 0) {
                //url = url + "&APP_KEY=" + AppConstant.APP_KEY;
            } else {
                //  url = url + "?APP_KEY=" + AppConstant.APP_KEY;  //拼接新的url
            }
            request = request.newBuilder().url(url).build() //重新构建请求
        } else if (method == "POST") {
            val requestBuilder = request.newBuilder()

            //请求体定制:统一添加token参数
            if (request.body!!.contentLength() == 0L) {
                //没有参数
                val newFormBody = FormBody.Builder()
                requestBuilder.method(request.method, newFormBody.build())
            } else if (request.body is FormBody) {
                //正常post
                val newFormBody = FormBody.Builder()
                val oidFormBody = request.body as FormBody?
                for (i in 0 until oidFormBody!!.size) {
                    newFormBody.addEncoded(oidFormBody.encodedName(i), oidFormBody.encodedValue(i))
                }
                requestBuilder.method(request.method, newFormBody.build())
            }
            request = requestBuilder.build()
        }
        chain.proceed(request)
    }

    companion object {
        private var apiRetrofit: ApiRetrofit? = null

        @JvmStatic
        val instance: ApiRetrofit?
            get() {
                if (apiRetrofit == null) {
                    synchronized(Any::class.java) {
                        if (apiRetrofit == null) {
                            apiRetrofit = ApiRetrofit()
                        }
                    }
                }
                return apiRetrofit
            }
    }

    init {
        client = OkHttpClient.Builder() //添加log拦截器
                .addInterceptor(interceptor)
                .addInterceptor(headInterceptor)
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.SECONDS) //                .sslSocketFactory()
                .build()
        retrofit = Retrofit.Builder()
                .baseUrl(Constant.BASE_SERVER_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addConverterFactory(ScalarsConverterFactory.create()) //支持RxJava2
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(client)
                .build()
        apiService = retrofit.create(ApiServer::class.java)
    }
}

7.ApiServer:这里还是以wanAndroidApi为例子

interface ApiServer {
    //首页文章列表
    @GET("/article/list/{page}/json")
    fun articleList(@Path("page") page:Int) : Observable<BaseResult<ArticleListBean>>

    @GET("/article/list/{page}/json")
    fun articleLists(@Path("page") page:Int) : Call<List<ArticleListBean>>

    //首页广告
    @GET("/banner/json")
    fun banner():Observable<BaseListResult<BannerBean>>

    //搜索热词
    @GET("/hotkey/json")
    fun hotkey(): Observable<BaseListResult<HotkeyBean>>

    //体系接口
    @GET("/tree/json")
    fun tree(): Observable<BaseListResult<KnowledgeBean.DataBean>>

    //知识体系下的文章
    @GET("article/list/{page}/json")
    fun article(@Path("page") page: Int,@Query("cid") cid: Int): Observable<BaseResult<ArticleListBean>>

    //导航
    @GET("/navi/json")
    fun navi():Observable<BaseListResult<NavigationBean.DataBean>>

    //项目分类
    @GET("/project/tree/json")
    fun projectTree(): Observable<BaseListResult<ProjectTitleBean.DataBean>>

    //项目分类下的列表
    @GET("/project/list/{page}/json")
    fun projectList(@Path("page") page: Int,@Query("cid") cid:Int): Observable<BaseBean<ProjectListBean>>

    //登录
    @POST("/user/login")
    @FormUrlEncoded
    fun login(@Field("username") username: String, @Field("password") password:String):Observable<BaseBean<LoginBean>>

    //注册
    @POST("/user/register")
    @FormUrlEncoded
    fun register(@Field("username") username: String, @Field("password") password: String, @Field("repassword") repassword: String) : Observable<BaseBean<RegisterBean>>

    //收藏文章列表
    @GET("/lg/collect/list/0/json")
    fun collect():Observable<BaseBean<*>>

    //收藏站内文章
    @POST("/lg/collect/1165/json")
    @FormUrlEncoded
    fun collectLg(@Path("id") id:Int):Observable<BaseBean<*>>

    //收藏站外文章
    @POST("/lg/collect/add/json")
    @FormUrlEncoded()
    fun collectadd():Observable<BaseBean<*>>

    //取消收藏
    @POST("/lg/uncollect_originId/2333/json")
    @FormUrlEncoded
    fun uncollect(@Path("id") id:Int):Observable<BaseBean<*>>

    //我的收藏
    @POST("/lg/uncollect/2805/json")
    @FormUrlEncoded
    fun mycollect(@Path("id") id: Int,@Path("orignId") orignId:Int ) :Observable<BaseBean<*>>

    //收藏网站列表
    @GET("/lg/collect/usertools/json")
    fun usertools():Observable<BaseBean<*>>


    //收藏网址
    @POST("/lg/collect/addtool/json")
    @FormUrlEncoded
    fun addcollect(@Path("name") name: Int,@Path("link") link: Int):Observable<BaseBean<*>>

    //编辑收藏网站
    @POST("/lg/collect/updatetool/json")
    @FormUrlEncoded
    fun updatetool(@Path("id") id: Int,@Path("name") name:Int,@Path("link") liml:Int):Observable<BaseBean<*>>

    //删除收藏网站
    @POST("/lg/collect/deletetool/json")
    @FormUrlEncoded
    fun deletetool(@Path("id") id: Int):Observable<BaseBean<*>>

    //搜索
    @POST("/article/query/{page}/json")
    @FormUrlEncoded
    fun query(@Path("page") page: Int, @Field("k") k:String):Observable<BaseBean<SearchBean>>

    //新增一个TODO
    @POST("/lg/todo/add/json")
    @FormUrlEncoded
    fun add(@Path("title") title:Int,@Path("content") content:Int,@Path("date") date:Int,@Path("type") type: Int):Observable<BaseBean<*>>

    //更新一条todo内容
    @POST("/lg/todo/update/83/json")
    @FormUrlEncoded
    fun update(@Path("id") id: Int,@Path("title") title: Int,@Path("content") content: Int,@Path("date") date: Int,
               @Path("status") status: Int,@Path("type") type: Int):Observable<BaseResult<*>>

    //删除一条todo
    @POST("/lg/todo/delete/83/json")
    @FormUrlEncoded
    fun delete(@Path("id") id: Int, @Path("status") status: Int):Observable<BaseResult<*>>

    //未完成TODO列表
    @POST("/lg/todo/listnotdo/")
    @FormUrlEncoded
    fun listnotdo(@Path("type") type: Int,@Path("page")page: Int):Observable<BaseResult<*>>

    //已完成TODO列表
    @POST("/lg/todo/listdone/")
    @FormUrlEncoded
    fun listdone(@Path("type") type:Int,@Path("page") page:Int):Observable<BaseResult<*>>

    // 仅更新完成状态Todo
    @POST("/lg/todo/done/80/json")
    @FormUrlEncoded
    fun done(@Path("id") id:Int):Observable<BaseResult<*>>
}

8.先创建一个view继承于BaseVew,然后创建一个presenter继承于BasePresenter,接着在Activity中继承于BaseActivity,若是Fragment则继承于BaseFragment,具体实现代码如下:

(1)AtricleListView:文章列表View

/**
 * @作者: njb
 * @时间: 2020/12/3 17:50
 * @描述:
 */
interface ArticleListView : BaseView {
    fun onLoadArtList(date: ArticleListBean)
    fun onLoadFriend(date: HotkeyBean)
}

(2)HomePresenter:文章列表Presenter

/**
 * @作者: njb
 * @时间: 2020/12/3 17:48
 * @描述:
 */
class HomePresenter(baseView: ArticleListView) : BasePresenter<ArticleListView>(baseView) {
    /**
     * 文章列表
     */
    fun articleList( page: Int) {
        addDisposable(apiServer.articleList(page),object :BaseObserver<BaseResult<ArticleListBean>>(baseView){
            override fun onSuccess(o: BaseResult<ArticleListBean>) {
                baseView!!.onLoadArtList(o.data!!)
            }

            override fun onError(msg: String?) {
                baseView!!.showError(msg!!)
            }

        })

    }
}

(3)Activity代码:

使用presenter!!.articleList(1)发起请求,这里只需传入参数即可,可以看到网络请求操作已经放到Presenter中去实现了,Activity不做请求和逻辑操作,只是负责发起请求然后根据返回结果去显示数据做相应处理等等.

class MainActivity : BaseActivity<HomePresenter>(), (ArticleListView) {

    override val layoutId: Int
        get() = R.layout.activity_main

    override fun createPresenter(): HomePresenter {
        return HomePresenter(this)
    }

    override fun initView() {
        presenter!!.articleList(1)
    }

    override fun addListener() {
        startA(HomeActivity::class.java)
    }

    override fun onLoadArtList(date: ArticleListBean) {
        Log.d("--data--", date.datas.toString())
    }

    override fun onLoadFriend(date: HotkeyBean) {
    }

    override fun showLoading() {

    }

    override fun hideLoading() {
    }

}

(4)文章列表实体类:ArticleListBean

class ArticleListBean {
    var datas: MutableList<DatasBean>? = null
    class DatasBean {
        /**
         * apkLink :
         * author : Jetictors
         * chapterId : 232
         * chapterName : 入门及知识点
         * collect : false
         * courseId : 13
         * desc :
         * envelopePic :
         * fresh : true
         * id : 3226
         * link : http://www.cnblogs.com/Jetictors/tag/Kotlin/
         * niceDate : 4小时前
         * origin :
         * projectLink :
         * publishTime : 1533522956000
         * superChapterId : 232
         * superChapterName : Kotlin
         * tags : []
         * title : Kotlin 系列文章
         * type : 0
         * userId : -1
         * visible : 1
         * zan : 0
         */

        var apkLink: String? = null         // 文章uri
        var author: String? = null          //
        var chapterId: Int = 0              //
        var chapterName: String? = null
        var isCollect: Boolean = false
        var courseId: Int = 0
        var desc: String? = null
        var envelopePic: String? = null
        var isFresh: Boolean = false
        var id: Int = 0
        var link: String? = null
        var niceDate: String? = null
        var origin: String? = null
        var projectLink: String? = null
        var publishTime: Long = 0
        var superChapterId: Int = 0
        var superChapterName: String? = null
        var title: String? = null
        var type: Int = 0
        var userId: Int = 0
        var visible: Int = 0
        var zan: Int = 0
        var tags: List<*>? = null
        var isSelect: Boolean  = false
    }
}

9.请求结果如下:通过如下截图可以看到成功获取到接口数据.

10.至此,kotlin+retrofit+rxjava+mvp的简单封装网络请求已经完成,下一篇会总结一下kotlin+retrofit+rxjava+mvvm的简单使用和封装,后面还会加上JetPack的实现.欢迎小伙伴们前来捶打,如有问题及时沟通,我会及时改正.

猜你喜欢

转载自blog.csdn.net/u012556114/article/details/111311547