What is function responsive programming (Java & Android) and function responsive programming (java )?
What is function responsive programming (Java and Android)
-- Welcome to reprint, please indicate the source of http://blog.csdn.net/asce1885, do not use for commercial purposes without my consent, thank you --
Link: http://www.bignerdranch.com/blog/what-is-functional-reactive-programming/
Function responsive programming (FRP) provides a new perspective for solving modern programming problems. Once you understand it, it can greatly simplify your project, especially when dealing with asynchronous events of nested callbacks, complex list filtering and transformation, or time-related issues.
I will try to skip the academic explanation of functional responsive programming (there are already many on the network) and help you understand what functional responsive programming is from a practical perspective, and how to apply it at work. This article will focus on a specific implementation of functional responsive programming RxJava, which can be used in Java and Android.
Start
Let's take a real example to explain how function responsive programming improves the readability of our code. Our task is to query the GitHub API, first obtain the user list, and then request the details of each user. This process includes two web service endpoints:
Https://api.github.com/users-get user list;
Https://api.github.com/users/?username}-obtain detailed information about a specific user, such as https://api.github.com/users/mutexkid.
Old Style
You may be familiar with the following example: it calls web service, uses the callback interface to pass the successful result to the next web service request, and defines another successful callback, then initiate the next web service request. As you can see, this will lead to two layers of nested callback:
//The "Nested Callbacks" Way public void fetchUserDetails() { //first, request the users... mService.requestUsers(new Callback<GithubUsersResponse>() { @Override public void success(final GithubUsersResponse githubUsersResponse, final Response response) { Timber.i(TAG, "Request Users request completed"); final synchronized List<GithubUserDetail> githubUserDetails = new ArrayList<GithubUserDetail>(); //next, loop over each item in the response for (GithubUserDetail githubUserDetail : githubUsersResponse) { //request a detail object for that user mService.requestUserDetails(githubUserDetail.mLogin, new Callback<GithubUserDetail>() { @Override public void success(GithubUserDetail githubUserDetail, Response response) { Log.i("User Detail request completed for user : " + githubUserDetail.mLogin); githubUserDetails.add(githubUserDetail); if (githubUserDetails.size() == githubUsersResponse.mGithubUsers.size()) { //we've downloaded'em all - notify all who are interested! mBus.post(new UserDetailsLoadedCompleteEvent(githubUserDetails)); } } @Override public void failure(RetrofitError error) { Log.e(TAG, "Request User Detail Failed!!!!", error); } }); } } @Override public void failure(RetrofitError error) { Log.e(TAG, "Request User Failed!!!!", error); } }); }
Although this is not the worst code-at least it is asynchronous, therefore, it is not blocked when waiting for each request to be completed-but it is far from ideal because of the complexity of the Code (more levels of callback Code complexity will increase exponentially. When we inevitably need to modify the code (in the previous web service call, we rely on the previous callback status, so it is not applicable to modularity or modify the data to be passed to the next Callback) it is far from easy to work. We affectionately call this situation "Callback hell ".
RxJava Method
Let's take a look at how RxJava achieves the same functions:
public void rxFetchUserDetails() { //request the users mService.rxRequestUsers().concatMap(Observable::from) .concatMap((GithubUser githubUser) -> //request the details for each user mService.rxRequestUserDetails(githubUser.mLogin) ) //accumulate them as a list .toList() //define which threads information will be passed on .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) //post them on an eventbus .subscribe(githubUserDetails -> { EventBus.getDefault().post(new UserDetailsLoadedCompleteEvent(githubUserDetails)); }); }
As you can see, using the function responsive programming model we completely get rid of callback and finally get a shorter program. Let's start with the basic definition of functional responsive programming and gradually understand what happened. The code is hosted on GitHub.
Basically, function responsive programming adds the function of manipulating and transforming data streams sent by Observables Based on the observer mode. In the above example, Observables is the pipeline where our data flow is located.
The observer mode has two roles: One Observable and one or more Observers. The Observable sends events, and the Observer subscribes to and receives these events. In the preceding example, The. subscribe () function is used to add an Observer to the Observable and create a request.
Build an Observable Pipeline
For each operation on the Observable pipeline, a new Observable will be returned. The content of this new Observable will either be the same as before the operation or be converted. In this way, we can break down tasks and break down event streams into small operations, then, We splice these Observables to complete more complex behaviors or reuse each independent unit in the pipeline. Every method call to the Observable will be added to the overall pipeline so that our data can flow in it.
Let's start with building an Observable and look at a specific example:
Observable<String> sentenceObservable = Observable.from(“this”, “is”, “a”, “sentence”);
In this way, we have defined the first part of the pipeline: Observable. The data in circulation is a string sequence. The first thing we need to realize is that this is a non-blocking code that does not implement any function and only defines what we want to accomplish. Observable starts to work only after we subscribe to it, that is, after registering an Observer for it.
Observable.subscribe(new Action1<String>() { @Override public void call(String s) { System.out.println(s); } });
In this step, the Observable will send data blocks added by each independent Observable from () function. The pipeline continuously sends Observables until all Observables are processed.
Changing data streams
Now we get the string stream being sent. We can transform these data streams as needed and create more complex behavior.
Observable<String> sentenceObservable = Observable.from(“this”, “is”, “a”, “sentence”);sentenceObservable.map(new Func1<String, String>() { @Override public String call(String s) { return s.toUpperCase() + " "; } }).toList().map(new Func1<List<String>, String>() { @Override public String call(List<String> strings) { Collections.reverse(strings); return strings.toString(); } })//subscribe to the stream of Observables.subscribe(new Action1<String>() { @Override public void call(String s) { System.out.println(s); } });
Once the Observable IS subscribed, we will get "sentence a is this ". The. the map function accepts objects of the Func1 class, which has two parameter types: one is the input type (the content of the previous Observable) and the other is the output type (in this example, is a string that is converted in uppercase, formatted, and packaged with a new Observable instance, and finally passed to the next function ). As you can see, we use reusable pipeline combinations to implement more complex functions.
In the above example, we can use the lambda expression of Java 8 to further simplify the Code:
Observable.just("this", "is", "a", "sentence").map(s -> s.toUpperCase() + " ").toList().map(strings -> { Collections.reverse(strings); return strings.toString(); });
In the subscribe function, we pass the Action1 class object as the parameter, and take the String type as the fan type parameter. This defines the subscriber's behavior. After the observer sends the last event, the processed string is received. This is the simplest form of overload for the. subscribe () function (see references ).
This example shows the transformation function. map () and Aggregate functions. toList (), which is only the tip of the iceberg in terms of data stream manipulation capabilities (all available data flow manipulation functions are visible to the https://github.com/ReactiveX/RxJava/wiki), but it shows the basic concept: in functional responsive programming, we can convert data streams through independent units in pipelines that implement data conversion or data operation functions. As needed, we can reuse these independent units in other pipelines composed of Observables. By splicing these Observable units together, we can make up more complex features, but at the same time keep them as easy-to-understand and modifiable composite logical small units.
Use Scheduler to manage threads
In the web service example, we show how to use RxJava to initiate a network request. We are talking about conversion, aggregation, and subscription of the Observable data stream, but we are not talking about how the web requests of the Observable data stream are implemented asynchronously.
This is the scope of how the FRP programming model calls Scheduler-this policy defines the thread in which the Observable stream event occurs and the thread in which the subscriber consumes the processing result of the Observable. In the web service example, we want the request to be carried out in the background thread, and the subscription behavior occurs in the main thread, So we define the following:
.subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) //post them on an eventbus .subscribe(githubUserDetails -> { EventBus.getDefault().post(new UserDetailsLoadedCompleteEvent(githubUserDetails)); });
The Observable. subscribeOn (Scheduler schedon) function specifies that the work of the Observable must be executed in the specified scheduler thread. Observable. observeOn (Scheduler schedeon) specifies the schedeon thread in which the Observable triggers the subscriber's onNext (), onCompleted (), and onError () functions, and calls the Observable's observeOn () function, pass the correct Scheduler to it.
Scheduler may be used as follows:
Schedulers. computation (): used for computing jobs such as event loop and callback processing. Do not use this function in I/O (Schedulers. io () should be used );
Schedulers. from (executor): Use the specified Executor as the schedtor;
Schedulers. immediate (): Execute the task immediately in the current thread;
Schedulers. io (): used for I/O-intensive jobs, such as blocking asynchronous I/O operations. This scheduler is supported by a thread pool that will increase as needed. For general computing jobs, use Schedulers. computation ();
Schedulers. newThread (): Creates a new thread for each work unit;
Schedulers. test (): Advanced events that support unit testing for testing purposes;
Schedulers. trampoline (): Put the jobs in the current thread into the queue and queue them in sequence.
By setting the observeOn and subscribeOn Schedulers, we define the thread used by the Network request (Schedulers. newThread ()).
Next step
We have covered a lot of basic content in this article. here you should have a good understanding of how function responsive programming works. Please refer to and understand the project introduced in this article. It is hosted on GitHub. Read the RxJava document and check out the rxjava-koans project to learn about the functional responsive programming model in a test-driven manner.
-- Welcome to reprint, please indicate the source of http://blog.csdn.net/asce1885, do not use for commercial purposes without my consent, thank you --