1. 程式人生 > >使用RxAndroid配合MVP模式實現非同步網路請求,更新ui

使用RxAndroid配合MVP模式實現非同步網路請求,更新ui

各位好,寫這篇文章主要是我在工作時發現了一個問題,現在使用RxAndroid解決了。

問題是:

我工作的專案是使用mvp模式寫的,(關於什麼是mvp,可參考:http://blog.csdn.net/lmj623565791/article/details/46596109)

而大家應該都知道,如果我們沒有開啟執行緒,則你程式碼裡寫的所有的任務將都在主執行緒中執行。

而主執行緒是不能執行耗時操作的(如:網路操作、訪問資料庫等)。

如果我們需要請求網路獲取了資料之後更新ui介面,我們就必須開啟一個執行緒來訪問網路,獲取了資料之後更新介面元素。

Activity中有個runOnUiThread的方法,可以使其在ui執行緒中執行。

而問題在於我的專案是使用mvp模式,m:處理邏輯,然後將處理的邏輯交給p層,p:是m層與v層的紐帶。

而我執行網路操作是放在m層處理的,如果要用runOnUiThread這個方法就得把activity傳進來,或者最後傳到activity去,這都會亂了結構,使程式碼凌亂不堪。

在找解決辦法時,我遇見了RxAndroid。(關於RxAndroid的基本使用,可參考:http://www.jianshu.com/p/51a8d2ff8697)

RxAndroid是RxJava的一個變體,它們都屬於函式響應式程式設計

RxAndroid有什麼作用呢?

1、函式響應式程式設計
2、非同步
3、事件驅動(事件作為可觀察序列)
4、基於觀察者模式
5、組合式
6、專門出錯處理
7、適用於處理併發問題

這不是我所注重,我最注重的是:提供了可設定計算的所線上程以及更新 UI 時可在主執行緒更新。

這裡關於mvp模式與RxAndroid使用啥的就不說了,我主要將它們兩者結合。

下面demo環節:

一個很簡單的demo,就是模擬登入操作。

結構如下:


biz:資料處理層

ApiData:模擬後臺資料

LoginBiz:mvp中的m層,用於處理登入邏輯

User:使用者實體物件

view:介面層

ILoginView:登入介面,是一個介面,如:展示進度條、隱藏進度條、展示資料等

MainActivity:ILoginView的實現類

persenter:連線view與biz的紐帶

LoginPersenter:請求LoginBiz獲取資料,使用ILoginView層更新介面。

程式碼環節:

MainActivity程式碼:

public class MainActivity extends Activity implements ILoginView {

    private EditText editText;
    private TextView textView;
    private LoginPresenter loginPresenter;
    private ProgressBar progressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editText = (EditText) findViewById(R.id.editText);
        textView = (TextView) findViewById(R.id.textView);
        progressBar = (ProgressBar) findViewById(R.id.progressBar);

        loginPresenter = new LoginPresenter(this);
    }



    public void onClick(View view){
        String userName = editText.getText().toString();
        loginPresenter.doLogin(userName);
    }

    @Override
    public void showView(String userName) {
        textView.setText(userName);
    }

    @Override
    public void showLoadding() {
        progressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideLoadding() {
        progressBar.setVisibility(View.GONE);
    }
}

ILoginView程式碼:

public interface ILoginView {

    void showView(String userName);
    void showLoadding();
    void hideLoadding();
}

layout程式碼:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="使用者名稱"/>

    <EditText
        android:id="@+id/editText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="DO"
        android:onClick="onClick"/>

    <ProgressBar
        style="?android:attr/progressBarStyleLarge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/progressBar"
        android:layout_gravity="center_horizontal"
        android:visibility="gone"/>

</LinearLayout>

實體類User程式碼:
public class User {

    public String name;
    public String pwd;
}
LoginBiz程式碼:
public class LoginBiz {

    private String TAG = "loginBiz";

    public void doLogin(final OnGetDataListener<User> listener, final User user){
        rx.Observable.create(new Observable.OnSubscribe<User>() {
            @Override
            public void call(Subscriber<? super User> subscriber) {
                // 模擬執行網路操作
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                User user2 = new ApiData().doLogin(user);
                subscriber.onNext(user2);
                Log.e(TAG, "------->call執行緒:" + Thread.currentThread().getName());
            }
        }).subscribeOn(Schedulers.newThread())      // 子執行緒執行網路操作
          .observeOn(AndroidSchedulers.mainThread()) // 轉回主執行緒
          .subscribe(new Subscriber<User>() {
              @Override
              public void onCompleted() {
              }

              @Override
              public void onError(Throwable throwable) {
              }

              @Override
              public void onNext(User user) {
                  if (user != null) {
                      listener.success(user);
                  }else{
                      listener.fail(user,"使用者名稱錯誤");
                  }
              }
          });
    }
}

可見使用  Schedulers.newThread() 是新見了一個執行緒執行的網路操作,使用observeOn(AndroidSchedulers.manThread())又切換到了主執行緒。

解決了執行緒來回切換問題,同時代碼邏輯也十分清晰。

OnGetDataListener程式碼:

public interface OnGetDataListener<T> {
    void success(T response); // 網路操作成功
    void fail(T response, String msg); // 網路操作失敗
}


LoginPresenter程式碼:
public class LoginPresenter {

    private ILoginView loginView;
    private LoginBiz loginBiz;

    public LoginPresenter(ILoginView loginView) {
        this.loginView = loginView;
        this.loginBiz = new LoginBiz();
    }

    public void doLogin(String userName){
        loginView.showLoadding(); // 展示載入條
        User user = new User();
        user.name = userName;
        loginBiz.doLogin(new OnGetDataListener<User>() {
            @Override
            public void success(User response) {
                loginView.hideLoadding(); // 隱藏載入條
                loginView.showView(response.name); // 展示資料
            }

            @Override
            public void fail(User response, String msg) {
                loginView.hideLoadding();
                loginView.showView(msg);
            }
        },user);
    }
}


最後看下Log日誌:


確實是開啟的新執行緒,放心了。

看官可以自己寫個demo試試,下面是本文的原始碼,可下載下來執行試試