android上传头像 sockettimeoutexception,Kotlin - Retrofit2和Rxjava2封装的网络请求类(含图片上传)...
閱讀建議:本文適合熟悉Retrofit與Rxjava2的同學閱讀,其中也包含一丟丟的RxLifecycle,文中不包含這兩個庫的使用說明。不熟悉RxJava的同學,建議去這里了解,里面包含3篇文章,均通俗易懂。
提示:文中使用的RxJava2的類均是不支持背壓的,即Observable(被觀察者)與Observer(觀察者)。需要背壓策略,請自行替換為對應的Flowable(被觀察者)與Subscriber(觀察者)即可。如果想使用Kotlin的一些便利的語法,可以將RxJava依賴換成RxKotlin即可。
開始
1.按慣例先添加依賴:
//Retrofit相關
compile(['com.squareup.okhttp3:logging-interceptor:3.9.0',//用于查看http請求時的log
'com.squareup.retrofit2:retrofit:2.3.0',
'com.squareup.retrofit2:adapter-rxjava2:2.3.0',
'com.squareup.retrofit2:converter-gson:2.3.0'])
//RxJava相關
compile(['io.reactivex.rxjava2:rxandroid:2.0.1',
'io.reactivex.rxjava2:rxjava:2.1.3']) //此處可換成'io.reactivex.rxjava2:rxkotlin:2.1.0'
//RxLifecycle相關
compile(['com.trello.rxlifecycle2:rxlifecycle-kotlin:2.2.0',
'com.trello.rxlifecycle2:rxlifecycle-components:2.2.0'])
2.封裝請求類
為了秉承RxJava的鏈式調用風格,也為了方便每一個API的調用操作,創建了一個單例類ApiClient,具體如下:
class ApiClient private constructor() {
lateinit var service: GitHubService
private object Holder {
val INSTANCE = ApiClient()
}
companion object {
val instance by lazy { Holder.INSTANCE }
}
fun init() { //在Application的onCreate中調用一次即可
val okHttpClient = OkHttpClient().newBuilder()
//輸入http連接時的log,也可添加更多的Interceptor
.addInterceptor(HttpLoggingInterceptor().setLevel(
if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY
else HttpLoggingInterceptor.Level.NONE
))
.build()
val retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com/") //本文以GitHub API為例
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.build()
service = retrofit.create(GitHubService::class.java)
}
}
其中使用GitHub的的API作為測試,GitHubService如下:
interface GitHubService {
//請添加相應的`API`調用方法
@GET("users/{user}/repos")
fun listRepos(@Path("user") user: String): Observable> //每個方法的返回值即一個Observable
}
上面的Repo即一個簡單的Kotlin數據類,由于字較多,就不貼出來了,具體可去文末Demo地址查找。
3.RESTful API請求響應的處理
API的響應返回形式有很多種,此處介紹最常見的兩種形式的處理:標準RESTful API與任性的后端寫的API。GitHub提供的API即標準RESTful API。
RESTful API的請求響應主要處理狀態碼與數據體,具體封裝如下:
abstract class ApiResponse(private val context: Context) : Observer {
abstract fun success(data: T)
abstract fun failure(statusCode: Int, apiErrorModel: ApiErrorModel)
override fun onSubscribe(d: Disposable) {
LoadingDialog.show(context)
}
override fun onNext(t: T) {
success(t)
}
override fun onComplete() {
LoadingDialog.cancel()
}
override fun onError(e: Throwable) {
LoadingDialog.cancel()
if (e is HttpException) { //連接服務器成功但服務器返回錯誤狀態碼
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) { //發送網絡問題或其它未知問題,請根據實際情況進行修改
is UnknownHostException -> ApiErrorType.NETWORK_NOT_CONNECT
is ConnectException -> ApiErrorType.NETWORK_NOT_CONNECT
is SocketTimeoutException -> ApiErrorType.CONNECTION_TIMEOUT
else -> ApiErrorType.UNEXPECTED_ERROR
}
failure(apiErrorType.code, apiErrorType.getApiErrorModel(context))
}
private fun otherError(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,用于列舉出服務器定義的錯誤狀態碼情況:
enum class ApiErrorType(val code: Int, @param: StringRes private val messageId: 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);
private val DEFAULT_CODE = 1
fun getApiErrorModel(context: Context): ApiErrorModel {
return ApiErrorModel(DEFAULT_CODE, context.getString(messageId))
}
}
還定義了一個錯誤消息的的實體類ApiErrorModel(在Kotlin中即為一個數據類),用于包含錯誤信息提示用戶或服務器返回的錯誤信息以提示開發人員:
data class ApiErrorModel(var status: Int, var message: String)
4.線程與生命周期
RxJava的一大特色即方便的線程切換操作,在請求API中需要進行線程的切換,通常是以下形式(偽代碼):
observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
但每個請求都寫一段這個,顯得特別麻煩,所以進行以下簡單封裝:
object NetworkScheduler {
fun compose(): ObservableTransformer {
return ObservableTransformer { observable ->
observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
}
}
}
使用的時候簡單搞定,偽代碼如下:
observable.compose(NetworkScheduler.compose())
在Android中,當一個Activity在調API時onDestroy了,需要取消請求,所以此處引入了RxLifecycle進行管理:
Activity繼承RxAppCompatActivity后,在observable的調用鏈中加入.bindUntilEvent(this, ActivityEvent.DESTROY)即可,偽代碼如下:
observable.compose(NetworkScheduler.compose())
.bindUntilEvent(this, ActivityEvent.DESTROY) //加入這句
.subscribe(...)
5.使用
以上準備工作完成后,即可開始使用:
首先在Application中初始化ApiClient:
class App : Application() {
override fun onCreate() {
super.onCreate()
ApiClient.instance.init() //這里
}
}
在需要的地方使用ApiClient,如本文Demo,點擊按鈕時,請求數據,成功后用TextView顯示出來:
class MainActivity : RxAppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
submit.setOnClickListener { fetchRepo() } //按鈕點擊事件
}
private fun fetchRepo() {
//鏈式調用
ApiClient.instance.service.listRepos(inputUser.text.toString()) //GitHubService中的方法
.compose(NetworkScheduler.compose()) //線程切換處理
.bindUntilEvent(this, ActivityEvent.DESTROY) //生命周期管理
.subscribe(object : ApiResponse>(this) { //對象表達式約等于Java中的匿名內部類
override fun success(data: List) { //請求成功,此處顯示一些返回的數據
userName.text = data[0].owner.login
repoName.text = data[0].name
description.text = data[0].description
url.text = data[0].html_url
}
override fun failure(statusCode: Int, apiErrorModel: ApiErrorModel) { //請求失敗,此處直接顯示Toast
Toast.makeText(this@MainActivity, apiErrorModel.message, Toast.LENGTH_SHORT).show()
}
})
}
}
效果如下:
效果.gif
6.任性的后端寫的API請求響應的處理
這種情況只需要對數據類和響應處理進行修改即可。有些后端開發者們,可能將返回體寫成如下形式:
{
"code": "200",
"data": [
{
"name": "Tom",
"age": 12,
"money": 100.5
},
{
"name": "Bob",
"age": 13,
"money": 200.5
}
],
"message": "客戶端請求成功"
}
所有返回的數據中,最外層都包裹了一層信息,以表示請求成功或失敗,中間data才是具體數據,所以定義數據類(實體類)時,需要定義成如下形式:
data class ResponseWrapper(var code: Int, var data: T, var message: String)
其中data為泛型,以適配不同的數據體。
然后將上文第3點中的ApiResponse修改如下:
abstract class RequestCallback(private val context: Context) : Observer> {
abstract fun success(data: T)
abstract fun failure(statusCode: Int, apiErrorModel: ApiErrorModel)
private object Status {
val SUCCESS = 200
}
override fun onSubscribe(d: Disposable) {
LoadingDialog.show(context)
}
override fun onNext(t: ResponseWrapper) {
if (t.code == Status.SUCCESS) {
success(t.data)
return
}
val apiErrorModel: ApiErrorModel = when (t.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 -> ApiErrorModel(t.code, t.message)
}
failure(t.code, apiErrorModel)
}
override fun onComplete() {
LoadingDialog.cancel()
}
override fun onError(e: Throwable) {
LoadingDialog.cancel()
val apiErrorType: ApiErrorType = when (e) {
is UnknownHostException -> ApiErrorType.NETWORK_NOT_CONNECT
is ConnectException -> ApiErrorType.NETWORK_NOT_CONNECT
is SocketTimeoutException -> ApiErrorType.CONNECTION_TIMEOUT
else -> ApiErrorType.UNEXPECTED_ERROR
}
failure(apiErrorType.code, apiErrorType.getApiErrorModel(context))
}
}
使用方式:
1.先在GitHubService.kt中新增如下方法:
@GET("xxx/xxx")
fun repos(@Path("user") user: String): Observable>>
2.之后與上文第5點相同。
2017年10月13日更新—增加上傳圖片的方法
新增OkHttpUtil.kt,用于上傳圖片,代碼如下:
object OkHttpUtil {
fun createTextRequestBody(source: String): RequestBody
= RequestBody.create(MediaType.parse("text/plain"), source)
fun createPartWithAllImageFormats(requestKey: String, file: File): MultipartBody.Part
= MultipartBody.Part
.createFormData(requestKey, file.name, RequestBody.create(MediaType.parse("image/*"), file))
}
使用方式:
1.先在GitHubService.kt中新增如下方法:
@Multipart
@POST("xxxx/xxxx") //This is imaginary URL
fun updateImage(@Part("name") name: RequestBody,
@Part image: MultipartBody.Part): Observable
2.在需要的地方使用:
ApiClient.instance.service.updateImage(OkHttpUtil.createTextRequestBody("Bob"),
OkHttpUtil.createPartWithAllImageFormats("avatar",file)) //此處調用OkHttpUtil中的方法
.compose(NetworkScheduler.compose())
.bindUntilEvent(this,ActivityEvent.DESTROY)
.subscribe(object : ApiResponse(this) {
override fun success(data: UserInfo) {
//Do something
}
override fun failure(statusCode: Int, apiErrorModel: ApiErrorModel) {
//Do something
}
})
最后
希望本文對您有所幫助。如果文中有什么表述不當的地方,請在下方評論,以幫助我改正。
總結
以上是生活随笔為你收集整理的android上传头像 sockettimeoutexception,Kotlin - Retrofit2和Rxjava2封装的网络请求类(含图片上传)...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android项目允许命令,androi
- 下一篇: 升级鸿蒙系统效果,鸿蒙系统初体验 全方位