RxJava 是一個在 Java 平台上實現反應式編程(Reactive Programming)的庫。它允許您以更簡單和優雅的方式處理異步事件和數據流。RxJava 的核心概念是可觀察對象(Observables)和觀察者(Observers),它們之間建立了一個異步的數據流。
https://jsonplaceholder.typicode.com/comments
這個網址。 implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
//Retrofit
implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
implementation 'io.reactivex.rxjava3:rxjava:3.1.6'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
//RxJava
<uses-permission android:name="android.permission.INTERNET" />
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/result"
android:layout_width="339dp"
android:layout_height="368dp"
android:text="TextView"
android:gravity="center"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="@color/black"
android:background="@drawable/border"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline"
app:layout_constraintHorizontal_bias="0.428"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/guideline3"
app:layout_constraintVertical_bias="0.069" />
<Button
android:id="@+id/search"
android:layout_width="183dp"
android:layout_height="126dp"
android:text="搜尋"
android:textSize="30sp"
android:backgroundTint="@color/nae"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline"
app:layout_constraintHorizontal_bias="0.502"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@+id/number"
app:layout_constraintVertical_bias="0.73" />
<Spinner
android:id="@+id/number"
android:layout_width="344dp"
android:layout_height="38dp"
android:background="@drawable/border"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@+id/parameter"
app:layout_constraintVertical_bias="0.12" />
<Spinner
android:id="@+id/parameter"
android:layout_width="344dp"
android:layout_height="38dp"
android:background="@drawable/border"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@+id/result"
app:layout_constraintVertical_bias="0.099" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.96107054" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.05109489" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="20dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
這次我的設計為使用兩個Spinner選取參數,點選按鈕後就會把Spinner所選取的選項給傳入抓取API資料的方法,讓它依照這個參數去取得我想要的資料,且這次我也有使用到drawble資源,外框使用的是胡桃色,並且也將按鈕更改為更好看的顏色。
這邊附上我使用的顏色及色碼
<color name="kurumi">#947A6D</color>
<color name="nae">#86C166</color>
Retrofit的環境建立基本架構是一樣的,不過多加了這一串
.addCallAdapterFactory(retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory.create())
這個是在讓Retrofit可以跟RxJava連結,所以整體就會長這樣
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ApiClient {
public Retrofit getCommentApi(){
return new Retrofit.Builder()
.baseUrl("https://jsonplaceholder.typicode.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory.create())
//使用RxJava需要多添加這個
.build();
}
}
import java.util.List;
import io.reactivex.rxjava3.core.Observable;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface GetApi {
@GET("comments")//一樣使用Get要取Api資料
Observable<List<DataResponse>> getCommentsData(
//使用Observable告知有這個數據流可以觀測,後續才可以透過subscribe去訂閱這個數據流
@Query("id") int id
//這邊設定這個數據流有一個參數(id),屆時就要輸入id才可以搜尋
);
}
RxJava的寫法會用到Observable
,它會指定哪個東西是可以被觀測的數據流,到後面要調用的時候就可以subscribe
,接著看到@Query
,這個是在設定這個Api的參數,像這裡就設定了這個Api的參數是id
,要注意()內的參數一定要是該Api資料需要的參數,且不能夠有任何差錯,否則等待你的只會有資料抓取失敗
的結果而已!
整體架構還是一樣,這邊就不細說了
public class DataResponse {
private int postId;
private int id;
private String name;
private String email;
private String body;
@Override
public String toString() {
return "DataResponse{" +
"\npostId=" + postId +
",\n id=" + id +
",\n name='" + name + '\'' +
",\n email='" + email + '\'' +
",\n body='" + body + '\'' +
'}';
}
public int getPostId() {
return postId;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public String getBody() {
return body;
}
}
這次的實作寫的東西比較多放上來有點影響觀感,這邊附上這次的GitHub
首先創立Retrofit的方法還是一樣先宣告
private ApiClient apiClient;
private GetApi getApi;
然後就是初始化跟綁定
apiClient = new ApiClient();
getApi = apiClient.getCommentApi().create(GetApi.class);
因為所有程式都放在onCreate裡面會有點太攏長,所以這次我選擇將它們包出去,這樣就可以讓程式比較清楚,且好整理
setSpinner();
selectedData();
search.setOnClickListener(view -> getComment(selected_number,selected_parameter));
可以看到我的按鈕的點擊事件,裡面寫了抓取本次Api資料的方法,並且傳入參數
private void setSpinner() {
ArrayAdapter parameter = new ArrayAdapter(
this,
android.R.layout.simple_spinner_dropdown_item,
parameter_data
);
ArrayList<Integer> numbers = new ArrayList<>();
for (int i = 0; i < 100; i++) {
numbers.add(i+1);
}
ArrayAdapter number = new ArrayAdapter(
this,
android.R.layout.simple_spinner_dropdown_item,
numbers
);
parameter_spinner.setAdapter(parameter);
number_spinner.setAdapter(number);
}//Spinner的基本設定
設定Spinner的方法就跟前面教的一樣。
parameter_spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
selected_parameter = parameter_spinner.getSelectedItem().toString();
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {}
});
spinner的點擊事件,可以看到我在有選取選項的方法內寫入,將選取的資料丟給selected_parameter紀錄,number的部分寫法也一樣,只是記得要加上轉換成整數的函式Integer.parseInt()
private void getComment(Integer selectedNumber, String selectedParameter) {
getApi.getCommentsData(selectedNumber)
.observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(new DisposableObserver<List<DataResponse>>() {
@Override
public void onNext(@NonNull List<DataResponse> dataResponses) {
switch (selectedParameter){
case "postId":
result.setText("postId : "+dataResponses.get(0).getPostId());
break;
case "name":
result.setText("name : "+dataResponses.get(0).getName());
break;
case "email":
result.setText("email : "+dataResponses.get(0).getEmail());
break;
case "body":
result.setText("body : "+dataResponses.get(0).getBody());
break;
case "all":
result.setText("all : "+dataResponses.get(0).toString());
break;
}
}
@Override
public void onError(@NonNull Throwable e) {
result.setText("抓取資料失敗");
}
@Override
public void onComplete() {;
}
});
}//結合RxJava的Retrofit
首先看到這個部分
getApi.getCommentsData(selectedNumber)
.observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(new DisposableObserver<List<DataResponse>>()
第一行就跟上一篇教的概念一樣,使用getApi裡面的getCommentData去抓資料,並且因為在GetApi有用到Observable觀察這個數據流,所以這裡就要設定在哪個線程(theard)
觀測及訂閱,這個會影響的是有時候可能資料太龐大或者網路不好,種種原因導致資料取得太慢,就會跟著影響整個程式的流程,這個時候切換線程
將複雜且可能出錯的地方放到副線程,就能讓它不會影響主要程式在做的事情,也就不會耽誤到整個程式的流程。
接著到第二行,.observeOn(Schedulers.io())
這裡做的就是告知要在哪個線程進行觀測,而這裡指定了要在io線程進行。
第三行,.subscribeOn(AndroidSchedulers.mainThread())
,這裡在設定要在哪個線程進行訂閱,也會連帶影響下面的onNext
、onError
、onComplete
會在哪個線程工作,現在這個就是指定在主線程進行訂閱的工作。
第四行,.subscribe(new DisposableObserver<List<DataResponse>>()
,這裡是在設定訂閱的型態,後面的new有不同種可以選擇使用,現在這個跟其他的相比多了取消訂閱的功能,用意是當這筆資料暫時不用被觀測時,就把它給取消訂閱,這樣可以減少資源的消耗。
最後看到onNext
、onError
、onComplete