What are the characteristics of a good web framework?

  • request

Of course, this request is not only about sending a request, it includes whether a series of configuration related to the request is easy, whether the sending mode is flexible, whether the request header information is easy to process, whether the request parameters are easy to operate and so on

  • The response

A good web request framework certainly needs to increase our productivity and ensure the robustness of our applications, so what does the response body have to do with that? Whether entities that can be easily converted to different needs can return different types of structures. XML, JSON, Text, etc

In general, it is easy to use, easy to expand, and highly readable


The purpose of,

  • background

There are many network requests on the market now, from the native HttpClient to OkHttp, Volley, xUtils is undoubtedly the network request simplification, convenience, safety and so on to improve the efficiency of development and the quality of the program. Later, Retrofit, which encapsulates OkHttp3, interfaces the request API, converts the returned data with GSON and other converters into directly develop-oriented objects, greatly improving our development efficiency. In order to solve the Android UI/ sub-thread responsible for the functional scene convenient switch, People started incorporating RxJava, and this led to the popular writing of the Retrofit + OkHttp3 + RxJava web framework,

  • The status quo

Kotlin’s emergence is a redefinition of Google’s ambiguous approach to Android development, not discarding Java but declaring Kotlin the preferred language. On the basis of the jetpack framework, it has become another choice for the development of the new era. From the beginning to the end, Google has not launched an official design mode for Android, and everyone has implemented their own design mode from the aspects of high cohesion and low coupling, so that the development is simple, maintenance is simple and the program is robust. Jetpack is Google’s first official production of design patterns, so what’s the point? Do we need to use the previous web framework on this basis? Are there better frameworks and combinations of frameworks that make our applications robust and easy to develop?

  • jetpack

The specific Jetpack is not explained here. Viewmodel-livedata has been recognized by the majority of developers. The combination of ViewModel and LiveData makes the data controllability better and the coupling degree lower in Android. Simply put, the official observer mode is applied to the real data structure. Here will also be combined with other frameworks to complete the design of the network framework

  • coroutines

Kotlin’s biggest change from Java is the introduction of Coroutines, which can replace RxJava, Thread, multilevel interface callbacks, etc., and have context and various patterns to cater to various scenarios. The details are not explained here.

  • Use the frameworks presented above to complete the encapsulation of the new network request framework

Second, combination framework

2.1 Adding a Dependency


//LifeCycle
    implementation 'androidx. Lifecycle: lifecycle - common: 2.2.0'
    implementation 'androidx. Lifecycle: lifecycle - runtime: 2.2.0'
    implementation 'android. Arch. Lifecycle: extensions: 2.2.0'
    implementation 'androidx. Lifecycle: lifecycle - livedata - KTX: 2.2.0'

//Retrofit
    implementation "Com. Squareup. Retrofit2: retrofit: 2.9.0"
    implementation "Com. Squareup. Okhttp3: logging - interceptor: 4.2.0"
    implementation "Com. Squareup. Retrofit2: converter - gson: 2.9.0"
    implementation 'com. Squareup. Retrofit2: converter - scalars: 2.6.2'
//Coroutines
    implementation 'org. Jetbrains. Kotlinx: kotlinx coroutines - android: 1.3.7'

    //Kotlin extensions for activity and fragments
    implementation 'androidx. Fragments: fragments - KTX: 1.2.5'
    implementation "Androidx. Activity: activity - KTX: 1.1.0." "

    // Kotlin + coroutines
    implementation "Androidx. Work: work - the runtime - KTX: 2.3.4." "

    // optional - RxJava2 support
    implementation "Androidx. Work: work - rxjava2:2.3.4." "

    // optional - GCMNetworkManager support
    implementation "Androidx. Work: work - GCM: 2.3.4." "

    // optional - Test helpers
    androidTestImplementation "Androidx. Work: work - testing: 2.3.4." "
    implementation 'org. Conscrypt: conscrypt - android: 2.2.1'

Copy the code

Add it based on requirements

2.2 Request helper classes

  • State management
enum class Status {
    SUCCESS,
    ERROR,
    LOADING
}
Copy the code
  • Request the result handling class
class Resource<out T>(val status: Status, val data: T? .val message: String?) {
    companion object {
        fun <T> success(data: T?). = Resource(Status.SUCCESS, data.null)
        fun <T> error(msg: String? .data: T?). = Resource(Status.ERROR, data, msg)
        fun <T> loading(data: T?). = Resource(Status.LOADING, data.null)}}Copy the code

2.3 Create APIS and helper classes using Retrofit

Interface management and requests are placed in separate class files for easy management

  • API interface
interface ApiService  {
    @GET("{page}")
    suspend fun getGirls(@Path("page") page: Int): Girls
}
Copy the code

The data classes will be uploaded to the Demo Retrofit + OkHttp3 + Coroutines + LiveData to create a network request framework

  • The API interface class calls the helper class
class ApiHelper(private val apiService: ApiService) {
    suspend fun getGirls(a) = apiService.getGirls(1)}Copy the code

2.4 Create network framework request help classes such as Retrofit and OkHttp

object ServiceCreator {
    private val okHttpClient by lazy { OkHttpClient().newBuilder() }
    private val retrofit: Retrofit by lazy {
        val builder = Retrofit.Builder()
            .baseUrl("https://gank.io/api/v2/data/category/Girl/type/Girl/page/1/count/")
            .addConverterFactory(GsonConverterFactory.create())
        val dispatcher = Dispatcher()
        dispatcher.maxRequests = 1
        val httpLoggingInterceptor = HttpLoggingInterceptor()
        httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
        okHttpClient
            .connectTimeout(10, TimeUnit.SECONDS)
            .writeTimeout(10, TimeUnit.SECONDS)
            .readTimeout(10, TimeUnit.SECONDS)
            .addInterceptor(httpLoggingInterceptor)
            .addInterceptor(com.kpa.network.data.http.interceptor.HttpLoggingInterceptor())
            .dispatcher(dispatcher)
        builder.client(okHttpClient.build()).build()
    }

    fun <T> create(clazz: Class<T>): T = retrofit.create(clazz)

    inline fun <reified T> createService(clazz: Class<T>): T =
        create(clazz)

}
Copy the code

With lazy loading, the desired configuration is configured here and the function is called inline again. Check the advantages of using this.

2.5 Creating a Data Warehouse

Data warehouse is created to be able to process data here. There may be data that needs to be stored or reconstructed. It is also the separation of data processing and ViewModel to do data processing full-time and ViewModel to do data turnover

class MainRepository(private val apiHelper: ApiHelper) {
    suspend fun getGirls(a) = apiHelper.getGirls()
}
Copy the code

2.6 the ViewModel

In general, when using the ViewModel, it is corresponding to one or a group of logically related pages, making the data more independent and clear

class MainViewModel(private val mainRepository: MainRepository) : ViewModel() {
    fun getGirls(a) = liveData(Dispatchers.IO) {
        emit(Resource.loading(null))
        try {
            emit(Resource.success(mainRepository.getGirls()))
        } catch (e: Exception) {
            emit(Resource.error(e.message, null))}}}Copy the code

2.7 Create a ViewMdoel factory to create ViewMdoel

The advantage of this is that you can pass parameters and so on

class ViewModelFactory(private val apiHelper: ApiHelper) : ViewModelProvider.Factory {
    override fun 
        create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
            return MainViewModel(
                MainRepository(apiHelper)
            ) as T
        }
        throw IllegalArgumentException("Unknown class name")}}Copy the code

2.9 Creating an Interface Initialize the help class

Clear interface initialization management together, easy to view

object NetWorkHelper {
    val apiService =
        ServiceCreator.createService(ApiService::class.java)
}

Copy the code

2.8 the use of

Initialize in Activity, Fragment

  • Initialize the
 mainViewModel =
            ViewModelProviders.of(this, ViewModelFactory(ApiHelper(NetWorkHelper.apiService))).get(MainViewModel::class.java)

Copy the code
  • Using the data

Clear callback status, handling different scenarios

mainViewModel.getGirls().observe(this, Observer { it? .let { resource ->when (resource.status) {
                   Status.SUCCESS -> {
                       recyclerView.visibility = View.VISIBLE
                        progressBar.visibility = View.GONE
                        resource.data? .let { girls -> renderList(girls) } } Status.ERROR -> { progressBar.visibility = View.VISIBLE recyclerView.visibility = View.GONE } Status.LOADING -> { progressBar.visibility = View.VISIBLE Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
                    }
                }
            }
        })
Copy the code

Third, summary

In the development can be better interface Databing and other components, more elegant development, for the processing of data ViewMdoel benefits really too much, you can learn more about,

Download the Demo