2015-08-19 13 views
2

我正在寫一些代碼插入一個記錄到Sqlite數據庫(如果表是空的)。在插入任何數據之前,它會使Web服務調用​​返回一些數據。RxJava - 操作員是一項任務還是整個鏈條都是一項任務?

我正在使用SqlBrite進行數據庫訪問和對Web訪問進行改造。這是我的代碼:

Observable.just(LoveToDo.briteDatabase()) 
     .map(new Func1<BriteDatabase, Integer>() { 
      @Override 
      public Integer call(BriteDatabase briteDatabase) { 
       Cursor cursor = briteDatabase.query("SELECT * FROM Accounts"); 

       try { 
        return cursor.getCount(); 

       } finally { 
        cursor.close(); 
       } 
      } 
     }) 
     .flatMap(new Func1<Integer, Observable<Person>>() { 
      @Override 
      public Observable<Person> call(Integer count) { 
       if (count == 0) { 
        return LoveToDo.basecampClient().fetchMe(); 
       } 

       return null; 
      } 
     }) 
     .map(new Func1<Person, Boolean>() { 
      @Override 
      public Boolean call(Person person) { 
       if (person == null) return false; 

       BriteDatabase database = LoveToDo.briteDatabase(); 

       long count = database.insert(Account.TABLE, new Account.Builder() 
        .accountId(Settings.accountId) 
        .userName(Settings.userName) 
        .password(Settings.password) 
        .agent(Settings.agent) 
        .personId(person.id) 
        .build() 
       ); 

       return count > 0; 
      } 
     }) 

     .subscribeOn(Schedulers.io()) 
     .observeOn(Schedulers.io()) 

     .subscribe(); 

不用說,我不認爲這是很棒的代碼。我想要做的是發現如何將此代碼轉換爲好的代碼。所以讓我們用它來挑選它的可怕性。

首先,我應該將數據庫和Web服務調用操作合併到一個運算符中。例如:

Observable.just(LoveToDo.briteDatabase()) 
     .flatMap(new Func1<BriteDatabase, Observable<Person>>() { 
      @Override 
      public Observable<Person> call(BriteDatabase briteDatabase) { 
       Cursor cursor = briteDatabase.query("SELECT * FROM Accounts"); 

       int count; 
       try { 
        count = cursor.getCount(); 

       } finally { 
        cursor.close(); 
       } 

       if (count == 0) { 
        return LoveToDo.basecampClient().fetchMe(); 
       } 

       return null; 
      } 
     }) 
     .map(new Func1<Person, Boolean>() { 
      @Override 
      public Boolean call(Person person) { 
       if (person == null) return false; 

       BriteDatabase database = LoveToDo.briteDatabase(); 

       long count = database.insert(Account.TABLE, new Account.Builder() 
         .accountId(Settings.accountId) 
         .userName(Settings.userName) 
         .password(Settings.password) 
         .agent(Settings.agent) 
         .personId(person.id) 
         .build() 
       ); 

       return count > 0; 
      } 
     }) 

     .subscribeOn(Schedulers.io()) 
     .observeOn(Schedulers.io()) 

     .subscribe(); 

或者是有一個很好的理由,以保持鏈分離出這樣的操作?

錯誤的第二件事是這是一個後臺操作 - 沒有用戶界面將作爲此代碼的結果直接更新。這就是爲什麼有一個無參數的subscribe()函數調用。但是,當發生異常時會發生什麼?這是否意味着我不得不做類似以下的事情?

 .subscribe(new Action1<Boolean>() { 
      @Override 
      public void call(Boolean aBoolean) { 
       // Do nothing 
      } 
     }, new Action1<Throwable>() { 
      @Override 
      public void call(Throwable throwable) { 
       // Do something with the exception 
      } 
     }); 

順便說一句,我還需要subscribeOnobserveOn設置爲後臺線程?

第三,鏈是用SqlBrite觀察者啓動的。在後面的鏈中,我需要再次使用SqlBrite,所以我使用單例LoveToDo.briteDatabase()訪問它。這是一個壞主意嗎?有一個更好的方法嗎?

最後,有沒有什麼辦法可以break;這個鏈?如果我可以放棄我在做的事情,而不是在每一步檢查丟失的數據,這將是很好的

回答

3

我看到很多問題。

  1. 每個方法/操作員是一個「任務」,它將根據以前的項目運行並將項目發送給下一個操作員。
  2. 爲了減少代碼動態變量,我們通常使用RetrolambdaGradle Retrolamda和RxJava。如果你不想使用Retolambda,你可以創建一個類NameModel,它包含Observable創建之前到subscribe()之前的所有邏輯。所有需要的邏輯都是孤立的。
  3. 如果您有網絡電話,那麼在訂閱中始終有一個onError Func是一個好主意,除非您在例如某個地方發現了所有可能的錯誤。與onErrorReturn。 onError可幫助您解決問題,例如,通知用戶。更新訂閱內容而不是內部鏈接也是一種很好的做法,從而隔離運營商的內容。
  4. subscribeOn使進程處於後臺,而不是observeOn。所以不,observeOn不需要,如果你不改變線程,example here
  5. 「打破」鏈條的最佳方式是拋出一個錯誤,或者更復雜的是使用自定義訂閱者自定義.lift()運算符從內部取消訂閱鏈接。

更新基於註釋:

從2以上第二,但我寧願這樣的事情:

Observable.just(LoveToDo.briteDatabase()) 
     .flatMap(briteDatabase -> { 
      Cursor cursor = briteDatabase.query("SELECT * FROM Accounts"); 

      int count; 
      try { 
       count = cursor.getCount(); 

      } finally { 
       cursor.close(); 
      } 

      if (count == 0) { 
       return LoveToDo.basecampClient().fetchMe() 
         .map(person -> insertPerson(person, briteDatabase)); 
      } 

      // if you want to track the account creation 
      return just(false); 
     }) 
     .subscribeOn(Schedulers.io()) 
     .subscribe(personInserted -> { 
      // do something if the person was created or not 
     }, e -> { 
     }); 


private Boolean insertPerson(Person person, BriteDatabase briteDatabase) { 
    long count = briteDatabase.insert(Account.TABLE, new Account.Builder() 
      .accountId(Settings.accountId) 
      .userName(Settings.userName) 
      .password(Settings.password) 
      .agent(Settings.agent) 
      .personId(person.id) 
      .build()); 

    return count > 0; 
} 
+0

至於1點,你會考慮的第一個代碼代碼片段比第二個代碼片段更合適? – Mitkins

+0

@Camel更新了我的答案 – Diolor

+0

謝謝。您提供的示例代碼看起來乾淨而簡單。它還解決了單例問題,並回答了問題的主要觀點:是否可以將邏輯塊放入單個運算符中?所以現在我認爲這個問題的答案是,「是的,如果它讓代碼更容易閱讀/管理」。在這種情況下,我認爲答案是「是」 – Mitkins

相關問題