使用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試試,下面是本文的原始碼,可下載下來執行試試