本实例封装Kotlin网络核心库以及重要核心逻辑
###### retrofit相关 implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0' implementation 'com.squareup.retrofit2:retrofit:2.5.0' ###### rxjava2 implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' ###### rxlifecycle implementation 'com.trello.rxlifecycle2:rxlifecycle-kotlin:2.2.0' implementation 'com.trello.rxlifecycle2:rxlifecycle-components:2.2.0'NOTE:可以去Retrofit、Rxjava2(RxAndroid)、okhttp、RxLifecycle,查询最新版本号。
为了秉承RxJava的链式调用风格,也为了方便每一个API的调用操作,创建了一个单例类ApiClient,具体如下:
classRetrofitManager private constructor(){// lateinit var apiService: ApiServicelateinitvar retrofit:Retrofit/** * 单例模式*/companionobject{val instance:RetrofitManager by lazy(mode =LazyThreadSafetyMode.SYNCHRONIZED){RetrofitManager() } } funinit(string:String){val okHttpClient =OkHttpClient.Builder() .addInterceptor(HttpLoggingInterceptor().setLevel( if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODYelseHttpLoggingInterceptor.Level.NONE)) .connectTimeout(5,TimeUnit.SECONDS) .readTimeout(5,TimeUnit.SECONDS) .writeTimeout(5,TimeUnit.SECONDS) .build() retrofit =Retrofit.Builder() .baseUrl(string) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .client(okHttpClient) .build() // apiService = retrofit.create(ApiService::class.java) } /** * 动态代理模式,创建请求接口类 * @param tClass * @param <T> * @return </T> */fun <T> createService(tClass:Class<T>): T{return retrofit.create(tClass) } }其中接口声明类:GitHubService如下:
/** * 接口声明类*/interfaceApiService{/** * 登录*/ @POST @FormUrlEncoded funlogin(@Url string:String, @Field("phone") mobile:String, @Field("pwd")pwd:String): Observable<UserBean>}上面的UserBean即一个简单的Kotlin数据类,可以去这里查看。
API的响应返回形式有很多种,此处介绍最常见的两种形式的处理:标准RESTful API与任性的后端写的API。
请求响应主要处理状态码与数据体,具体封装如下:
/** * 封装响应数据,统一异常处理*/abstractclassNetResponseObserver<T>(privatevalcontext:Context):Observer<T>{/** * 事件接收完毕*/overridefunonComplete(){LoadingDialog.cancel() } /** * 订阅事件的回调*/overridefunonSubscribe(d:Disposable){LoadingDialog.show(context) } /** * 接收事件*/overridefunonNext(t:T){success(t) } /** * 成功的回调*/abstractfunsuccess(data:T) /** * 失败的回调*/abstractfunfailure(statusCode:Int, apiErrorModel:ApiErrorModel) /** * 异常处理*/overridefunonError(e:Throwable){LoadingDialog.cancel() if (e isHttpException){val apiErrorModel:ApiErrorModel=when (e.code()){ApiErrorType.INTERNAL_SERVER_ERROR.code ->ApiErrorType.INTERNAL_SERVER_ERROR.getApiErrorModel(context) ApiErrorType.BAD_GATEWAY.code ->ApiErrorType.BAD_GATEWAY.getApiErrorModel(context) ApiErrorType.NOT_FOUND.code ->ApiErrorType.NOT_FOUND.getApiErrorModel(context) else-> otherError(e) } failure(e.code(), apiErrorModel) return } val apiErrorType:ApiErrorType=when (e){isUnknownHostException->ApiErrorType.NETWORK_NOT_CONNECTisConnectException->ApiErrorType.NETWORK_NOT_CONNECTisSocketTimeoutException->ApiErrorType.CONNECTION_TIMEOUTelse->ApiErrorType.UNEXPECTED_ERROR } failure(apiErrorType.code, apiErrorType.getApiErrorModel(context)) } privatefunotherError(e:HttpException) =Gson().fromJson(e.response().errorBody()?.charStream(), ApiErrorModel::class.java) }说明 :
1.每个响应继承Observer,其中的泛型以适配返回的不同的数据体;
2.定义两个抽象方法success和failure,在使用的时候只需关注成功和失败这两种情况;
3.在onSubscribe即开始请求的时候显示Loading,在请求完成或出错时隐藏;
4.在onNext即Observer成功接收数据后直接调用success,在调用处可直接使用返回的数据;
5.在onError即请求出错时处理,此处包含两种情况:连接服务器成功但服务器返回错误状态码、网络或其它问题。
在错误处理中,定义了一个枚举类ApiErrorType,用于列举出服务器定义的错误状态码情况:
/** * 响应状态码处理*/enumclassApiErrorType(valcode:Int, @param: StringRes privatevalmessageId:Int){//灵活定制INTERNAL_SERVER_ERROR(500, R.string.service_error), BAD_GATEWAY(502, R.string.service_error), NOT_FOUND(404, R.string.not_found), CONNECTION_TIMEOUT(408, R.string.timeout), NETWORK_NOT_CONNECT(499, R.string.network_wrong), UNEXPECTED_ERROR(700, R.string.unexpected_error); privatevalDEFAULT_CODE=1fungetApiErrorModel(context:Context): ApiErrorModel{returnApiErrorModel(DEFAULT_CODE, context.getString(messageId)) } }还定义了一个错误消息的的实体类ApiErrorModel(在Kotlin中即为一个数据类),用于包含错误信息提示用户或服务器返回的错误信息以提示开发人员:
data classApiErrorModel(varstatus:Int, varmessage:String)RxJava的一大特色即方便的线程切换操作,在请求API中需要进行线程的切换,通常是以下形式(伪代码):
observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())但每个请求都写一段这个,显得特别麻烦,所以进行以下简单封装:
/** * 线程调度器*/object NetScheduler{fun <T> compose():ObservableTransformer<T,T>{returnObservableTransformer{observable -> observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) } } }使用的时候简单搞定,伪代码如下:
observable.compose(NetScheduler.compose())在Android中,当一个Activity在调API时onDestroy了,需要取消请求,所以此处引入了RxLifecycle进行管理: Activity继承RxAppCompatActivity后,在observable的调用链中加入.bindUntilEvent(this, ActivityEvent.DESTROY)即可,伪代码如下:
observable.compose(NetScheduler.compose()) .bindUntilEvent(this, ActivityEvent.DESTROY) //加入这句 .subscribe(...)在以上准备工作完成后,即可开始使用:
首先在Application中初始化ApiClient:
classApp : Application(){overridefunonCreate(){super.onCreate() RetrofitManager.instance.init(Api.BASE_URL) } }在需要的地方使用ApiClient,点击按钮时,请求数据,成功后用TextView显示出来:
classMainActivity : RxAppCompatActivity(), View.OnClickListener{overridefunonCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) initView() } privatefuninitView(){login.setOnClickListener(this) } /** * 登录测试*/funlogin(){//链式调用RetrofitManager.instance.createService(ApiService::class.java).login(Api.LOGIN_URL,"18612991023","111111") .compose(NetScheduler.compose()) .bindUntilEvent(this, ActivityEvent.DESTROY) .subscribe(object:NetResponseObserver<UserBean>(this){overridefunsuccess(data:UserBean){Toast.makeText(this@MainActivity,data.result.phone,Toast.LENGTH_SHORT).show() } overridefunfailure(statusCode:Int, apiErrorModel:ApiErrorModel){} }) } /** * 点击事件*/overridefunonClick(v:View?){when (v!!.id){R.id.login -> login() R.id.reg -> reg() } } /** * 注册测试*/privatefunreg(){TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } }这种情况只需要对数据类和响应处理进行修改即可。有些后端开发者们,可能将返回体写成如下形式:
{"result":{"headPic": "http://mobile.bwstudent.com/images/small/head_pic/2019-02-26/20190226233015.jpeg", "nickName": "Jr_09b24", "phone": "18612991023", "sessionId": "1553950250180251", "sex": 1, "userId": 251 }, "message": "登录成功", "status": "0000" }所有返回的数据中,最外层都包裹了一层信息,以表示请求成功或失败,中间data才是具体数据,所以定义数据类(实体类)时,需要定义成如下形式:
/** * 实体类:通过jsontokotlin插件生成*/data classUserBean( valmessage:String, valresult:Result, valstatus:String ) data classResult( valheadPic:String, valnickName:String, valphone:String, valsessionId:String, valsex:Int, valuserId:Int )2019年3月31日更新
后续新增上传图片的方法,敬请期待