Executing a network task

Networking is part of probably 99 percent of mobile apps nowadays: we always need to connect to a remote server to retrieve the information we need in our app.

As a first approach to networking, we are going to create a new scenario in which we are going to:

  • Load a progress bar
  • Start a file download using a button
  • Update the progress bar during the download
  • Start the video player after the download is completed

Our user interface will be very simple. We will just need a fancy progress bar and a DOWNLOAD button.

Executing a network task

First of all, we will create mDownloadProgress:

private PublishSubject<Integer>mDownloadProgress = PublishSubject.create();

This is the subject that we are going to use to manage the progress bar updates. This subject works together with the download function:

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;
}

Using this code as it is will trigger NetworkOnMainThreadException. We can easily create a RxJava version of this function and jump into our beloved reactive world to solve this issue:

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);
        }
    });
}

Now we need to trigger the download, tapping the download button:

@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 = "/sdcard/softboy.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();
            });
}

We are using the Butter Knife annotation's @OnClick to bind the button to the method, and we are updating the button message and its clickable status: we don't want the user to trigger multiple downloads with multiple clicks.

Then, we create a new subscription to observe the download progress and update the progress bar accordingly. Obviously, we are observing on the main thread because the progress bar is a UI element:

obserbableDownload("http://archive.blender.org/fileadmin/movies/ softboy.avi", "/sdcard/softboy.avi";)

This is the download Observable. A network call is an I/O job and we are using the I/O Scheduler, as expected. When the download is complete, we are in onNext() and we can launch the video player, knowing that the downloaded file will be available to the player at the destination URI.

The following figure shows the download progress and the video player dialog:

Executing a network task
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.74.66