在本教程中,我们将在安卓应用程序中使用Retrofit实现RxJava调用。我们将创建一个应用程序,该应用程序使用Retrofit和RxJava填充RecEconerView。我们将使用加密货币API。
你会学到什么?
- 使用RxJava进行Retrofit呼叫。
- 使用RxJava进行多个Retrofit呼叫
- 使用RxJava转换Retrofit POJO响应
我们将在Android应用程序中使用Java 8来释放lambda表达式。
概述
Retrofit是一个REST客户端,它使用OkHttp作为HttpClient和JSON解析器来解析响应。我们将使用gson作为JSON解析器。以下是如何创建Retrofit实例:
1HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
2 interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
3 OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
4
5 Gson gson = new GsonBuilder()
6 .setLenient()
7 .create();
8
9 Retrofit retrofit = new Retrofit.Builder()
10 .baseUrl(BASE_URL)
11 .client(client)
12 .addConverterFactory(GsonConverterFactory.create(gson))
13 .build();
HttpLoggingInterceptor用于记录网络调用过程中的数据。RxJava是一个以流的形式用于异步和反应式编程的库。我们在RxJava中使用不同的线程。用于网络调用的后台线程和用于更新UI的主线程。RxJava中的调度器负责使用不同的线程执行操作。RxAndroid 是RxJava的扩展,它包含了要在Android环境中使用的Android线程。要在改造环境中使用RxJava,我们只需要做两个主要更改:
- 在Retrofit Builder中添加RxJava。
- 使用界面中的
Observable
类型,而不是Call
要执行多个调用或转换响应,我们使用[RxJava operators](/community/tutorials/rxjava-operators)。让我们通过下面的示例应用程序来看看它是如何完成的。
项目结构
1implementation 'com.android.support:cardview-v7:27.1.0'
2 implementation 'com.android.support:design:27.1.0'
3 implementation('com.squareup.retrofit2:retrofit:2.3.0')
4 {
5 exclude module: 'okhttp'
6 }
7
8 implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
9 implementation 'io.reactivex.rxjava2:rxjava:2.1.9'
10 implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
11 implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
12 implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'
代码
布局activity_main.xml的代码如下所示。
1<?xml version="1.0" encoding="utf-8"?>
2<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
3 xmlns:tools="https://schemas.android.com/tools"
4 android:layout_width="match_parent"
5 android:layout_height="match_parent"
6 tools:context=".MainActivity">
7
8 <android.support.v7.widget.RecyclerView
9 android:id="@+id/recyclerView"
10 android:layout_width="match_parent"
11 android:layout_height="match_parent" />
12
13</android.support.constraint.ConstraintLayout>
CryptoCurrencyService.java
类的代码如下所示。
1package com.journaldev.rxjavaretrofit;
2
3import com.journaldev.rxjavaretrofit.pojo.Crypto;
4
5import io.reactivex.Observable;
6import retrofit2.http.GET;
7import retrofit2.http.Path;
8
9public interface CryptocurrencyService {
10
11 String BASE_URL = "https://api.cryptonator.com/api/full/";
12
13 @GET("{coin}-usd")
14 Observable<Crypto> getCoinData(@Path("coin") String coin);
15}
@Path将我们指定的路径传递到花括号中。注意 :@Path参数名称必须与@Get中的匹配。POJO类Crypto.java
如下:
1package com.journaldev.rxjavaretrofit.pojo;
2
3import com.google.gson.annotations.SerializedName;
4import java.util.List;
5
6public class Crypto {
7
8 @SerializedName("ticker")
9 public Ticker ticker;
10 @SerializedName("timestamp")
11 public Integer timestamp;
12 @SerializedName("success")
13 public Boolean success;
14 @SerializedName("error")
15 public String error;
16
17 public class Market {
18
19 @SerializedName("market")
20 public String market;
21 @SerializedName("price")
22 public String price;
23 @SerializedName("volume")
24 public Float volume;
25
26 public String coinName;
27
28 }
29
30 public class Ticker {
31
32 @SerializedName("base")
33 public String base;
34 @SerializedName("target")
35 public String target;
36 @SerializedName("price")
37 public String price;
38 @SerializedName("volume")
39 public String volume;
40 @SerializedName("change")
41 public String change;
42 @SerializedName("markets")
43 public List<Market> markets = null;
44
45 }
46}
‘coinName’是我们设置的一个字段。使用RxJava的魔力,我们将在该字段上设置一个值来转换响应。
使用RxJava创建单次调用
1CryptocurrencyService cryptocurrencyService = retrofit.create(CryptocurrencyService.class);
2
3Observable<Crypto> cryptoObservable = cryptocurrencyService.getCoinData("btc");
4cryptoObservable.subscribeOn(Schedulers.io())
5.observeOn(AndroidSchedulers.mainThread())
6.map(result -> result.ticker)
7.subscribe(this::handleResults, this::handleError);
scribeOn()
创建一个Scheduler线程,我们在该线程上执行网络调用。我们可以在其中传递以下任一调度器。
- trampoline():在当前线程上运行任务。因此它将在线程上的当前任务完成后运行您的代码。对执行任务很有用。
- newThread():创建并返回一个线程,该线程为每个工作单元创建一个新的线程。这是昂贵的,因为它每次创建一个单独的线程。
- calculation():创建并返回一个用于计算工作的对象。这应该用于并行工作,因为线程池是绑定的。I/O操作不应该在这里进行。
- io():创建并返回一个用于IO绑定工作的对象。它也像计算一样有界。这通常用于网络呼叫。
订阅打开()与观察打开()
- SubscribeOn在下游和上游工作。它上面和下面的所有任务都将使用相同的线程。
- ObserveOn仅在下游工作。
- 连续的SubscribeOn方法不会更改线程。将只使用第一个订阅线程。
- 连续的serveOn方法将更改线程。
- 在serveOn()之后,放入一个SUBSCRIBE On()不会改变线程。因此,ObserveOn通常应该在订阅On之后。
androidSchedulers.mainThread()
是RxAndroid的一部分,只用于观察主线程上的数据。订阅方法是触发改进调用并在方法handleResults
中获取数据的方法,我们将很快看到这一点。
多个呼叫
我们使用RxJava操作符merge
一个接一个地执行两个改造调用。
1Observable<List<Crypto.Market>> btcObservable = cryptocurrencyService.getCoinData("btc");
2
3Observable<List<Crypto.Market>> ethObservable = cryptocurrencyService.getCoinData("eth");
4
5Observable.merge(btcObservable, ethObservable)
6 .subscribeOn(Schedulers.computation())
7 .observeOn(AndroidSchedulers.mainThread())
8 .subscribe(this::handleResults, this::handleError);
转换响应
要转换POJO响应,我们可以执行以下操作:
1Observable<List<Crypto.Market>> btcObservable = cryptocurrencyService.getCoinData("btc")
2 .map(result -> Observable.fromIterable(result.ticker.markets))
3 .flatMap(x -> x).filter(y -> {
4 y.coinName = "btc";
5 return true;
6 }).toList().toObservable();
7
8 Observable<List<Crypto.Market>> ethObservable = cryptocurrencyService.getCoinData("eth")
9 .map(result -> Observable.fromIterable(result.ticker.markets))
10 .flatMap(x -> x).filter(y -> {
11 y.coinName = "eth";
12 return true;
13 }).toList().toObservable();
14
15 Observable.merge(btcObservable, ethObservable)
16 .subscribeOn(Schedulers.computation())
17 .observeOn(AndroidSchedulers.mainThread())
18 .subscribe(this::handleResults, this::handleError);
我们使用Observable.FromIterable
将地图结果转换为可见流。flatMap‘逐个对元素进行操作。从而将ArrayList转换为单个单数元素。在
filter方法中,我们更改响应。
toList()用于将flatMap的结果转换回列表。
toObservable()`将它们包装为可观察的流。
MainActivity.Java
MainActivity.Java类的代码如下所示:
1package com.journaldev.rxjavaretrofit;
2
3import android.support.v7.app.AppCompatActivity;
4import android.os.Bundle;
5import android.support.v7.widget.LinearLayoutManager;
6import android.support.v7.widget.RecyclerView;
7import android.util.Log;
8import android.widget.Toast;
9
10import com.google.gson.Gson;
11import com.google.gson.GsonBuilder;
12import com.journaldev.rxjavaretrofit.pojo.Crypto;
13
14import java.util.List;
15
16import io.reactivex.Observable;
17import io.reactivex.android.schedulers.AndroidSchedulers;
18import io.reactivex.schedulers.Schedulers;
19import okhttp3.OkHttpClient;
20import okhttp3.logging.HttpLoggingInterceptor;
21import retrofit2.Retrofit;
22import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
23import retrofit2.converter.gson.GsonConverterFactory;
24
25import static com.journaldev.rxjavaretrofit.CryptocurrencyService.BASE_URL;
26
27public class MainActivity extends AppCompatActivity {
28
29 RecyclerView recyclerView;
30 Retrofit retrofit;
31 RecyclerViewAdapter recyclerViewAdapter;
32
33 @Override
34 protected void onCreate(Bundle savedInstanceState) {
35 super.onCreate(savedInstanceState);
36 setContentView(R.layout.activity_main);
37
38 recyclerView = findViewById(R.id.recyclerView);
39 recyclerView.setLayoutManager(new LinearLayoutManager(this));
40 recyclerViewAdapter = new RecyclerViewAdapter();
41 recyclerView.setAdapter(recyclerViewAdapter);
42
43 HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
44 interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
45 OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
46
47 Gson gson = new GsonBuilder()
48 .setLenient()
49 .create();
50
51 retrofit = new Retrofit.Builder()
52 .baseUrl(BASE_URL)
53 .client(client)
54 .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
55 .addConverterFactory(GsonConverterFactory.create(gson))
56 .build();
57
58 callEndpoints();
59 }
60
61 private void callEndpoints() {
62
63 CryptocurrencyService cryptocurrencyService = retrofit.create(CryptocurrencyService.class);
64
65 //Single call
66 /*Observable<Crypto> cryptoObservable = cryptocurrencyService.getCoinData("btc");
67 cryptoObservable.subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).map(result -> result.ticker).subscribe(this::handleResults, this::handleError);*/
68
69 Observable<List<Crypto.Market>> btcObservable = cryptocurrencyService.getCoinData("btc")
70 .map(result -> Observable.fromIterable(result.ticker.markets))
71 .flatMap(x -> x).filter(y -> {
72 y.coinName = "btc";
73 return true;
74 }).toList().toObservable();
75
76 Observable<List<Crypto.Market>> ethObservable = cryptocurrencyService.getCoinData("eth")
77 .map(result -> Observable.fromIterable(result.ticker.markets))
78 .flatMap(x -> x).filter(y -> {
79 y.coinName = "eth";
80 return true;
81 }).toList().toObservable();
82
83 Observable.merge(btcObservable, ethObservable)
84 .subscribeOn(Schedulers.computation())
85 .observeOn(AndroidSchedulers.mainThread())
86 .subscribe(this::handleResults, this::handleError);
87
88 }
89
90 private void handleResults(List<Crypto.Market> marketList) {
91 if (marketList != null && marketList.size() != 0) {
92 recyclerViewAdapter.setData(marketList);
93
94 } else {
95 Toast.makeText(this, "NO RESULTS FOUND",
96 Toast.LENGTH_LONG).show();
97 }
98 }
99
100 private void handleError(Throwable t) {
101
102 Toast.makeText(this, "ERROR IN FETCHING API RESPONSE. Try again",
103 Toast.LENGTH_LONG).show();
104 }
105
106}
handleResults
和handleError
是通过Java 8调用::
来调用的。在handlResults中,我们将转换后的响应设置在Recle erViewAdapter上。如果响应有错误,则调用handleError()。Rececumerview_Item_Layout布局的代码如下所示。
1<?xml version="1.0" encoding="utf-8"?>
2<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
3 xmlns:app="https://schemas.android.com/apk/res-auto"
4 android:layout_width="match_parent"
5 android:layout_height="wrap_content">
6
7 <android.support.v7.widget.CardView
8 android:id="@+id/cardView"
9 android:layout_width="match_parent"
10 android:layout_height="wrap_content"
11 android:layout_gravity="center"
12 android:layout_margin="16dp">
13
14 <android.support.constraint.ConstraintLayout
15 android:layout_width="match_parent"
16 android:layout_height="wrap_content"
17 android:padding="8dp">
18
19 <TextView
20 android:id="@+id/txtCoin"
21 android:layout_width="wrap_content"
22 android:layout_height="wrap_content"
23 android:layout_marginLeft="8dp"
24 android:layout_marginRight="8dp"
25 android:layout_marginTop="8dp"
26 android:textAllCaps="true"
27 android:textColor="@android:color/black"
28 app:layout_constraintHorizontal_bias="0.023"
29 app:layout_constraintLeft_toLeftOf="parent"
30 app:layout_constraintRight_toRightOf="parent"
31 app:layout_constraintTop_toTopOf="parent" />
32
33 <TextView
34 android:id="@+id/txtMarket"
35 android:layout_width="wrap_content"
36 android:layout_height="wrap_content"
37 android:layout_marginLeft="8dp"
38 android:layout_marginRight="8dp"
39 android:layout_marginTop="8dp"
40 app:layout_constraintHorizontal_bias="0.025"
41 app:layout_constraintLeft_toLeftOf="parent"
42 app:layout_constraintRight_toRightOf="parent"
43 app:layout_constraintTop_toBottomOf="@+id/txtCoin" />
44
45 <TextView
46 android:id="@+id/txtPrice"
47 android:layout_width="wrap_content"
48 android:layout_height="wrap_content"
49 android:layout_marginLeft="8dp"
50 android:layout_marginStart="8dp"
51 android:layout_marginTop="8dp"
52 app:layout_constraintHorizontal_bias="0.025"
53 app:layout_constraintLeft_toLeftOf="parent"
54 app:layout_constraintRight_toRightOf="parent"
55 app:layout_constraintTop_toBottomOf="@+id/txtMarket" />
56
57 </android.support.constraint.ConstraintLayout>
58 </android.support.v7.widget.CardView>
59</LinearLayout>
下面给出了RecillerViewAdapter.Java类的代码:
1package com.journaldev.rxjavaretrofit;
2
3import android.graphics.Color;
4import android.support.v4.content.ContextCompat;
5import android.support.v7.widget.CardView;
6import android.support.v7.widget.RecyclerView;
7import android.view.LayoutInflater;
8import android.view.View;
9import android.view.ViewGroup;
10import android.widget.TextView;
11
12import com.journaldev.rxjavaretrofit.pojo.Crypto;
13
14import java.util.ArrayList;
15import java.util.List;
16
17public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
18
19 private List<Crypto.Market> marketList;
20
21 public RecyclerViewAdapter() {
22 marketList = new ArrayList<>();
23 }
24
25 @Override
26 public RecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
27 int viewType) {
28
29 View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item_layout, parent, false);
30
31 RecyclerViewAdapter.ViewHolder viewHolder = new RecyclerViewAdapter.ViewHolder(view);
32 return viewHolder;
33 }
34
35 @Override
36 public void onBindViewHolder(RecyclerViewAdapter.ViewHolder holder, int position) {
37 Crypto.Market market = marketList.get(position);
38 holder.txtCoin.setText(market.coinName);
39 holder.txtMarket.setText(market.market);
40 holder.txtPrice.setText("$" + String.format("%.2f", Double.parseDouble(market.price)));
41 if (market.coinName.equalsIgnoreCase("eth")) {
42 holder.cardView.setCardBackgroundColor(Color.GRAY);
43 } else {
44 holder.cardView.setCardBackgroundColor(Color.GREEN);
45 }
46 }
47
48 @Override
49 public int getItemCount() {
50 return marketList.size();
51 }
52
53 public void setData(List<Crypto.Market> data) {
54 this.marketList.addAll(data);
55 notifyDataSetChanged();
56 }
57
58 public class ViewHolder extends RecyclerView.ViewHolder {
59
60 public TextView txtCoin;
61 public TextView txtMarket;
62 public TextView txtPrice;
63 public CardView cardView;
64
65 public ViewHolder(View view) {
66 super(view);
67
68 txtCoin = view.findViewById(R.id.txtCoin);
69 txtMarket = view.findViewById(R.id.txtMarket);
70 txtPrice = view.findViewById(R.id.txtPrice);
71 cardView = view.findViewById(R.id.cardView);
72 }
73 }
74}
上述实际应用程序的输出如下所示:上述输出合并了通过改造完成的比特币和以太市场价格的结果。本教程到此结束。您可以从下面的链接下载Android RxJavaRetrofit项目。