Giới thiệu

Retrofit.
Retrofit là một thư viện hỗ trợ việc kết nối và lấy dữ liệu từ một WebService cho Android và Java, được tạo ra bởi Square. Với retrofit bạn có thể dễ dàng lấy dữ liệu từ webservice, convert dữ liệu trả về dạng json thành các object.

RXjava và Rxandroid.
Rxjava và Rxandroid Là thư viện mã nguồn mở implement ReactiveX trên Java, ReactiveX hỗ trợ lập trình dữ liệu bất đồng bộ, được thiết kế dự trên Observer pattern, nó giúp cho việc sử lý dữ liệu bất đồng bộ một cách hiệu quả , Rxjava cung cấp rất nhiều các operator hỗ trợ cho việc sử lý dữ liệu nhứ filter, groupBy, …

Trong phần nay chúng ta sẽ cũng nhau tìm hiểu cách kết hợp Rxjava và Retrofit để lấy dữ liệu API và hiển thị lên giao diện trong Kotlin

Bắt Đầu

Dưới đây, chúng ta sẽ thực hiện việc lấy thông tin các phiên bản android dựa vào api :https://api.learn2crack.com/android/jsonarray/

1.Thêm các Dependencies cần thiết
RxJava, Retrofit, Card View, Recycler View và ConstraintLayout.

    // ConstraintLayout.
   implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    // Recyclerview
    compile 'com.android.support:recyclerview-v7:26.1.0'
    compile 'com.android.support:cardview-v7:26.1.0'
    compile 'com.android.support:design:26.1.0'

    // RxJava
    compile 'io.reactivex.rxjava2:rxjava:2.0.1'
    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'

    // Retrofit
    compile 'com.squareup.retrofit2:retrofit:2.3.0'
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
    compile 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'

2.Thêm Permissions cho Manifest

<uses-permission android:name="android.permission.INTERNET"/>

3.Tạo layout

  • activity_android_version.xml
    Trong layout activity_android_version.xml chúng ta sẽ sử dụng một RecyclerView bao ngoài là 1 ViewGroup là ConstraintLayout.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_android_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
  • item_android_version.xml
    Tiếp tới là 1 layout cho các item của RecylerView với các TextView để hiển thị Android version name, version number and API level.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="1dp"
    android:background="@color/colorItemAndroidVersion"
    android:foreground="?android:attr/selectableItemBackground">

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:textColor="@android:color/white"
        android:textSize="24sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_version"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:textColor="@android:color/white"
        android:textSize="18sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_name" />

    <TextView
        android:id="@+id/tv_api_level"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:textColor="@android:color/white"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_version" />
</android.support.constraint.ConstraintLayout>

4.Xây dựng Model

  • AndroidVersion
data class AndroidVersion(val name: String,
                          val ver: String,
                          val api: String)

5.Creating Retrofit Interface
Vì ở đây chúng ta sử dụng kết hợp giữa RxJava và Retrofit nên đối tượng trả về của hàm được định nghĩa này cần là 1 Observable.

  • Apiservice
interface ApiService {
    @GET("android/jsonarray/")
    fun getAndroidVersion(): Observable<List<AndroidVersion>>
}

6.Tạo Adapter cho RecyclerView

class AndroidVersionAdapter(private val androidVersionList: List<AndroidVersion>) : RecyclerView.Adapter<AndroidVersionAdapter.ViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_android_version, parent, false)
        return ViewHolder(view)
    }

    override fun getItemCount() = androidVersionList.count()

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(androidVersionList[position])
    }

    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        fun bind(android: AndroidVersion) {
            itemView.tv_name.text = android.name
            itemView.tv_version.text = android.ver
            itemView.tv_api_level.text = android.api

        }
    }
}

7.Tạo Repository
Lớp Repository sử dụng Retrofit builder của Retrofit để tạo ra một rest client mới với url đã cung cấp (BASE_URL)

class Repository {
    companion object {
        private var retrofit: Retrofit? = null
        private var builder: Retrofit.Builder = Retrofit.Builder().baseUrl(Constants.BASE_URL)
                                                                                                      .addConverterFactory(GsonConverterFactory.create()) 
                                                                                                      .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        private val httpClient = OkHttpClient.Builder()
        fun <S> createService(serviceClass: Class<S>): S {
            return createService(serviceClass, null)
        }

        fun <S> createService(serviceClass: Class<S>, authToken: Map<String, String>?): S {
            if (authToken != null) {
                var interceptor = AuthenticationInterceptor(authToken!!)
                if (!httpClient.interceptors().contains(interceptor)) {
                    httpClient.addInterceptor(interceptor)
                    builder.client(httpClient.build())
                    retrofit = builder.build()
                }
            }
            retrofit = builder.build()
            return retrofit!!.create(serviceClass)
        }

    }

    class AuthenticationInterceptor(private val authToken: Map<String, String>) : Interceptor {
        override fun intercept(chain: Interceptor.Chain): Response {
            val original = chain.request()
            val builder = original.newBuilder()
            for (key in authToken.keys) {
                builder.header(key, authToken.getValue(key))
            }
            val request = builder.build()
            return chain.proceed(request)
        }
    }

8.Tạo Activity

class ListAndroidVersionActivity : AppCompatActivity() {
    private val TAG = ListAndroidVersionActivity::class.java.simpleName
    private var mAdapter: AndroidVersionAdapter? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_android_version)
        initRecyclerView()
        requestAndroidVersion()
    }
    // khởi tao recyclerview
    private fun initRecyclerView() {
        rv_android_list.setHasFixedSize(true)
        val layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(this)
        rv_android_list.layoutManager = layoutManager
    }

    // request data tu server
    private fun requestAndroidVersion() {
        Repository.createService(ApiService::class.java).getAndroidVersion()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(
                        //cú pháp của rxjava trong kotlin
                        { result ->
                            //request thành công
                            handleSuccessAndroidVersion(result)
                        },
                        { error ->
                            //request thất bai
                            handlerErrorAndroidVersion(error)
                        }
                )
    }

    //Xử lí dữ liệu khi request thành công
    private fun handleSuccessAndroidVersion(result: List<AndroidVersion>) {
        mAdapter = AndroidVersionAdapter(result)
        rv_android_list.adapter = mAdapter
    }

    //Xử lí dữ l request thất bại
    private fun handlerErrorAndroidVersion(error: Throwable) {
        Log.e(TAG, "handlerErrorAndroidVersion: ${error.localizedMessage}")
        Toast.makeText(this, "Error ${error.localizedMessage}", Toast.LENGTH_SHORT).show()
    }
}

Hình Ảnh

Source Code

https://github.com/oTongXuanAn/viblo_android_kotlin_example

Kết

Ở trên mình đã giới thiệu cơ bản với các bạn về cách kết hợp giữa RxJava 2 và Retrofit trong Kotlin để lấy dữ liệu từ API.
Bài kế tiếp chúng ta sẽ viết lại ví dụ trên với mô hình MVP và MVVP