在當(dāng)今99%的移動(dòng)應(yīng)用中網(wǎng)絡(luò)都是必不可缺的一部分:總是需要連接遠(yuǎn)程服務(wù)器來檢索App需要的信息。
作為網(wǎng)絡(luò)訪問的第一個(gè)案例,我們將創(chuàng)建下面這樣一個(gè)場(chǎng)景:
我們的用戶界面非常簡(jiǎn)單,我們只需要一個(gè)有趣的進(jìn)度條和一個(gè)下載按鈕。
http://wiki.jikexueyuan.com/project/rxjava/images/chapter7_5.png" alt="" />
首先,我們創(chuàng)建mDownloadProgress
private PublishSubject<Integer> mDownloadProgress = PublishSubject.create();
這個(gè)主題我們用來管理進(jìn)度的更新,它和download函數(shù)協(xié)同工作。
private boolean downloadFile(String source, String destination) {
boolean result = false;
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(source);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return false;
}
int fileLength = connection.getContentLength();
input = connection.getInputStream();
output = new FileOutputStream(destination);
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
total += count;
if (fileLength >0) {
int percentage = (int) (total * 100 / fileLength);
mDownloadProgress.onNext(percentage);
}
output.write(data, 0, count);
}
mDownloadProgress.onCompleted();
result = true;
} catch (Exception e) {
mDownloadProgress.onError(e);
} finally {
try {
if (output != null) {
output.close();
}
if (input != null) {
input.close();
}
} catch (IOException e) {
mDownloadProgress.onError(e);
}
if (connection != null) {
connection.disconnect();
mDownloadProgress.onCompleted();
}
}
return result;
}
上面的這段代碼將會(huì)觸發(fā)NetworkOnMainThreadException異常。我們可以創(chuàng)建RxJava版本的函數(shù)進(jìn)入我們摯愛的響應(yīng)式世界來解決這個(gè)問題:
private Observable<Boolean> obserbableDownload(String source, String destination) {
return Observable.create(subscriber -> {
try {
boolean result = downloadFile(source, destination);
if (result) {
subscriber.onNext(true);
subscriber.onCompleted();
} else {
subscriber.onError(new Throwable("Download failed."));
}
} catch (Exception e) {
subscriber.onError(e);
}
});
}
現(xiàn)在我們需要觸發(fā)下載操作,點(diǎn)擊下載按鈕:
@OnClick(R.id.button_download)
void download() {
mButton.setText(getString(R.string.downloading));
mButton.setClickable(false);
mDownloadProgress.distinct()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Integer>() {
@Override
public void onCompleted() {
App.L.debug("Completed");
}
@Override
public void onError(Throwable e) {
App.L.error(e.toString());
}
@Override
public void onNext(Integer progress) {
mArcProgress.setProgress(progress);
}
});
String destination = "sdcardsoftboy.avi";
obserbableDownload("http://archive.blender.org/fileadmin/movies/softboy.avi", destination)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(success -> {
resetDownloadButton();
Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
File file = new File(destination);
intent.setDataAndType(Uri.fromFile(file),"video/avi");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}, error -> {
Toast.makeText(getActivity(), "Something went south", Toast.LENGTH_SHORT).show();
resetDownloadButton();
});
}
我們使用Butter Knife的注解@OnClick來綁定按鈕的方法并更新按鈕信息和點(diǎn)擊狀態(tài):我們不想讓用戶點(diǎn)擊多次從而觸發(fā)多次下載事件。
然后,我們創(chuàng)建一個(gè)subscription來觀察下載進(jìn)度并相應(yīng)的更新進(jìn)度條。很明顯,我們訂閱在主線程是因?yàn)檫M(jìn)度條是UI元素。
obserbableDownload("http://archive.blender.org/fileadmin/movies/softboy.avi", "sdcardsoftboy.avi";)
這是一個(gè)下載Observable。網(wǎng)絡(luò)調(diào)用是一個(gè)I/O任務(wù),理應(yīng)使用I/O調(diào)度器。當(dāng)下載完成,就會(huì)在onNext() 中啟動(dòng)視頻播放器,并且播放器將會(huì)在目標(biāo)路徑找到下載的文件.。
下圖展示了下載進(jìn)度和視頻播放器選擇對(duì)話框:
http://wiki.jikexueyuan.com/project/rxjava/images/chapter7_6.png" alt="" />