执行网络任务
在当今99%的移动应用中网络都是必不可缺的一部分:总是需要连接远程服务器来检索App需要的信息。
作为网络访问的第一个案例,我们将创建下面这样一个场景:
- 加载一个进度条。
- 用一个按钮开始文件下载。
- 下载过程中更新进度条。
- 下载完后开始视频播放。
我们的用户界面非常简单,我们只需要一个有趣的进度条和一个下载按钮。

首先,我们创建mDownloadProgress
private PublishSubject<Integer> mDownloadProgress = PublishSubject.create();
这个主题我们用来管理进度的更新,它和download函数协同工作。
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;}
上面的这段代码将会触发NetworkOnMainThreadException异常。我们可以创建RxJava版本的函数进入我们挚爱的响应式世界来解决这个问题:
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);}});}
现在我们需要触发下载操作,点击下载按钮:
@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>() {@Overridepublic void onCompleted() {App.L.debug("Completed");}@Overridepublic void onError(Throwable e) {App.L.error(e.toString());}@Overridepublic 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来绑定按钮的方法并更新按钮信息和点击状态:我们不想让用户点击多次从而触发多次下载事件。
然后,我们创建一个subscription来观察下载进度并相应的更新进度条。很明显,我们订阅在主线程是因为进度条是UI元素。
obserbableDownload("http://archive.blender.org/fileadmin/movies/softboy.avi", "sdcardsoftboy.avi";)
这是一个下载Observable。网络调用是一个I/O任务,理应使用I/O调度器。当下载完成,就会在onNext() 中启动视频播放器,并且播放器将会在目标路径找到下载的文件.。
下图展示了下载进度和视频播放器选择对话框:

