“This is the 21st day of my participation in the Gwen Challenge in November. See details: The Last Gwen Challenge in 2021”

In chapter 24 of the Definitive Guide to Android Programming, there’s a new app called PhotoGallery that allows you to get the latest public images from Flickr, “images without copyright.” This chapter will learn about the Retrofit Network Request library, Json data, Gson parsing Json, and more.

Create a PhotoGallery application

Traditionally, when creating an application, you write down an XML file, again using the fragment method of an activity. Main_activity. XML:

<? The XML version = "1.0" encoding = "utf-8"? > <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/flayout_container" android:layout_width="match_parent" android:layout_height="match_parent" />Copy the code

Add list to fragment: fragment_photo_gallery. XML:

<? The XML version = "1.0" encoding = "utf-8"? > <androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/recyclerview_photo" android:layout_width="match_parent" android:layout_height="match_parent" />Copy the code

MainActivity. Kt:

class MainActivity : AppCompatActivity() { private lateinit var mBinding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mBinding = ActivityMainBinding.inflate(layoutInflater) setContentView(mBinding.root) val isFragmentContainerEmpty = savedInstanceState == null if (isFragmentContainerEmpty){ supportFragmentManager .beginTransaction() .add(R.id.flayout_container, PhotoGalleryFragment. NewInstance ()). The commit ()}}} with the method of checking savedInstanceState judgment above the current Activity is to rebuild or create for the first time, Add the fragment.Copy the code

PhotoGalleryFragment. Kt:

class PhotoGalleryFragment : Fragment() { private lateinit var photoRecyclerView: RecyclerView override fun onCreateView( inflater: LayoutInflater, container: ViewGroup? , savedInstanceState: Bundle? ) : View? { val view = inflater.inflate(R.layout.fragment_photo_gallery, container, false) photoRecyclerView = view.findViewById(R.id.recyclerview_photo) photoRecyclerView.layoutManager = GridLayoutManager(context, 3) return super.onCreateView(inflater, container, savedInstanceState) } companion object { fun newInstance() = PhotoGalleryFragment() } }Copy the code

Currently it runs as an empty page because there is no binding data for RecyclerView.

Retrofit network connection basic

Retrofit “square. Making. IO/Retrofit/” is a square to create and maintain an open source library. But essentially, its HTTP client package uses the OkHttp “square.github. IO/OkHttp/” library.

Retrofit creates HTTP gateway classes. Give Retrofit an interface with annotated methods, and it will do the interface implementation. Retrofit’s interface implementation can initiate HTTP requests and receive HTTP response data that is parsed into an okhttp.responseBody. However, okhttp. ResponseBody cannot be used directly: you need to convert it to the data type your application needs. To solve this problem, you can register a response data converter. Retrofit can then use this converter to convert various data types to and from each other when preparing the data needed for the network request and parsing the data from the network response.

Start by adding Retrofit dependencies to build.gradle:

Implementation 'com. Squareup. Retrofit2: retrofit: 2.9.0'Copy the code
  • Define the Retrofit API

Create a new package API, create a new file, Flickrapi.kt:

import retrofit2.Call
import retrofit2.http.GET

interface FlickrApi {
    @GET("/")
    fun fetchContents(): Call<String>
}
Copy the code

Here each function in the interface corresponds to a specific HTTP request and must be annotated using the HTTP request method.

Common HTTP request types are @get, @POST, @PUT, @delete, and @head.

The @get (“/”) annotation configures the Call returned by the fetchContents() function as a GET request. The string “/” represents a relative path URL — a relative path to the Flickr API endpoint base URL. Most HTTP request method annotations include relative paths. Here, the “/” relative path means that the request will be sent to the base URL we provide later.

All Retrofit network requests return a RetroFIT2.Call object (an executable network request) by default. Executing the Call network request returns a corresponding HTTP network response. (Retrofit can also be configured to return RxJava Observable “current mainstream”)

Call’s generic parameter type is the same data type Retrofit generates after deserializing HTTP response data. Retrofit deserializes HTTP response data into an okhttp.responseBody object by default. Specifying Call tells Retrofit that we need a String object, not an okhttp.responseBody object.

  • Build a Retrofit object and create an API instance

The Retrofit instance is responsible for implementing and creating API interface instances. Generate network requests for the definite-based API interface. Now start building the Retrofit instance.

        val retrofit = Retrofit.Builder()
            .baseUrl("https://www.flickr.com/")
            .addConverterFactory(ScalarsConverterFactory.create())
            .build()
        
        val flickrApi = retrofit.create(FlickrApi::class.java)
Copy the code

Retrofit.builder () is a flow interface for configuring and building Retrofit instances. baseUrl(…) Provides the base URL endpoint to access. Calling build() after setting retrofit.Builder () returns a configured instance of Retrofit.

Add a dependency package, do data type conversion.

Implementation 'com. Squareup. Retrofit2: converter - scalars: 2.9.0'Copy the code

Using addConverterFactory (…). The datatype converter () function adds a specific datatype converter instance. The Retrofit object uses this String data converter to convert the ResponseBody object into a String before returning the Call result. Of course, Square also provides a number of other open source datatypers for Retrofit.

  • Executing network requests

Create a Call request:

 val flickrHomePageRequest : Call<String> = flickrApi.fetchContents()
Copy the code

Note that calling FlickrApi’s fetchContents() does not perform the network request, but rather returns a Call object representing the network request.

Then, in onCreate(savedInstanceState: Bundle?) Call in the enqueue (…). To execute the Call object representing the network request.

        flickrHomePageRequest.enqueue(object : Callback<String> {
            override fun onResponse(call: Call<String>, response: Response<String>) {
                Log.d(TAG, "Response received : ${response.body()}")
            }

            override fun onFailure(call: Call<String>, t: Throwable) {
                Log.e(TAG, "Failed to fetch photos", t)
            }
        })
Copy the code

Retrofit naturally follows two of the most important rules of Android multithreading.

(1) Execute time-consuming tasks only on background threads.

(2) Only perform UI update operations on the main thread.

Call.enqueue(…) The Call function executes the Call object representing the network request. Crucially, it executes network requests on background threads. It’s all managed and scheduled by Retrofit.

The Call object passed to the onResponse() and onFailure() functions is the Call object that initially initiates the network request.

  • Obtain the permission to use the network

Add network permissions to androidmanifest.xml:

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

“Note that this API needs to be accessed through the wall, so, you can find another domestic public API to access.”

  • Use the warehouse model for networking

Here you take the Retrofit configuration code and API networking code and move it into a new class.

private const val TAG = "FlickrFetchr"

class FlickrFetchr {

    private val flickrApi :FlickrApi

    init {
        val retrofit = Retrofit.Builder()
            .baseUrl("https://www.flickr.com/")
            .addConverterFactory(ScalarsConverterFactory.create())
            .build()
        flickrApi = retrofit.create(FlickrApi::class.java)
    }

    fun fetchContents():LiveData<String>{

        val responseLiveData : MutableLiveData<String> = MutableLiveData()

        val flickrHomePageRequest: Call<String> = flickrApi.fetchContents()

        flickrHomePageRequest.enqueue(object : Callback<String> {
            override fun onResponse(call: Call<String>, response: Response<String>) {
                Log.d(TAG, "Response received : ${response.body()}")
                responseLiveData.value = response.body()
            }

            override fun onFailure(call: Call<String>, t: Throwable) {
                Log.e(TAG, "Failed to fetch photos", t)
            }
        })
        return responseLiveData
    }
}
Copy the code

Note that the fetchContents() function returns an unmodifiable LiveData. Modifiable LiveData objects should not be exposed in case they are tampered with by other external code. Data flow in LiveData should remain in one direction.

Then modify the onCreate() method in the PhotoGalleryFragment.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val flickrLiveData: LiveData<String> = FlickrFetchr().fetchContents()
        flickrLiveData.observe(this,
            Observer { responseString ->
                Log.d(TAG, "Response received:$responseString")
            })
    }
Copy the code

It borrows from the repository model advocated by The Google Application Architecture guide. FlickrFetchr acts as a basic repository. This warehouse class encapsulates the logic for retrieving data from one or more data sources. Whether it’s a local database or a remote server, it knows how to get or store all kinds of data. The UI code doesn’t care about getting and saving the data (the warehouse class’s own internal implementation); when you need data, you just go to the warehouse class.

When you run the program, you can see that the log print is the Flickr home page, just like above.

other

This is a little bit too much. Continue to break up, and learn gradually. 💪 🏻

PhotoGallery project Demo

Github.com/visiongem/A…


🌈 follow me ac~ ❤️

Public account: Ni K Ni K