Kotlin
Has become Android
Google's first recommendation language development, the project has also been used for a long time kotlin, plus Kotlin
1.3 release, kotlin coroutine has stabilized, it is inevitable there will be some of their own thinking.
For the project network request function, we are constantly reflect on how to write elegant, simple, fast and safe. I believe this is a developer you constantly think about. Since our projects are used Retrofit
as a network library, so all thoughts are based Retrofit
deployment.
This article will start with the evolution of my thinking. Kotlin involved coroutine, extension methods, DSL, there is no basis for small partners, first understand these three things, this article will not explain. DSL can see as I write this Introduction
A network request, the implicit question we need to focus on is this: page lifecycle bindings, pages need to be closed after the close network of outstanding requests. To do this, you seniors, Eight Immortals, recount. I also learn from, imitate their predecessors, the transition to self-understanding.
1. Callback
In the first study to use, the Callback
asynchronous method is Retrofit
the most basic use, as follows:
interface:
interface DemoService {
@POST("oauth/login")
@FormUrlEncoded
fun login(@Field("name") name: String, @Field("pwd") pwd: String): Call<String>
}
复制代码
use:
val retrofit = Retrofit.Builder()
.baseUrl("https://baidu.com")
.client(okHttpClient.build())
.build()
val api = retrofit.create(DemoService::class.java)
val loginService = api.login("1", "1")
loginService.enqueue(object : Callback<String> {
override fun onFailure(call: Call<String>, t: Throwable) {
}
override fun onResponse(call: Call<String>, response: Response<String>) {
}
})
复制代码
Here not elaborate.
In the closed network requests when in need onDestroy
call cancel
the method:
override fun onDestroy() {
super.onDestroy()
loginService.cancel()
}
复制代码
In this way, prone to forget to call cancel
method, and network operations and closed the requested operation is separate, is not conducive to management.
This is certainly not an elegant way. With Rx hot, the way network requests of our project, is gradually turning into a way of Rx
2. RxJava
Such use, Baidu, full of tutorials explain, at least in this way can be seen is a program we are more accepted.
In use Rx, we also tried a variety of packages, such as custom Subscriber
, will onNext
, OnCompleted 、
the onError split combination, to meet different needs.
First, Retrofit
add Rx converter inside RxJava2CallAdapterFactory.create()
:
addCallAdapterFactory(RxJava2CallAdapterFactory.create())
复制代码
The use substantially as follows RxJava, first interface Call
to Observable
:
interface DemoService {
@POST("oauth/login")
@FormUrlEncoded
fun login(@Field("name") name: String, @Field("pwd") pwd: String): Observable<String>
}
复制代码
:( use with RxAndroid binding declaration period)
api.login("1","1")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) //RxAndroid
.subscribe(object :Observer<String> {
override fun onSubscribe(d: Disposable) {
}
override fun onComplete() {
}
override fun onNext(t: String) {
}
override fun onError(e: Throwable) {
}
})
复制代码
This use is indeed a lot of convenience, reactive programming idea is also very good, everything is the flow of events. By RxAndroid
switched UI thread binding and page life cycle, the page is closed, automatically cut off the flow of events passed down.
RxJava
The biggest risk is that that is a memory leak, but RxAndroid
does avoid some of the risk of leakage. And by looking at the RxJava2CallAdapterFactory
source code and found that indeed call a cancel
method, ah ...... it looks good too. But always felt RxJava too large, overkill.
3. LiveData
With the release of advancing the project and Google's family bucket. A lightweight version of RxJava
entering into our line of sight, that is LiveData
, LiveData
drawing a lot RxJava
of design ideas, also belong to the category of reactive programming. LiveData
The biggest advantage of that is that the response Acitivty
lifecycle, do not like RxJava
to go to binding declaration period.
Similarly, we first need to add LiveDataCallAdapterFactory (link is written in the official google, you can copy directly to the project) for the retrofit of Callback
convert LiveData
:
addCallAdapterFactory(LiveDataCallAdapterFactory.create())
复制代码
Interface with the following:
interface DemoService {
@POST("oauth/login")
@FormUrlEncoded
fun login(@Field("name") name: String, @Field("pwd") pwd: String): LiveData<String>
}
复制代码
transfer:
api.login("1", "1").observe(this, Observer {string ->
})
复制代码
These are the most basic use, when to use in the project, usually the custom Observer
, used to distinguish between various types of data.
Call the above observe
method, we passed a this
, this this
refers to the statement cycle, we generally AppCompatActivity
when in use, direct transfer themselves on it.
Following is a brief description jump performed at source. By looking at the source code can be found:
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer)
复制代码
Which this
itself is passed LifecycleOwner
.
Then we jump in layers AppCompatActivity
, you will find AppCompatActivity
is inherited from SupportActivity
the parent class:
public class SupportActivity extends Activity implements LifecycleOwner, Component
复制代码
On its own LifecycleOwner
interface implementation. That is, unless special requirements, generally we just need to pass itself on it. LiveData
Automatically monitor data flow and processing unbind.
Generally speaking: the onCreate
data is a one-time binding, the back do not need to re-bound.
When the life cycle come onStart
and onResume
time, LiveData
it will automatically receive an event stream;
When the page is inactive, the pause will receive event stream to recover data when receiving the page resume. (E.g. A jump to B, then A will be suspended received back from B to A as later to restore the data stream received)
When the page onDestroy
when the viewer is automatically deleted, thereby interrupting the flow of events.
It can be seen LiveData
as the official kit, easy to use, response life cycle is very intelligent, and generally do not require the extra processing.
(More advanced usage, you can refer to the official Demo , you can wait to have carried out a set of database cache response package, very nice. I recommend learning package under official ideology, even if not in use, but also for their great benefit)
4. Kotlin coroutine
It says so much, here entered the topic. Under careful observation you will find that the above are used by Retrofit
the enqueue
asynchronous method, and then use Callback
the network callback, even RxJava and Livedata converter, inside is actually used Callback
. Prior to this, Retrofit
the author also wrote a coroutine converter, addresses this , but the interior is still using Callback
, in essence, are the same. (At present, the library was only abandoned, in fact I think that the use of coroutines no sense, Retrofit
in the latest version 2.6.0, directly supported kotlin coroutine suspend
suspend function),
Before looking at Retrofit
the small partners you should know that Retrofit
there is a synchronous and asynchronous invocation methods.
void enqueue(Callback<T> callback);
复制代码
This is way above an asynchronous call, a pass Callback
, this is our most most commonly used method.
Response<T> execute() throws IOException;
复制代码
This is a synchronous call the above method, the thread will block, is returned directly to the network data Response
, it is rarely used.
Later, I was thinking, can not be combined with kotlin coroutine, abandoned Callback
the direct use of Retrofit
synchronization method, when the asynchronous write synchronization code sequence written, logical, high efficiency, synchronous wording is even more convenient to manage objects.
He went ahead.
First, write an extension method of a coroutine:
val api = ……
fun <ResultType> CoroutineScope.retrofit() {
this.launch(Dispatchers.Main) {
val work = async(Dispatchers.IO) {
try {
api.execute() // 调用同步方法
} catch (e: ConnectException) {
e.logE()
println("网络连接出错")
null
} catch (e: IOException) {
println("未知网络错误")
null
}
}
work.invokeOnCompletion { _ ->
// 协程关闭时,取消任务
if (work.isCancelled) {
api.cancel() // 调用 Retrofit 的 cancel 方法关闭网络
}
}
val response = work.await() // 等待io任务执行完毕返回数据后,再继续后面的代码
response?.let {
if (response.isSuccessful) {
println(response.body()) //网络请求成功,获取到的数据
} else {
// 处理 HTTP code
when (response.code()) {
401 -> {
}
500 -> {
println("内部服务器错误")
}
}
println(response.errorBody()) //网络请求失败,获取到的数据
}
}
}
}
复制代码
The above is the core code, the main meaning of the written comments. Ui entire workflow coroutine out, it is possible to operate freely UI controls, followed io thread to call the network synchronization request, and waits io finished thread, followed by processing the results to get the entire process is based on writing synchronization code, a step in the process, did not back off and the code lead to a sense of fragmentation. So to continue, we think of ways to get data back out.
Here we use the DSL method, first customize a class:
class RetrofitCoroutineDsl<ResultType> {
var api: (Call<ResultType>)? = null
internal var onSuccess: ((ResultType?) -> Unit)? = null
private set
internal var onComplete: (() -> Unit)? = null
private set
internal var onFailed: ((error: String?, code, Int) -> Unit)? = null
private set
var showFailedMsg = false
internal fun clean() {
onSuccess = null
onComplete = null
onFailed = null
}
fun onSuccess(block: (ResultType?) -> Unit) {
this.onSuccess = block
}
fun onComplete(block: () -> Unit) {
this.onComplete = block
}
fun onFailed(block: (error: String?, code, Int) -> Unit) {
this.onFailed = block
}
}
复制代码
Such exposure outside the three methods: onSuccess
, , onComplete
, onFailed
used to classify data is returned.
Then, we have to transform our core code, the method will be passed:
fun <ResultType> CoroutineScope.retrofit(
dsl: RetrofitCoroutineDsl<ResultType>.() -> Unit //传递方法,需要哪个,传递哪个
) {
this.launch(Dispatchers.Main) {
val retrofitCoroutine = RetrofitCoroutineDsl<ResultType>()
retrofitCoroutine.dsl()
retrofitCoroutine.api?.let { it ->
val work = async(Dispatchers.IO) { // io线程执行
try {
it.execute()
} catch (e: ConnectException) {
e.logE()
retrofitCoroutine.onFailed?.invoke("网络连接出错", -100)
null
} catch (e: IOException) {
retrofitCoroutine.onFailed?.invoke("未知网络错误", -1)
null
}
}
work.invokeOnCompletion { _ ->
// 协程关闭时,取消任务
if (work.isCancelled) {
it.cancel()
retrofitCoroutine.clean()
}
}
val response = work.await()
retrofitCoroutine.onComplete?.invoke()
response?.let {
if (response.isSuccessful) {
retrofitCoroutine.onSuccess?.invoke(response.body())
} else {
// 处理 HTTP code
when (response.code()) {
401 -> {
}
500 -> {
}
}
retrofitCoroutine.onFailed?.invoke(response.errorBody(), response.code())
}
}
}
}
}
复制代码
Here using DSL delivery method, can be more needs to be passed, for example, only you need onSuccess
, then just pass this way does not have to pass all three, on-demand use.
Use:
According to the official document kotlin first need to transform the next activity:
abstract class BaseActivity : AppCompatActivity(), CoroutineScope {
private lateinit var job: Job // 定义job
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job // Activity的协程
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
job = Job()
}
override fun onDestroy() {
super.onDestroy()
job.cancel() // 关闭页面后,结束所有协程任务
}
}
复制代码
Activity
Implement CoroutineScope
interface according to the current can directly context
using the acquired coroutine.
Then there is the real use, you can call this extension method in any position:
retrofit<String> {
api = api.login("1","1")
onComplete {
}
onSuccess { str ->
}
onFailed { error, code ->
}
}
复制代码
In sometimes, we just need to deal with onSuccess
the situation, we do not care about the other two. Then write directly to:
retrofit<String> {
api = api.login("1","1")
onSuccess { str ->
}
}
复制代码
Which you need to write what code is very clean.
As can be seen, then we do not need a separate network requests to bind to the life cycle, when the page is destroyed, job
it was closed, when the coroutine is turned off, it will cancel the calling method Retrofit of the closed network.
5. Section
Coroutine is less than the cost of Thread
multi-threaded, fast response, ideal for lightweight workflow. For coroutine, as well as to take me deeper thinking and learning. Coroutine is not Thread
a substitute for, or more asynchronous tasks to add one more, we can not follow the inertia of thinking to understand the coroutine, but to its own characteristics and more start to develop it more comfortable use. And with the Retrofit 2.6.0
release, it comes with a new coroutine program, as follows:
@GET("users/{id}")
suspend fun user(@Path("id") long id): User
复制代码
Increased suspend
pending function support, visible coroutine applications will become increasingly popular.
All of the above mentioned network processing methods, either Rx
or LiveData
, it is a very good package, no good or bad art. I coroutine packages, perhaps not the best, but we can not lack of thinking, exploration, practice three elements, think about do it.
The best answer is always given himself.
The first written record of this type of article, the process of serious, rigorous record is not, forgive me. Thank you for reading