RxJava development skills 8-seamless integration with REST-RxJava and Retrofit

Source: Internet
Author: User
Tags openweathermap api

RxJava development skills 8-seamless integration with REST-RxJava and Retrofit

In this chapter, we will create a final versionReal worldIn this example, retroing data to remote APIs with Retrofit asynchronously queries data, thus creating a rich UI with no effort.

Project objectives

We will create a newActivity. ThisActivityThe StackExchange API will be used to retrieve the most active 10 users from stackoverflow. With this information, the App will display a user's profile picture, name, fame count, and address list. For each user, the app uses its residential city and OpenWeatherMap API to retrieve local weather forecasts and display a small weather icon. Based on the information retrieved from StackOverflow, the app providesonClickEvent, which will open the personal website they specified in personal information or the personal homepage of Stack Overflow.

Retrofit

Elastic fit is a type-safe REST client designed by Square for Android and Java. It helps you easily interact with any rest api. It perfectly integrates R small Java: All JSON response objects are mapped to original Java objects, and all network calls are based on Rxjava Observable objects.

Using the API documentation, we can define the JSON response data we receive from the server. To easily map JSON response data to our Java code, we will use jsonschema2pojo. This flexible service will generate all the Java classes we need to map JSON response data.

After we have prepared all the Java models, we can start to build the adaptive fit. Retrofi uses standard Java interfaces to map API routes. In the example, we will use a route from the API. below is our ingress fit interface:

public interface StackExchangeService {    @GET("2.2users?order=desc&sort=reputation&site=stackoverflow")    Observable
  
    getMostPopularSOusers(@Query("pagesize") int howmany);}
  

interfaceThe Interface contains only one method, that isgetMostPopularSOusers. This method uses integerhowmanyAs a parameter and returnUserResponse.

When we haveinterface, We can createRestAdapterClass, to better organize our code, we createSeApiManagerFunctions provide a more appropriate way to interact with StackExchange APIs.

public class SeApiManager {    private final StackExchangeService mStackExchangeService;    public SeApiManager() {        RestAdapter restAdapter = new RestAdapter.Builder()            .setEndpoint("https://api.stackexchange.com")            .setLogLevel(RestAdapter.LogLevel.BASIC)            .build();        mStackExchangeService = restAdapter.create(StackExchangeService.class);}public Observable
  
   
    > getMostPopularSOusers(int howmany) {    return mStackExchangeService    .getMostPopularSOusers(howmany)    .map(UsersResponse::getUsers)    .subscribeOn(Schedulers.io())    .observeOn(AndroidSchedulers.mainThread());}
   
  

To simplify the example, we no longer design this class as a singleton that should have been designed. Using dependency injection solutions, such as Dagger2, will lead to higher code quality.

CreateRestAdapterClass. We have created several important points for the API client. In this example, we setendpointAndlog level. Because the URL in this example is hard-coded, it is important to use external resources to store data like this. It is a good practice to avoid hard encoding strings in the code.

Modify fitRestAdapterClass and our API interface are bound together and then created. It returns an object to query the API. We can choose to expose this object directly, or encapsulate it in a certain way to restrict access to it. In this example, we encapsulate it and only exposegetMostPopularSOusersMethod. This method is used to execute the query and let parse fit parse the JSON response data. Obtain the user list and return it to the subscriber. As you can see, with Retrofit, RxJava, and lambda, we have almost no template code: It is very compact and highly readable.

Now we have an API manager to expose a responsive method, which obtains data from the Remote API and sends it to the I/O scheduler, resolution ing finally provides a concise user list for our consumers.

App Architecture

We do not use any MVC, MVP, or MVVM mode. Because it is not the purpose of this book, so ourActivityClass will contain all the logic that we need to create and display the user list.

Create Activity class

We willonCreate()CreateSwipeRefreshLayoutAndRecyclerView; We haverefreshList()To obtain and display the user list,showRefreshing()To manage the progress bar andRecyclerView.

OurrefreshList()The function looks as follows:

private void refreshList() {     showRefresh(true);    mSeApiManager.getMostPopularSOusers(10)        .subscribe(users -> {             showRefresh(false);            mAdapter.updateUsers(users);        }, error -> {             App.L.error(error.toString());            showRefresh(false);        });}

The progress bar is displayed to view the user list from the StackExchange API manager. Once the list data is obtained, we start to display it and update it.AdapterAndRecyclerViewDisplayed as visible.

Create a RecyclerView Adapter

After we get data from the rest api, We need to bind it to the View and fill the list with an adapter. Our RecyclerView adapter is standard. It inherits fromRecyclerView.AdapterAnd specify its ownViewHolder:

public static class ViewHolder extends RecyclerView.ViewHolder {    @InjectView(R.id.name) TextView name;    @InjectView(R.id.city) TextView city;    @InjectView(R.id.reputation) TextView reputation;    @InjectView(R.id.user_image) ImageView user_image;    public ViewHolder(View view) {         super(view);        ButterKnife.inject(this, view);     }}

Once we receive data from the API manager, we can set all the labels on the interface:name,cityAndreputation.

To show the user's profile picture, we will useUniversal Image Loader. UIL is a well-known and tested image management library. We can also use Square's Picasso, Glide, or Facebook's Fresco. This is only based on your hobbies. What's important is that you don't need to repeat the wheel: Libraries help developers live and help them achieve their goals more quickly.

In our adapter, we can do this:

@Overridepublic void onBindViewHolder(SoAdapter.ViewHolder holder, int position) {    User user = mUsers.get(position);    holder.setUser(user); }

InViewHolder, We can do this:

public void setUser(User user) {     name.setText(user.getDisplayName());    city.setText(user.getLocation());    reputation.setText(String.valueOf(user.getReputation()));    ImageLoader.getInstance().displayImage(user.getProfileImage(), user_image);}

At this point, we can allow the code to get a list of users, as shown in:

Search weather forecasts

We increased the difficulty and added the weather of the local city to the list.OpenWeatherMapIs a flexible web service public API. We can query and retrieve many useful forecast information.

As usual, we will map using Retrofit to the API and access it through RxJava. For the StackExchange API, we will createinterface,RestAdapterAnd a flexible Manager:

public interface OpenWeatherMapService {    @GET("data2.5/weather")    Observable
  
   
    
      getForecastByCity(@Query("q") String city);}
    
   
  

This method uses the city name as a parameter to provide local forecast information. We willRestAdapterClasses are bound together:

RestAdapter restAdapter = new RestAdapter.Builder()        .setEndpoint("http://api.openweathermap.org")        .setLogLevel(RestAdapter.LogLevel.BASIC)        .build();mOpenWeatherMapService = restAdapter.create(OpenWeatherMapService.class);

As before, we only need to set the API port and log level: we only need to do two things immediately.

OpenWeatherMapApiManagerClass provides the following methods:

public Observable
  
   
    
     
       getForecastByCity(String city) {    return mOpenWeatherMapService.getForecastByCity(city)    .subscribeOn(Schedulers.io())    .observeOn(AndroidSchedulers.mainThread());}
     
    
   
  

Now that we have a user list, we can query OpenWeatherMap Based on the city name to receive weather forecast information. The next step is to modify ourViewHolderClass to retrieve and use weather forecast information for each user to display the weather icon according to the status.

We use these tools to verify the user homepage information and obtain a legal city name:

private boolean isCityValid(String location) {    int separatorPosition = getSeparatorPosition(location);    return !"".equals(location) && separatorPosition > -1; }private int getSeparatorPosition(String location) {     int separatorPosition = -1;    if (location != null) {        separatorPosition = location.indexOf(",");     }    return separatorPosition; }private String getCity(String location, int position) {    if (location != null) {        return location.substring(0, position);     } else {        return "";     }}

With a valid city name, we can use the following command to obtain all the data of the weather we need:

OpenWeatherMapApiManager.getInstance().getForecastByCity(city)

With the weather response results, we can get the URL of the weather map:

getWeatherIconUrl(weatherResponse);

With the icon URL, we can retrieve the icon itself:

private Observable
  
   
    
     
      
        loadBitmap(String url) { return Observable.create(subscriber -> { ImageLoader.getInstance().displayImage(url,city_image, new ImageLoadingListener() { @Override public void onLoadingStarted(String imageUri, View view) { } @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { subscriber.onError(failReason.getCause()); } @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { subscriber.onNext(loadedImage); subscriber.onCompleted(); } @Override public void onLoadingCancelled(String imageUri, View view) { subscriber.onError(new Throwable("Image loading cancelled")); } }); });}
      
     
    
   
  

ThisloadBitmap()The returned Observable can be linked to the previous one, and finally we can return a separate Observable for this task:

if (isCityValid(location)) {    String city = getCity(location, separatorPosition);    OpenWeatherMapApiManager.getInstance().getForecastByCity(city)        .filter(response -> response != null)        .filter(response -> response.getWeather().size() > 0)        .flatMap(response -> {            String url = getWeatherIconUrl(response);            return loadBitmap(url);        })    .subscribeOn(Schedulers.io())    .observeOn(AndroidSchedulers.mainThread())    .subscribe(new Observer
  
   
    
     
      
       
        () { @Override public void onCompleted() { } @Override public void onError(Throwable e) { App.L.error(e.toString()); } @Override public void onNext(Bitmap icon) { city_image.setImageBitmap(icon); } });}
       
      
     
    
   
  

Run the code to obtain the new weather icon for each user in the following list:

Open website

Using the information contained on the user homepage, we will createonClickThe listener to navigate to the user's web page, if any, or the Stack Overflow personal homepage.

In order to implement it, we simply implementActivityClass interface, used to trigger AndroidonClickEvent.

OurAdapter ViewHolderSpecify this interface:

public interface OpenProfileListener {    public void open(String url); }

ActivityImplement it:

[https://github.com/yuxingxin/RxJava-Essentials-CN/raw/master.] implements SoAdapter.ViewHolder.OpenProfileListener { [https://github.com/yuxingxin/RxJava-Essentials-CN/raw/master.]    mAdapter.setOpenProfileListener(this); [https://github.com/yuxingxin/RxJava-Essentials-CN/raw/master.]@Overridepublic void open(String url) {    Intent i = new Intent(Intent.ACTION_VIEW);    i.setData(Uri.parse(url));     startActivity(i);}

ActivityOpen the URL with an external Android browser. OurViewHolderCreate on each card in the user listOnClickListenerAnd check whether we open the Stack Overflow user homepage or the external personal site:

mView.setOnClickListener(view -> {     if (mProfileListener != null) {        String url = user.getWebsiteUrl();        if (url != null && !url.equals("") && !url.contains("search")) {            mProfileListener.open(url);         } else {            mProfileListener.open(user.getLink());         }    })};

Once we click, We will redirect directly to the expected website. On Android, we can use a special form of RxAndroid (ViewObservable) to achieve the same result in a more responsive way.

ViewObservable.clicks(mView)    .subscribe(onClickEvent -> {    if (mProfileListener != null) {        String url = user.getWebsiteUrl();        if (url != null && !url.equals("") && !url.contains("search")) {             mProfileListener.open(url);        } else {            mProfileListener.open(user.getLink());        }     }});

The above two code snippets are equivalent. You can choose your favorite method.

Summary

Our journey is over. You are ready to bring your Java application to a new level of code quality. You can enjoy a new coding mode and get in touch with your daily coding life in a smoother way of thinking. RxJava provides the opportunity to consider data in a time-oriented manner: Everything is continuously changeable, data is being updated, and events are being triggered. Then you can create responses based on these events, A flexible and smooth App.

Switching to RxJava at the beginning seems difficult and time-consuming, but we have experienced how to effectively handle daily problems in a responsive manner. Now you can migrate your old code to RxJava:gettersA new responsive life.

RxJava is a developing and expanding world. There are still many methods we haven't explored yet. Some methods are not even available. Because of RxJava, you can create your own operators and push them further.

Android is a fun place, but it also has limitations. As an Android developer, you can use RxJava and RxAndroid to overcome many of them. We used AndroidScheduler to briefly mention RxAndroid. Apart from the last chapter, you understandViewObservable. RxAndroid gives you a lot: for example,WidgetObservable,LifecycleObservable. Now it depends on you to push more.

Remember that the observed sequence is like a river: they flow. You can "filter" a river, you can "Switch" a river, you can combine the two rivers into one, and then it still flows. Finally, it will become the river you want.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.