Our first Observable

In our first example, we are going to retrieve the list of the installed apps and populate a RecyclerView item to show them. We also have a fancy pull-to-refresh feature and a progress bar to notify the user that the task is ongoing.

First of all, we create our Observable. We need a function that retrieves the installed apps' list and provides it to the Observer. We are emitting items one by one and then grouping them into one single list, to show the flexibility of the reactive approach:

import com.packtpub.apps.rxjava_essentials.apps.AppInfo;

private Observable<AppInfo> getApps() {
    return Observable.create(subscriber -> {
        List<AppInfoRich> apps = new ArrayList<>();

        final Intent mainIntent = new Intent(Intent.ACTION_MAIN,  null);
        mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);

        List<ResolveInfo> infos = getActivity() .getPackageManager().queryIntentActivities(mainIntent, 0);
        for (ResolveInfo info : infos) {
            apps.add(new AppInfoRich(getActivity(), info));
        }

        for (AppInfoRich appInfo : apps) {
            Bitmap icon = Utils.drawableToBitmap(appInfo.getIcon());
            String name = appInfo.getName();
            String iconPath = mFilesDir + "/" + name;
            Utils.storeBitmap(App.instance, icon, name);

            if (subscriber.isUnsubscribed()) {
                return;
            }
            subscriber.onNext(new AppInfo(name, iconPath,  appInfo.getLastUpdateTime()));
        }
        if (!subscriber.isUnsubscribed()) {
            subscriber.onCompleted();
        }
    });
}

The AppInfo object looks like this:

@Data
@Accessors(prefix = "m")
public class AppInfo implements Comparable<Object> {

    long mLastUpdateTime;

    String mName;

    String mIcon;

    public AppInfo(String name, String icon, long lastUpdateTime) {
        mName = name;
        mIcon = icon;
        mLastUpdateTime = lastUpdateTime;
    }

    @Override
    public int compareTo(Object another) {
        AppInfo f = (AppInfo) another;
        return getName().compareTo(f.getName());
    }
}

It's important to note that we are checking the Observer subscription before emitting new items or completing the sequence. This makes the code more efficient, because we are not generating unnecessary items if nobody is waiting for them.

At this point, we can subscribe to this Observable and observe it. Subscribing to an Observable means that we have to provide the actions to execute when the data we need come in.

What is our scenario right now? Well, we are showing a spinning progress bar, waiting for the data. When the data comes in, we have to hide the progress bar, populate the list, and, eventually, show the list. Now, we know what to do when everything is fine. What about an error scenario? In case of error, we just want to show an error message as Toast.

Using Butter Knife, we get the reference to the list and the pull-to-refresh element:

@InjectView(R.id.fragment_first_example_list)
RecyclerView mRecyclerView;

@InjectView(R.id.fragment_first_example_swipe_container)
SwipeRefreshLayout mSwipeRefreshLayout;

We are using Android 5's standard components: RecyclerView and SwipeRefreshLayout. This screenshot shows the layout file for our simple apps' list's Fragment:

Our first Observable

We are using a pull-to-refresh approach, so the list can come from the initial loading, or from a refresh action triggered by the user. We have the same behavior for two scenarios, so we will put our Observer in a function to be easily reused. Here is our Observer, with its success, errors, and completed behaviors:

private void refreshTheList() {
    getApps().toSortedList()
            .subscribe(new Observer<List<AppInfo>>() {
                @Override
                public void onCompleted() {
                    Toast.makeText(getActivity(), "Here is the  list!", Toast.LENGTH_LONG).show();
                }

                @Override
                public void onError(Throwable e) {
                    Toast.makeText(getActivity(), "Something went  wrong!", Toast.LENGTH_SHORT).show();
                    mSwipeRefreshLayout.setRefreshing(false);
                }

                @Override
                public void onNext(List<AppInfo> appInfos) {
                    mRecyclerView.setVisibility(View.VISIBLE);
                    mAdapter.addApplications(appInfos);
                    mSwipeRefreshLayout.setRefreshing(false);
                }
            });
}

Having a function gives us the possibility of using the same block for two scenarios. We just have to call refreshTheList()when the fragment loads and sets refreshTheList() as the action to trigger when the user uses the pull-to-refresh approach:

mSwipeRefreshLayout.setOnRefreshListener(this::refreshTheList);

Our first example is now complete, up, and running!

Our first Observable
..................Content has been hidden....................

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