上一篇博客我们讲了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的实现.欢迎小伙伴们前来捶打,如有问题及时沟通,我会及时改正.