[Android development experience] replacing AsyncTask and AsyncTaskLoader-RxJava Android templates with RxJava. Observable, androidasynctask

Source: Internet
Author: User

[Android development experience] replacing AsyncTask and AsyncTaskLoader-RxJava Android templates with RxJava. Observable, androidasynctask

Reprinted please indicate the source http://blog.csdn.net/zhaokaiqiang1992

Welcome to the ndroid-tech-frontier open-source project, and regularly translate foreign Android high-quality technologies, open-source libraries, software architecture design, testing, and other articles

  • Translator: ZhaoKaiQiang
  • Proofreader: chaossss
  • Status: Completed

There are many posts on the Internet about the RxJava Getting Started Guide, some of which are based on the Android environment. However, I think so far, many people are only addicted to what they see. When they want to solve specific problems in their Android projects, they do not know how or why RxJava is used. In this series of articles, I want to explore the patterns in some Android projects that I have worked on that depend on the RxJava architecture.

At the beginning of the article, I want to handle problems that Android Developers can easily encounter when using RxJava. From this perspective, I will provide more advanced and appropriate solutions. In this series of articles, I hope to hear other developers solve similar problems when using RxJava. Maybe they are the same as what I found.

Question 1: Background tasks

The first challenge for Android Developers is how to effectively work in the background thread and then update the UI in the UI thread. This is often because data needs to be obtained from the web service. You may say, "What are the challenges? You only need to start an AsyncTask, and then all the work will be done for you ." If you think so, you have a chance to improve the situation. This is because you are used to this complicated method and forget that it should be concise, or that you have not handled all the boundary situations that should be handled. Let's talk about this.

Default solution: AsyncTask

AsyncTask is the default processing tool in Android. developers can do some processing work for a long time without blocking the user interface. (Note: Recently, AsyncTaskLoader is used to process more specific data loading tasks. We will talk about this later)

On the surface, this seems simple. You define some code to run in the background thread, and then define some code to run in the UI thread. After the background task is processed, it processes execution results transmitted from background tasks in the UI thread.

private class CallWebServiceTask extends AsyncTask<String, Result, Void> {    protected Result doInBackground(String... someData) {        Result result = webService.doSomething(someData);        return result;    }    protected void onPostExecute(Result result) {        if (result.isSuccess() {            resultText.setText("It worked!");        }    }}

The biggest problem with AsyncTask is the detailed handling. Let's talk about this.

Error Handling

The first problem with this simple usage is: "What if an error occurs ?" Unfortunately, there are no good solutions for the moment. Therefore, many developers ultimately need to inherit AsyncTask, then wrap a try/catch in doInBackground (), and return

Lifecycle of Activity and Fragment

Another question you must face is: "What will happen if I exit the Activity or rotate the device when AsyncTask is running ?" Well, if you only send some data and no longer care about the sending results, it may be okay. But what if you need to update the UI Based on the returned results of the Task? If you do not stop it, when you try to call Activity or view, you will get a null pointer exception that causes program crash because they are invisible or null.

Similarly, AsyncTask does not do much work to help you with this problem. As a developer, you need to ensure that a Task is referenced, and either cancel the Task when the Activity is being destroyed or update the UI in onPostExecute, make sure that the Activity is in an reachable state. When you only want to do some specific work and make the project easy to maintain, this will continue to increase the difficulty of the maintenance project.

Cache during rotation (or in other cases)

What happens when your user stays in the current Activity and only rotates the screen? In this case, canceling a Task makes no sense, because after rotation, you still need to restart the Task. Or you don't want to restart the Task, because the status changes in some places in non-Screen mode (because it mutates some state somewhere in a non-idempotent way ), but you really want to get the result, because you can update the UI to reflect this situation.

If you specifically perform a read-only loading operation, you can use AsyncTaskLoader to solve this problem. But for the standard Android method, it is still very heavy, because of the lack of error processing, there is no cache in the Activity, there are many other unique geeks.

Combined multiple Web Server calls

Now, if we have tried to solve all the above problems, but now we need to perform some continuous network requests. Each request must be based on the results of the previous request. Alternatively, we want to make some concurrent network requests and then merge the results to the UI thread. However, sorry again, AsyncTask won't help you here.

Once you start doing such a thing, as more complex thread models grow, the previous problems will cause a lot of pain and weakness to deal with such a thing. If you want these threads to run together, or you want them to run independently, and then call back, or in other cases, let them run synchronously in a background thread, and finally make up different copies. (To chain calltogether, you either keep them separate and end up in callback hell, or run them synchronously together in one background task end up duplicating work to compose them differently in other situations .)

To run the task in parallel, you must create a custom executor and pass it to AsyncTaskTask because the default AsyncTask does not support parallel tasks. To coordinate parallel Threads, you need to use CountDownLatchs, Threads, Executors, and ures to reduce more complex synchronization modes.

Testability

If you like to test your code, AsyncTask is not helpful. If we do not do additional work, it is very difficult to test AsyncTask. It is very fragile and difficult to maintain. This is a post about how to successfully test AsyncTask.

Better solution: RxJava's Observable

Fortunately, once we decide to use the RxJava dependency library, we will be able to solve these problems. Let's see what it can do for us.

Next we will use Observables to write a request code to replace the above AsyncTask method. (If you use Retrofit, it should be easy to use. Secondly, it also supports Observable return values, and it works in a background thread pool, without extra work)

webService.doSomething(someData).observeOn(AndroidSchedulers.mainThread()).subscribe(    result -> resultText.setText("It worked!")),    e -> handleError(e));
Error Handling

You may notice that, without doing additional work, we have handled the situations where AsyncTask won't handle the successes and errors, and we have written very little code. The extra component you see is the result that we want the Observer to process in the UI main thread. This allows us to move forward a little bit. If your sebService object does not want to run in the background thread, you can also use. subscribeOn (…) here (...) Statement. (Note: These examples use the lambda Syntax of Java 8. Using lambda can be used in the Android project, but in my opinion, the return is higher than the risk, compared with this article, we prefer to use it in our project .)

Lifecycle of Activity and Fragment

Now, we want to use RxAndroid to solve the aforementioned lifecycle problem. We do not need to specify mainThread () scheduler (by the way, you only need to import RxAndroid ). As shown below:

AppObservable.bindFragment(this, webService.doSomething(someData)).subscribe(    result -> resultText.setText("It worked!")),    e -> handleError(e));

My usual practice is to create a help method in the Base Fragment of the application to simplify this point. You can refer to a Base RxFragment I maintain for some guidance.

bind(webService.doSomething(someData)).subscribe(    result -> resultText.setText("It worked!")),    e -> handleError(e));

If our Activity or Fragment is no longer reachable, AppObservable. bindFragment () can be inserted into the call chain to prevent onNext () from running. If the Activity and Fragment are not reachable when the Task is trying to run, subscribe will cancel the subscription and stop running, so there is no risk of NULL pointer and the program will not crash. It should be noted that when we leave Activity and Fragment, we may temporarily or permanently disclose the information, depending on the Observable behavior in the problem. Therefore, in the bind () method, I will also call the LifeCycleObservable mechanism, which is automatically canceled when the Fragment is destroyed. The advantage of doing so is to make it happen once and for all.

Therefore, this solves the first two problems, but the following is what RxJava is doing.

Combined multiple Web Server calls

I will not describe it in detail here, because this is a complicated problem, but by using Observables, You can do complicated things in a very simple and easy-to-understand way. Here is an example of a chained Web Service call. These requests are mutually dependent and run the second batch of concurrent calls in the thread pool. Then, the data is merged and sorted before the results are returned to the Observer. For better measurement, I even put a filter in it. All business logic is included in the following five-line code...

public Observable<List<CityWeather>> getWeatherForLargeUsCapitals() {return cityDirectory.getUsCapitals()     .flatMap(cityList -> Observable.from(cityList))    .filter(city -> city.getPopulation() > 500,000)    .flatMap(city -> weatherService.getCurrentWeather(city)) //each runs in parallel    .toSortedList((cw1,cw2) -> cw1.getCityName().compare(cw2.getCityName()));}
Cache during rotation (or in other cases)

Since this is a loaded data, we may need to cache the data so that when we rotate the device, it will not trigger the re-call of all web services. One way is to retain the Fragment and save a reference to the Observable cache, as shown below:

@Overridepublic void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setRetainInstance(true);    weatherObservable = weatherManager.getWeatherForLargeUsCapitals().cache();}public void onViewCreated(...) {    super.onViewCreated(...)    bind(weatherObservable).subscribe(this);}

After rotation, the subscriber's cache instance immediately sends the same request as the first request to prevent real Web Service requests.

If you want to avoid cached Fragment (and there are plenty of reasons to avoid it), you can use AsyncSubject to implement cache. AsyncSubject resends the final project at any time. Or we can use BehaviorSubject to get the final value and new value to change the entire application.

WeatherListFragment. java

public void onViewCreated() {super.onViewCreated()bind(weatherManager.getWeatherForLargeUsCapitals()).subscribe(this);}

WeatherManager. java

public Observable<List<CityWeather>> getWeatherForLargeUsCapitals() {if (weatherSubject == null) {    weatherSubject = AsyncSubject.create();    cityDirectory.getUsCapitals()         .flatMap(cityList -> Observable.from(cityList))        .filter(city -> city.getPopulation() > 500,000)        .flatMap(city -> weatherService.getCurrentWeather(city))        .toSortedList((cw1,cw2) -> cw1.getCityName().compare(cw2.getCityName()))        .subscribe(weatherSubject);}return weatherSubject;}

Because the "cache" is managed by the Manager independently, it will not be bound to the Fragment/Activity cycle and will continue to exist in the Activity/Fragment. If you want to force refresh to retain the lifecycle events of the Fragment cache instance in a similar way, you can do this:

public void onCreate() {super.onCreate()if (savedInstanceState == null) {    weatherManager.invalidate(); //invalidate cache on fresh start}}

The great thing about this is that it is not like Loaders. We can cache a lot of results in activities and Services flexibly. You only need to remove the invalidate () call in oncreate () and let your Manager object decide when to send new meteorological data. It may be a Timer, a change in user positioning, or any other time point. It really doesn't matter. You can now control when to update the cache and reload it. In addition, when your cache policy changes, the interfaces between Fragment and your Manager objects do not need to be changed. It is just a List of observers.

Testability

Testing is the last challenge we want to achieve clean and simple. (Let's ignore the fact that during the test, we may want to simulate the actual Web service. This is simple. The following interface is used to inject the dependency to the standard mode that you may already be working on .)

Fortunately, the Observables gives us a simple way to change an Asynchronous Method to synchronous. What you need to do is to use the toblocking () method. Let's look at a test example.

List results = getWeatherForLargeUsCapitals().toBlocking().first();assertEquals(12, results.size());

Just like this, we do not need to use ures or CountDownLatchs to make some fragile operations, such as thread sleep or make our tests very complex, our tests are now simple, clean, and maintainable.

Conclusion

Update: I have created a simple project to demonstrate the AsyncTask style and AsyncTaskLoader style.

RxJava, you deserve it. We use rx. Observable to replace AsyncTask and AsyncTaskLoader to implement enhanced and clearer code. It is a pleasure to use RxJava Observables, and I look forward to presenting more Android-related solutions.

Original article link

Replace AsyncTask and AsyncTaskLoader with rx. Observable-RxJava Android Patterns

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.