RxJava development essentials 3-Hello to the responsive world
Hello to responsive world
In the previous chapter, we have a theoretical and rapid overview of the observer model. We also saw how to create Observables from the beginning, from the list, or from an existing function. In this chapter, we will use what we have learned to create our first responsive Android Application. First, we need to build an environment to import the required libraries and useful libraries. Then we will create a simple application that contains several RecycleView items filled with RxJava in different flavors.
Start the engine
We will use IntelliJ IDEA/Android Studio to create this project, so you will be familiar with it.
Let's start creating a new Android project. You can create your own project or use the import provided in this book. Select the creation method you like. It depends on you.
Dependency
Obviously, we will useGradleTo manage our dependency list. Our build. gradble file looks like this:
As you can see, we have introduced RxAndroid. RxAndroid is an enhanced version of RxJava, especially designed for Android.
RxAndroid
RxAndroid is part of the RxJava family. It is based on RxJava1.0.x and adds several useful classes based on common RxJava. In most cases, it adds a special Scheduler for Android. We will discuss it again in Chapter 7 Schedulers-Defeating the Android MainThread Issue.
Tools
Out of practicality, we introduced Lombok and Butter Knife. These two can help us to write a lot of template-class code less in Android applications.
Lombok
Lombok uses annotations to generate a lot of code for you. We will use it to generategetter/setter
,toString()
,equals()
,hashCode()
. It relies on Gradle dependencies and an Android Studio plug-in.
Butter Knife
Butter Knife uses annotation to help us avoid writingfindViewById()
And set the click listening pain. As for Lombok, we can import dependencies and install the Android Studio plug-in for a better experience.
Retrolambda
Finally, we imported export Lambda because the Android we developed is based on Java 1.6, and we can use it to implement the Java 8 Lambda function, thus reducing a lot of template code.
Our first Observable
In our first column, we will retrieve the list of installed applications and fill in RecycleView items to display them. We also imagine a pull-down refresh function and a progress bar to inform the user that the current task is being executed.
First, create an Observable. We need a function to retrieve the list of installed applications and provide it to our observer. One by one, we can launch these application data and group them into a separate list to demonstrate the flexibility of responsive methods.
private Observable getApps(){ return Observable.create(subscriber -> { List apps = new ArrayList(); final Intent mainIntent = new Intent(Intent.ACTION_MAIN,null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); List
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 is as follows:
@Data@Accessors(prefix = "m")public class AppInfo implements Comparable
{ long mLastUpdateTime; String mName; String mIcon; public AppInfo(String nName, long lastUpdateTime, String icon) { mName = nName; mIcon = icon; mLastUpdateTime = lastUpdateTime; } @Override public int compareTo(Object another) { AppInfo f = (AppInfo)another; return getName().compareTo(f.getName()); }}
It is important to check the observer's subscription before transmitting new data or completing the sequence. In this way, the code will be more efficient, because we will not generate unnecessary data items if no observer waits.
Now, we can subscribe to the Observable and observe it. Subscribing to an Observable means that when the data we need comes in, we must provide corresponding operations to execute it.
What is the current scenario? A progress bar is displayed to wait for data. When the data arrives, we need to hide the progress bar, fill in the list, and finally display the list. Now, we know what to do when everything is ready. So what are the wrong scenarios? In this case, we only use Toast to display an error message.
Using Butter Knife, we get the reference of the list and pull-down refresh components:
@InjetcView(R.id.fragment_first_example_list)RecyclerView mRecycleView;@InjectView(R.id.fragment_first_example_swipe_container)SwipeRefreshLayout mSwipeRefreshLayout;
We use the standard components of Android 5: RecyclerView and SwipeRefreshLayout. Screenshots show the layout file of the list Fragment of our simple App:
We use a pull-down refresh method, so the list data can be a refresh action triggered by the user or the initialization load. For these two scenarios, we use the same behavior, so we place our observer in a function that is easy to reuse. The following is our observer who defines the tasks to be done, such as success, failure, and completion: <喎?http: www.bkjia.com kf ware vc " target="_blank" class="keylink"> VcD4NCjxwcmUgY2xhc3M9 "brush: java;"> private void refreshTheList() { getApps().toSortedList() .subscribe(new Observable >() { @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 appInfos) { mRecyclerView.setVisibility(View.VISIBLE); mAdapter.addApplications(appInfos); mSwipeRefreshLayout.setRefreshing(false); } });}
Defining a function enables us to use the same block to process two scenarios. When fragment is loaded, we only need to callrefreshTheList()
Method and setrefreshTheList()
Method as the method triggered by the user drop-down.
mSwipeRefreshLayout.setOnRefreshListener(this::refreshTheList);
The first example is now complete. Run it.
Create an Observable from the listIn this example, we will introducefrom()
Function. Using this special "CREATE" function, we can create an Observable from a list. The Observable emits every element in the list. We can subscribe to them to respond to these elements.
To achieve the same results as the first exampleonNext()
The function updates our adapter, adds elements, and notifies insertion.
We will reuse the same structure as the first example. The main difference is that we no longer retrieve the list of installed applications. The list is provided by external entities:
mApps = ApplicationsList.getInstance().getList();
After obtaining the list, we only need to respond to it and fill in the item of RecyclerView:
private void loadList(List
apps) { mRecyclerView.setVisibility(View.VISIBLE); Observable.from(apps) .subscribe(new Observable() { @Override public void onCompleted() { mSwipeRefreshLayout.setRefreshing(false); 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(AppInfo appInfo) { mAddedApps.add(appInfo); mAdapter.addApplication(mAddedApps.size() - 1,appInfo); } });}
As you can see, we will pass the list of installed applications as parametersfrom()
Function, And then we subscribe to the generated Observable. The observer is very similar to the observer in our first example. One major difference is thatonCompleted()
In the function, the progress bar is stopped because we transmit elements one by one. In the first example, the Observable emits the entire list.onNext()
It is safe to stop the progress bar in the function.
Several more examplesIn this section, we willjust()
,repeat()
,defer()
,range()
,interval()
, Andtimer()
Method to show some examples.
Just ()Suppose we only have three independent AppInfo objects, and we want to convert them into Observable and fill them in the items of RecyclerView:
List
apps = ApplicationsList.getInstance().getList();AppInfo appOne = apps.get(0);AppInfo appTwo = apps.get(10);AppInfo appThree = apps.get(24);loadApps(appOne,appTwo,appThree);
We can retrieve the list and extract the three elements as in our previous example. Then we upload them to thisloadApps()
Function:
private void loadApps(AppInfo appOne,AppInfo appTwo,AppInfo appThree) { mRecyclerView.setVisibility(View.VISIBLE); Observable.just(appOne,appTwo,appThree) .subscribe(new Observable
() { @Override public void onCompleted() { mSwipeRefreshLayout.setRefreshing(false); 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(AppInfo appInfo) { mAddedApps.add(appInfo); mAdapter.addApplication(mAddedApps.size() - 1,appInfo); } });}
As you can see, the code is similar to the previous example. This method gives us the opportunity to consider code reuse.
You can pass a function as a parameterjust()
Method, you will get the original Observable version of the existing code. Migrating existing code based on a new responsive architecture may be a useful start point.
Repeat ()Assume that you want to repeatedly transmit three data records to an Observable. For example, we usejust()
Observable in the example:
private void loadApps(AppInfo appOne,AppInfo appTwo,AppInfo appThree) { mRecyclerView.setVisibility(View.VISIBLE); Observable.just(appOne,appTwo,appThree) .repeat(3) .subscribe(new Observable
() { @Override public void onCompleted() { mSwipeRefreshLayout.setRefreshing(false); 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(AppInfo appInfo) { mAddedApps.add(appInfo); mAdapter.addApplication(mAddedApps.size() - 1,appInfo); } });}
As you can seejust()
Added after creating the Observablerepeat(3)
It will create a sequence of nine elements, each of which is independently transmitted.
Defer ()In this scenario, you want to declare an Observable, but you want to postpone the creation of the Observable until the observer subscribes. See the followinggetInt()
Function:
private Observable
getInt(){ return Observable.create(subscriber -> { if(subscriber.isUnsubscribed()){ return; } App.L.debug("GETINT"); subscriber.onNext(42); subscriber.onCompleted(); });}
This is simple, and it does not do much, but it serves us. Now, we can create a new Observable and apply it.defer()
:
Observable
deferred = Observable.defer(this::getInt);
This time,deferred
Yes,getInt()
create()
The method has not been called: The logcat log has not been printed by "GETINT:
deferred.subscribe(number -> { App.L.debug(String.valueOf(number));});
But once we subscribe,create()
The method will be called, and we can also get two in the logcat log: GETINT and 42.
Range ()Do you need to transmit N numbers from a specified number X? You can userange
:
Observable.range(10,3) .subscribe(new Observable
() { @Override public void onCompleted() { Toast.makeText(getActivity(), "Yeaaah!", Toast.LENGTH_LONG).show(); } @Override public void onError(Throwable e) { Toast.makeText(getActivity(), "Something went wrong!", Toast.LENGTH_SHORT).show(); } @Override public void onNext(Integer number) { Toast.makeText(getActivity(), "I say " + number, Toast.LENGTH_SHORT).show(); } });
range()
The function uses two numbers as parameters: the first is the starting point, and the second is the number of numbers we want to transmit.
Interval ()interval()
Functions are useful when you need to create a polling program.
Subscription stopMePlease = Observable.interval(3,TimeUnit.SECONDS) .subscribe(new Observable
() { @Override public void onCompleted() { Toast.makeText(getActivity(), "Yeaaah!", Toast.LENGTH_LONG).show(); } @Override public void onError(Throwable e) { Toast.makeText(getActivity(), "Something went wrong!", Toast.LENGTH_SHORT).show(); } @Override public void onNext(Integer number) { Toast.makeText(getActivity(), "I say " + number, Toast.LENGTH_SHORT).show(); } });
interval()
Two Parameters of the function: one parameter specifies the interval between two launches, and the other parameter specifies the time unit used.
Timer ()If you need to launch the Observable after a period of time, you can use the following example:timer()
:
Observable.timer(3,TimeUnit.SECONDS) .subscribe(new Observable
() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Long number) { Log.d("RXJAVA", "I say " + number); } });
It will launch 0 in 3 seconds, and then it will be finished. Let's usetimer()
The third parameter, as shown in the following example:
Observable.timer(3,3,TimeUnit.SECONDS) .subscribe(new Observable
() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Long number) { Log.d("RXJAVA", "I say " + number); } });
With this code, you can create an initial value to delay (the previous example is 3 seconds) execution.interval()
Version, and then a new number is sent every N seconds (the previous example is 3 seconds ).
SummaryIn this chapter, we create the first Android Application reinforced by RxJava. We create an Observable from scratch, from an existing list, and from an existing function. We also learned how to create repeated emission Observables, interval emission Observables, and delayed emission Observables.
In the next chapter, we will master the filtering operation and be able to create the sequence we need from the sequence we receive.