2016-01-12 229 views
7

我想運行2個異步任務,其中一個跟隨另一個(按順序)。我已經閱讀了一些關於ZIP或Flat的內容,但是我不太瞭解它...如何在Android RxJava Observable中按順序運行2個查詢?

我的目的是從本地SQLite加載數據,當它完成時,它將查詢調用到服務器遠程)。

可有人建議我,一個方法來實現這一目標?

這是RxJava可觀察到骨骼,我使用(單任務):

// RxJava Observable 
    Observable.OnSubscribe<Object> onSubscribe = subscriber -> { 
     try { 

      // Do the query or long task... 

      subscriber.onNext(object); 
      subscriber.onCompleted(); 
     } catch (Exception e) { 
      subscriber.onError(e); 
     } 
    }; 

    // RxJava Observer 
    Subscriber<Object> subscriber = new Subscriber<Object>() { 
     @Override 
     public void onCompleted() { 
      // Handle the completion 
     } 

     @Override 
     public void onError(Throwable e) { 
      // Handle the error 
     } 

     @Override 
     public void onNext(Object result) { 

      // Handle the result 

     } 
    }; 

    Observable.create(onSubscribe) 
      .subscribeOn(Schedulers.newThread()) 
      .observeOn(AndroidSchedulers.mainThread()) 
      .subscribe(subscriber); 
+1

[RxJava:chaining observables]的可能重複(http://stackoverflow.com/questions/26935821/rxjava-chaining-observables) –

回答

6

做到這一點的操作將是merge,看到http://reactivex.io/documentation/operators/merge.html

我的方法是創建兩個觀測,讓我們說observableLocalobservableRemote,併合並輸出:

Observable<Object> observableLocal = Observable.create(...) 
Observable<Object> observableRemote = Observable.create(...) 
Observable.merge(observableLocal, observableRemote) 
      .subscribe(subscriber) 

如果你想確保遠程運行後的地方,就可以使用concat

+1

這隻有在observableLocal和observableRemote返回相同的對象或這個對象擴展的東西 – Sniper

6

盧卡斯Batteau的回答是最好的,如果查詢不依賴於彼此。但是,如果你之前獲得來自本地SQLite查詢的數據,需要在運行遠程查詢(例如,你需要爲遠程查詢參數或標題中的數據),那麼你可以與當地觀察的開始,然後flatmap這兩個觀測後您從本地查詢獲得的數據結合起來:

Observable<Object> localObservable = Observable.create(...) 
    localObservable.flatMap(object -> 
    { 
     return Observable.zip(Observable.just(object), *create remote observable here*, 
      (localObservable, remoteObservable) -> 
      { 
       *combining function* 
      }); 
    }).subscribe(subscriber); 

的flatmap功能允許您通過壓縮功能的地方觀察到轉化爲本地&遠程觀測的組合。並且重申一下,這裏的優點是兩個觀測值是連續的,並且zip函數只會在兩個相關的觀測值運行之後才運行。

此外,壓縮功能可以讓你即使底層的對象有不同類型的觀測相結合。在這種情況下,您提供了一個組合函數作爲第三個參數。如果基礎數據是相同的類型,則用合併替換zip函數。

1

你可以試試我的解決方案,有幾種方法來解決你的問題。
爲了確保它的工作,我創建了一個獨立的工作示例和使用此API測試:https://jsonplaceholder.typicode.com/posts/1

private final Retrofit retrofit = new Retrofit.Builder() 
      .baseUrl("https://jsonplaceholder.typicode.com/posts/") 
      .addConverterFactory(GsonConverterFactory.create()) 
      .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 
      .build(); 

    private final RestPostsService restPostsService = retrofit.create(RestPostsService.class); 

    private Observable<Posts> getPostById(int id) { 
     return restPostsService.getPostsById(id); 
    } 

RestPostService。java的

package app.com.rxretrofit; 

import retrofit2.http.GET; 
import retrofit2.http.Path; 
import rx.Observable; 

/** 
* -> Created by Think-Twice-Code-Once on 11/26/2017. 
*/ 

public interface RestPostsService { 

    @GET("{id}") 
    Observable<Posts> getPostsById(@Path("id") int id); 
} 

解決方法1時使用的序列調用多個任務,以前的任務的結果永遠是下一個任務

getPostById(1) 
       .concatMap(posts1 -> { 
        //get post 1 success 
        return getPostById(posts1.getId() + 1); 
       }) 
       .concatMap(posts2 -> { 
        //get post 2 success 
        return getPostById(posts2.getId() + 1); 
       }) 
       .concatMap(posts3 -> { 
        //get post 3success 
        return getPostById(posts3.getId() + 1); 
       }) 
       .subscribeOn(Schedulers.io()) 
       .observeOn(AndroidSchedulers.mainThread()) 
       .subscribe(finalPosts -> { 
        //get post 4 success 
        Toast.makeText(this, "Final result: " + finalPosts.getId() + " - " + finalPosts.getTitle(), 
          Toast.LENGTH_LONG).show(); 
       }); 

的輸入溶液2當按順序調用多個任務時使用,以前任務的所有結果都是in最後的任務的認沽(例如:上傳頭像圖片封面圖片後,調用API來使用這些圖像的URL創建新的用戶)

Observable 
       .zip(getPostById(1), getPostById(2), getPostById(3), (posts1, posts2, posts3) -> { 
        //this method defines how to zip all separate results into one 
        return posts1.getId() + posts2.getId() + posts3.getId(); 
       }) 
       .flatMap(finalPostId -> { 
        //after get all first three posts, get the final posts, 
        // the final posts-id is sum of these posts-id 
        return getPostById(finalPostId); 
       }) 
       .subscribeOn(Schedulers.io()) 
       .observeOn(AndroidSchedulers.mainThread()) 
       .subscribe(finalPosts -> { 
        Toast.makeText(this, "Final posts: " + finalPosts.getId() + " - " + finalPosts.getTitle(), 
          Toast.LENGTH_SHORT).show(); 
       }); 

AndroidManifest

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

root build.gradle

// Top-level build file where you can add configuration options common to all sub-projects/modules. 

buildscript { 
    repositories { 
     jcenter() 
    } 
    dependencies { 
     classpath 'com.android.tools.build:gradle:2.3.3' 
     classpath 'me.tatarka:gradle-retrolambda:3.2.0' 
     classpath 'me.tatarka.retrolambda.projectlombok:lombok.ast:0.2.3.a2' 

     // NOTE: Do not place your application dependencies here; they belong 
     // in the individual module build.gradle files 
    } 

    // Exclude the version that the android plugin depends on. 
    configurations.classpath.exclude group: 'com.android.tools.external.lombok' 
} 

allprojects { 
    repositories { 
     jcenter() 
    } 
} 

task clean(type: Delete) { 
    delete rootProject.buildDir 
} 

應用/的build.gradle

apply plugin: 'me.tatarka.retrolambda' 
apply plugin: 'com.android.application' 

android { 
    compileSdkVersion 26 
    buildToolsVersion "26.0.1" 
    defaultConfig { 
     applicationId "app.com.rxretrofit" 
     minSdkVersion 15 
     targetSdkVersion 26 
     versionCode 1 
     versionName "1.0" 
     testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 
    } 
    buildTypes { 
     release { 
      minifyEnabled false 
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 
     } 
    } 

    compileOptions { 
     sourceCompatibility JavaVersion.VERSION_1_8 
     targetCompatibility JavaVersion.VERSION_1_8 
    } 
} 

dependencies { 
    compile fileTree(dir: 'libs', include: ['*.jar']) 
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 
     exclude group: 'com.android.support', module: 'support-annotations' 
    }) 
    compile 'com.android.support:appcompat-v7:26.+' 
    compile 'com.android.support.constraint:constraint-layout:1.0.2' 
    testCompile 'junit:junit:4.12' 

    provided 'org.projectlombok:lombok:1.16.6' 
    compile 'com.squareup.retrofit2:retrofit:2.3.0' 
    compile 'com.squareup.retrofit2:converter-gson:2.3.0' 
    compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0' 
    compile 'io.reactivex:rxandroid:1.2.1' 
} 

模式

package app.com.rxretrofit; 
import com.google.gson.annotations.SerializedName; 
/** 
* -> Created by Think-Twice-Code-Once on 11/26/2017. 
*/ 
public class Posts { 
    @SerializedName("userId") 
    private int userId; 
    @SerializedName("id") 
    private int id; 
    @SerializedName("title") 
    private String title; 
    @SerializedName("body") 
    private String body; 
    public int getUserId() { 
     return userId; 
    } 
    public void setUserId(int userId) { 
     this.userId = userId; 
    } 
    public int getId() { 
     return id; 
    } 
    public void setId(int id) { 
     this.id = id; 
    } 
    public String getTitle() { 
     return title; 
    } 
    public void setTitle(String title) { 
     this.title = title; 
    } 
    public String getBody() { 
     return body; 
    } 
    public void setBody(String body) { 
     this.body = body; 
    } 
} 

順便說一句,使用RX +改造+匕首+ MVP模式是一個偉大的組合。