What is function-responsive programming (Java&android version)

Source: Internet
Author: User

What is function-responsive programming (Java&android version)

Original 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, you can greatly simplify your project, especially asynchronous events that handle nested callbacks, complex list filtering and transformations, or time-related issues.

I'll try to skip the explanation of the college style of function-responsive programming (there's already a lot on the web) and focus on helping you understand what functional-responsive programming is, and how to apply it in your work, from a practical point of view. This article will focus on a specific implementation of function-responsive programming rxjava, which can be used in Java and Android.

Begin

Let's start with a real example of how function-responsive programming can improve the readability of our code. Our task is to get a list of users by querying the GitHub API, and then request the details of each user. This process consists of two Web service endpoints:
Https://api.github.com/users-Get a list of users;
Https://api.github.com/users/{username}-Get detailed information for a specific user, such as Https://api.github.com/users/mutexkid.

The old style

You may be familiar with the following example: It invokes a Web service, uses a callback interface to pass successful results to the next Web service request, defines another successful callback, and then initiates the next Web service request. As you can see, this results in a two-level nested callback:

//the "Nested callbacks"     Public void fetchuserdetails() {//first, request the users ...Mservice.requestusers (NewCallback<githubusersresponse> () {@Override             Public void Success(FinalGithubusersresponse Githubusersresponse,FinalResponse Response) {timber.i (TAG,"Request Users request Completed");Final synchronizedList<githubuserdetail> githubuserdetails =NewArraylist<githubuserdetail> ();//next, loop over each item in the response                 for(Githubuserdetail githubuserdetail:githubusersresponse) {//request A Detail object for that userMservice.requestuserdetails (Githubuserdetail.mlogin,NewCallback<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 is interested!Mbus.post (NewUserdetailsloadedcompleteevent (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, it does not block when waiting for each request to complete-but because of the complexity of the code (adding more levels of callback code complexity will grow exponentially) is therefore far from ideal code. When we inevitably have to modify the code (in the previous Web service call, we rely on the previous callback state, so it does not apply to modularity or modify the data to be passed to the next callback) is far from easy work. We kindly call this situation "callback hell".

The way of Rxjava

Let's look at how to implement the same functionality using Rxjava:

PublicvoidRxfetchuserdetails () {//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  would  be passed  on.Subscribeon(Schedulers.newthread ()).Observeon(Androidschedulers.mainthread ())//Post them  on  an Eventbus.Subscribe(Githubuserdetails, {eventbus.getdefault (). Post (new userdetailsloadedcompleteevent (githubuse        Rdetails)); }); }

As you can see, using the function-responsive programming model, we get rid of the callbacks completely, and end up with a shorter program. Let's start with the basic definition of function-responsive programming and slowly explain what's going on, and gradually understand the code above, which is hosted on GitHub.

Fundamentally speaking, function-responsive programming is based on the observer pattern, which increases the ability to manipulate and transform the data stream sent by observables. In the example above, observables is the conduit where our data flows.

In retrospect, the observer pattern consists of two characters: one observable and one or more observers. Observable sends events, and observer subscribes to and receives these events. In the example above, the. Subscribe () function is used to add a observer to observable and create a request.

Build a observable pipeline

Each operation on the observable pipeline will return a new observable, the content of which is either the same as before the operation or is converted. This approach allows us to break down the task and break it down into smaller operations, and then stitch the observables together to accomplish more complex behavior or reuse each individual unit in the pipeline. Each method call we make to observable is added to the total pipeline so that our data flows in it.

Let's start with a observable to see a concrete example:

Observable<String> sentenceObservable = Observable.from(“this”, “is”, “a”, “sentence”);

So we've defined the first part of the pipeline: Observable. The data in circulation is a sequence of strings. The first thing to realize is that this is a non-blocking code that does not implement any function, just defines what we want to accomplish. Observable only starts working after we "subscribe" to it, which means after registering a observer with it.

Observable.subscribe(new Action1<String>() {          @Override          publicvoidcall(String s) {                System.out.println(s);          } });

In this step observable will send the data block added by each independent observable from () function. The pipeline will continue to send observables until all observables have been processed.

Transform Data Flow

Now that we get the stream of strings being sent, we can transform these streams as needed and build more complex behavior.

Observable<String>Sentenceobservable=Observable.From (' This ', ' is ', ' a ', ' sentence '); sentenceobservable.Map(NewFunc1<String,String>() {@Override Public StringCallStrings) {returnS.toUpperCase ()+ " "; }        }).ToList ().Map(NewFunc1<List<String>,String>() {@Override Public StringCallList<String>strings) {Collections.Reverse (strings);returnStrings.ToString (); }        })//subscribe to the stream of observables.SubscribeNewAction1<String>() {@Override Public voidCallStrings) {System.Out.println (s); }        });

Once observable is subscribed, we will get "sentence A is this". The. Map function called above accepts an object of the Func1 class that has two generic type parameters: One is the input type (the contents of the previous observable) and the other is the output type (in this case, a string that is converted in uppercase, formatted and wrapped with a new observable instance, which is eventually passed to the Next function). As you can see, we achieve more complex functionality through a reusable pipeline combination.

In the example above, we can also use Java8 's lambda expression to further simplify the code:

observable.just  ( Span class= "hljs-string" > "This" , ,  "a" ,  "sentence" ) .map  (S-s.touppercase  () +  "" ) .tolist  () .map  (strings, {collections.rever            Se  (strings) ;         return Strings.tostring  ()  })   

In the Subscribe function, we pass the Action1 class object as a parameter and use the string type as the pattern parameter. This defines the behavior of the subscriber, and when the last event is sent by the observer, the processed string is received. This is the simplest overloaded form of the. Subscribe () function (see Https://github.com/ReactiveX/RxJava/wiki/Observable#establishing-subscribers for more complex function overload signatures).

This example shows the transformation function. Map () and aggregate functions. ToList (), which is just the tip of the iceberg in terms of manipulating the data flow (all available data flow operation functions are visible https://github.com/ReactiveX/RxJava/wiki), But it shows the basic concept: in function-responsive programming, we can transform data streams by implementing separate units in a pipeline that implements data transformations or data manipulation functions. As needed, we can reuse these separate units in other pipelines made up of observables. By stitching together these observable units, we can compose more complex features, but at the same time keep them as easy-to-understand and modifiable composable logical small units.

Managing Threads with Scheduler

In the Web service example, we showed how to use Rxjava to initiate a network request. We talk about transformations, aggregating and subscribing to observable data streams, but we don't talk about how Web requests for observable traffic are implemented asynchronously.

This is how the FRP programming model invokes the Scheduler category-the policy defines which thread the observable stream event occurs in and which thread the subscriber consumes observable processing results. In the Web service example, we want the request to occur in a background thread, and the subscription behavior occurs in the main thread, so we define it as follows:

.subscribeOn(Schedulers.newThread())        .observeOn(AndroidSchedulers.mainThread())        //post them on an eventbus        .subscribe(githubUserDetails -> {            EventBus.getDefault().post(new UserDetailsLoadedCompleteEvent(githubUserDetails));        });

The Observable.subscribeon (Scheduler Scheduler) function specifies that the work of the Observable needs to be performed in the specified Scheduler thread. Observable.observeon (Scheduler Scheduler) specifies Observable in which Scheduler thread triggers the subscribers ' OnNext (), oncompleted (), and onerror () functions, and call Observable's Observeon () function, passing the correct scheduler to it.

The following is a possible use of scheduler:

    • Schedulers.computation (): Used for computational work such as event loops and callback processing, do not use this function in I/O (the Schedulers.io () function should be used);

    • Schedulers.from (Executor): uses the specified executor as the scheduler;

    • Schedulers.immediate (): Immediately begins execution of the task in the current thread;

    • Schedulers.io (): Asynchronous operation for I/O intensive work such as blocking I/O, a scheduler supported by a thread pool that grows on demand, and Schedulers.computation () for general computing work;

    • Schedulers.newthread (): Create a new thread for each unit of work;

    • Schedulers.test (): For testing purposes, supports advanced events for unit testing;

    • Schedulers.trampoline (): Queues the work in the current thread into the queue, and then in turn.

By setting the Observeon and Subscribeon Scheduler, we define which thread (Schedulers.newthread ()) the network request uses.

Next

We have covered a lot of basic content in this article, so here you should have a good understanding of how function-responsive programming works. Please review and understand the project described in this article, which is hosted on GitHub, read the Rxjava documentation and check out the Rxjava-koans project to test the driven way of mastering the function-responsive programming paradigm.

What is function-responsive programming (Java&android version)

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.