天天看點

使用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,就是模拟登入操作。

結構如下:

使用RxAndroid配合MVP模式實作異步網絡請求,更新ui

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日志:

使用RxAndroid配合MVP模式實作異步網絡請求,更新ui

确實是開啟的新線程,放心了。

看官可以自己寫個demo試試,下面是本文的源碼,可下載下傳下來運作試試

點選下載下傳源碼