Skip to content

Using Fast Android Networking Library With RxJava

AMIT SHEKHAR edited this page Mar 19, 2017 · 18 revisions

What is RxJava?

RxJava is used for reactive programming. In reactive programming, the consumer reacts to the data as it comes in. Reactive programming allows for event changes to propagate to registered observers.

The main components: observables, observers, and subscriptions

RxJava provides Observables and Observers. Observables can send out values. Observers watch Observables by subscribing to them.

Observers are notified when an Observable emits a value and when the Observable says an error has occurred. They are also notified when the Observable sends the information confirming that it no longer has any values to emit.

The corresponding functions are onNext, onError, and onCompleted() from the Observer interface. An instance of Subscription represents the connection between an observer and an observable. We can call unsubscribe() on this instance to remove the connection.

Let’s understand this better by exploring an example:

Observable<String> observable = Observable.just("Cricket", "Football");

This is an Observable which emits strings and it can be observed by an Observer.

Let’s create an Observer:

Observer<String> observer = newObserver<String>() {
@OverridepublicvoidonCompleted() {
}
@OverridepublicvoidonError(Throwablee) {
}
@OverridepublicvoidonNext(Stringresponse) {
Log.d(TAG, "response : " + response);
}
};

Now, we have to connect both Observable and Observer with a subscription. Only then can it actually do anything:

observable.subscribe(observer);

This will cause the following output, one line at a time:

Cricket
Football

This means the Observable emits two strings, one by one, which are observed by Observer.

Other highly recommended references to learn more about RxJava

RxJava is an Art and endless possibilities await those who can master it. So let’s start mastering it by learning how to use it with the network layer.

Found this project useful ❤️

  • Support by clicking the ⭐ button on the upper right of this page. ✌️

Now using RxJava with Fast Android Networking

Add this in your build.gradle

compile 'com.amitshekhar.android:rx-android-networking:1.0.0'

For RxJava2

compile 'com.amitshekhar.android:rx2-android-networking:1.0.0'

RxJava2 Support, check here.

Then initialize it in onCreate() Method of application class :

AndroidNetworking.initialize(getApplicationContext());

I will try to give you a more friendly introduction to RxJava Operators, with plenty of complete code samples that you can actually compile and modify. If you read all the examples here, you will be able to learn most of the RxJava operators.

Using Map Operator

/* * Here we are getting ApiUser Object from api server* then we are converting it into User Object because * may be our database support User Not ApiUser Object* Here we are using Map Operator to do that*/RxAndroidNetworking.get("https://fierce-cove-29863.herokuapp.com/getAnUser/{userId}")
.addPathParameter("userId", "1")
.build()
.getObjectObservable(ApiUser.class)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(newFunc1<ApiUser, User>() { // takes ApiUser and returns User@OverridepublicUsercall(ApiUserapiUser) {
// here we get ApiUser from serverUseruser = newUser(apiUser);
// then by converting, we are returing userreturnuser;
}
})
.subscribe(newObserver<User>() {
@OverridepublicvoidonCompleted() {
// do anything onComplete
}
@OverridepublicvoidonError(Throwablee) {
// handle error
}
@OverridepublicvoidonNext(Useruser) {
// do anything with user
}
});

Using Zip Operator - Combining two network request

/* * Here we are making two network calls * One returns the list of cricket fans* Another one returns the list of football fans* Then we are finding the list of users who loves both*//** This observable return the list of User who loves cricket*/privateObservable<List<User>> getCricketFansObservable() {
returnRxAndroidNetworking.get("https://fierce-cove-29863.herokuapp.com/getAllCricketFans")
.build()
.getObjectListObservable(User.class);
}
/** This observable return the list of User who loves Football*/privateObservable<List<User>> getFootballFansObservable() {
returnRxAndroidNetworking.get("https://fierce-cove-29863.herokuapp.com/getAllFootballFans")
.build()
.getObjectListObservable(User.class);
}
/** This do the complete magic, make both network call* and then returns the list of user who loves both* Using zip operator to get both response at a time*/privatevoidfindUsersWhoLovesBoth() {
// here we are using zip operator to combine both requestObservable.zip(getCricketFansObservable(), getFootballFansObservable(),
newFunc2<List<User>, List<User>, List<User>>() {
@OverridepublicList<User> call(List<User> cricketFans,
List<User> footballFans) {
List<User> userWhoLovesBoth = filterUserWhoLovesBoth(cricketFans, footballFans);
returnuserWhoLovesBoth;
}
}
).subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(newObserver<List<User>>() {
@OverridepublicvoidonCompleted() {
// do anything onComplete
}
@OverridepublicvoidonError(Throwablee) {
// handle error
}
@OverridepublicvoidonNext(List<User> users) {
// do anything with user who loves both
}
});
}
privateList<User> filterUserWhoLovesBoth(List<User> cricketFans, List<User> footballFans) {
List<User> userWhoLovesBoth = newArrayList<>();
// your logic to filter who loves bothreturnuserWhoLovesBoth;
}

Using FlatMap And Filter Operators

/* * First of all we are getting my friends list from* server, then by using flatMap we are emitting users* one by one and then after applying filter we are* returning only those who are following me one by one.*//** This observable return the list of User who are my friends*/privateObservable<List<User>> getAllMyFriendsObservable() {
returnRxAndroidNetworking.get("https://fierce-cove-29863.herokuapp.com/getAllFriends/{userId}")
.addPathParameter("userId", "1")
.build()
.getObjectListObservable(User.class);
}
/** This method does all*/publicvoidflatMapAndFilter() {
getAllMyFriendsObservable()
.flatMap(newFunc1<List<User>, Observable<User>>() { // flatMap - to return users one by one@OverridepublicObservable<User> call(List<User> usersList) {
returnObservable.from(usersList); // returning(emitting) user one by one from usersList.
}
})
.filter(newFunc1<User, Boolean>() { // filter operator@OverridepublicBooleancall(Useruser) {
// filtering user who follows me.returnuser.isFollowing;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(newObserver<User>() {
@OverridepublicvoidonCompleted() {
// do anything onComplete
}
@OverridepublicvoidonError(Throwablee) {
// handle error
}
@OverridepublicvoidonNext(Useruser) {
// only the user who is following me comes here one by one
}
});
}

Using Take Operator

/* Here first of all, we get the list of users from server.* Then using using take operator, it only emits* required number of users. *//** This observable return the list of users.*/privateObservable<List<User>> getUserListObservable() {
returnRxAndroidNetworking.get("https://fierce-cove-29863.herokuapp.com/getAllUsers/{pageNumber}")
.addPathParameter("pageNumber", "0")
.addQueryParameter("limit", "10")
.build()
.getObjectListObservable(User.class);
}
getUserListObservable()
.flatMap(newFunc1<List<User>, Observable<User>>() { // flatMap - to return users one by one@OverridepublicObservable<User> call(List<User> usersList) {
returnObservable.from(usersList); // returning user one by one from usersList.
}
})
.take(4) // it will only emit first 4 users out of all
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(newObserver<User>() {
@OverridepublicvoidonCompleted() {
// do something onCompletion
}
@OverridepublicvoidonError(Throwablee) {
// handle error
}
@OverridepublicvoidonNext(Useruser) {
// only four user comes here one by one
}
});

Using flatMap Operator

/* Here first of all, we get the list of users from server.* Then for each userId from user, it makes the network call to get the detail * of that user. * Finally, we get the userDetail for the corresponding user one by one*//** This observable return the list of users.*/privateObservable<List<User>> getUserListObservable() {
returnRxAndroidNetworking.get("https://fierce-cove-29863.herokuapp.com/getAllUsers/{pageNumber}")
.addPathParameter("pageNumber", "0")
.addQueryParameter("limit", "10")
.build()
.getObjectListObservable(User.class);
}
/** This observable return the userDetail corresponding to the user.*/privateObservable<UserDetail> getUserDetailObservable(longuserId) {
returnRxAndroidNetworking.get("https://fierce-cove-29863.herokuapp.com/getAnUserDetail/{userId}")
.addPathParameter("userId", String.valueOf(userId))
.build()
.getObjectObservable(UserDetail.class);
}
/** This method do the magic - first gets the list of users* from server.Then, for each user, it makes the network call to get the detail * of that user.* Finally, we get the UserDetail for the corresponding user one by one*/publicvoidflatMap() {
getUserListObservable()
.flatMap(newFunc1<List<User>, Observable<User>>() { // flatMap - to return users one by one@OverridepublicObservable<User> call(List<User> usersList) {
returnObservable.from(usersList); // returning user one by one from usersList.
}
})
.flatMap(newFunc1<User, Observable<UserDetail>>() {
@OverridepublicObservable<UserDetail> call(Useruser) {
// here we get the user one by one// and returns corresponding getUserDetailObservable// for that userIdreturngetUserDetailObservable(user.id);
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(newObserver<UserDetail>() {
@OverridepublicvoidonCompleted() {
// do something onCompleted
}
@OverridepublicvoidonError(Throwablee) {
// handle error
}
@OverridepublicvoidonNext(UserDetailuserDetail) {
// here we get userDetail one by one for all usersLog.d(TAG, "userDetail id : " + userDetail.id);
Log.d(TAG, "userDetail firstname : " + userDetail.firstname);
Log.d(TAG, "userDetail lastname : " + userDetail.lastname);
}
});
}

Using combination of flatMap with zip Operator

/* Very Similar to above example, only change is * that, here we are using zip after flatMap to * combine(pair) User and UserDetail*//** This method do the magic - first gets the list of users* from server.Then, for each user, it makes the network call to get the detail * of that user.* Finally, we get the UserDetail for the corresponding user one by one*/privatevoidflatMapWithZip() {
getUserListObservable()
.flatMap(newFunc1<List<User>, Observable<User>>() { // flatMap - to return users one by one@OverridepublicObservable<User> call(List<User> usersList) {
returnObservable.from(usersList); // returning user one by one from usersList.
}
})
.flatMap(newFunc1<User, Observable<Pair<UserDetail, User>>>() {
@OverridepublicObservable<Pair<UserDetail, User>> call(Useruser) {
// here we get the user one by one and then we are zipping// two observable - one getUserDetailObservable (network call to get userDetail)// and another Observable.just(user) - just to emit userreturnObservable.zip(getUserDetailObservable(user.id), // zip to combine two observableObservable.just(user),
newFunc2<UserDetail, User, Pair<UserDetail, User>>() {
@OverridepublicPair<UserDetail, User> call(UserDetailuserDetail, Useruser) {
// runs when network call completes // we get here userDetail for the corresponding userreturnnewPair<>(userDetail, user); // returning the pair(userDetail, user)
}
});
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(newObserver<Pair<UserDetail, User>>() {
@OverridepublicvoidonCompleted() {
// do something onCompleted
}
@OverridepublicvoidonError(Throwablee) {
// handle error
}
@OverridepublicvoidonNext(Pair<UserDetail, User> pair) {
// here we are getting the userDetail for the corresponding user one by oneUserDetailuserDetail = pair.first;
Useruser = pair.second;
Log.d(TAG, "userId : " + user.id);
Log.d(TAG, "userDetail firstname : " + userDetail.firstname);
Log.d(TAG, "userDetail lastname : " + userDetail.lastname);
}
});
}

Binding Networking with Activity Lifecycle

publicclassSubscriptionActivityextendsActivity {
privatestaticfinalStringTAG = SubscriptionActivity.class.getSimpleName();
privatestaticfinalStringURL = "http://i.imgur.com/AtbX9iX.png";
privateStringdirPath;
privateStringfileName = "imgurimage.png";
Subscriptionsubscription;
@OverrideprotectedvoidonCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
dirPath = Utils.getRootDirPath(getApplicationContext());
subscription = getObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getObserver());
}
@OverrideprotectedvoidonDestroy() {
super.onDestroy();
if (subscription != null) {
// unsubscribe it when activity onDestroy is calledsubscription.unsubscribe();
}
}
publicObservable<String> getObservable() {
returnRxAndroidNetworking.download(URL, dirPath, fileName)
.build()
.getDownloadObservable();
}
privateObserver<String> getObserver() {
returnnewObserver<String>() {
@OverridepublicvoidonCompleted() {
Log.d(TAG, "onCompleted");
}
@OverridepublicvoidonError(Throwablee) {
Log.d(TAG, "onError " + e.getMessage());
}
@OverridepublicvoidonNext(Stringresponse) {
Log.d(TAG, "onResponse response : " + response);
}
};
}
}

Making a GET Request

RxAndroidNetworking.get("https://fierce-cove-29863.herokuapp.com/getAllUsers/{pageNumber}")
.addPathParameter("pageNumber", "0")
.addQueryParameter("limit", "3")
.build()
.getJSONArrayObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(newObserver<JSONArray>() {
@OverridepublicvoidonCompleted() {
// do anything onComplete
}
@OverridepublicvoidonError(Throwablee) {
// handle error
}
@OverridepublicvoidonNext(JSONArrayresponse) {
//do anything with response
}
});

Making a POST Request

RxAndroidNetworking.post("https://fierce-cove-29863.herokuapp.com/createAnUser")
.addBodyParameter("firstname", "Amit")
.addBodyParameter("lastname", "Shekhar")
.build()
.getJSONObjectObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(newObserver<JSONObject>() {
@OverridepublicvoidonCompleted() {
// do anything onComplete
}
@OverridepublicvoidonError(Throwablee) {
// handle error
}
@OverridepublicvoidonNext(JSONObjectresponse) {
//do anything with response
}
});

Downloading a file from server

RxAndroidNetworking.download("http://i.imgur.com/AtbX9iX.png",dirPath,imgurimage.png)
.build()
.setDownloadProgressListener(newDownloadProgressListener() {
@OverridepublicvoidonProgress(longbytesDownloaded, longtotalBytes) {
// do anything with progress 
}
})
.getDownloadObservable()
.subscribeOn(Schedulers.io()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(newObserver<String>() {
@OverridepublicvoidonCompleted() {
// do anything onComplete
}
@OverridepublicvoidonError(Throwablee) {
// handle error
}
@OverridepublicvoidonNext(Stringresponse) {
//gives response = "success"
}
});

Uploading a file to server

RxAndroidNetworking.upload("https://fierce-cove-29863.herokuapp.com/uploadImage")
.addMultipartFile("image", newFile(imageFilePath)) .build()
.setUploadProgressListener(newUploadProgressListener() {
@OverridepublicvoidonProgress(longbytesUploaded, longtotalBytes) {
// do anything with progress 
}
})
.getJSONObjectObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(newObserver<JSONObject>() {
@OverridepublicvoidonCompleted() {
// do anything onComplete
}
@OverridepublicvoidonError(Throwablee) {
// handle error
}
@OverridepublicvoidonNext(JSONObjectresponse) {
//do anything with response
}
});

Using it with your own JAVA Object - JSON Parser

/*--------------Example One -> Getting the userList----------------*/RxAndroidNetworking.get("https://fierce-cove-29863.herokuapp.com/getAllUsers/{pageNumber}")
.addPathParameter("pageNumber", "0")
.addQueryParameter("limit", "3")
.build()
.getObjectListObservable(User.class)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(newObserver<List<User>>() {
@OverridepublicvoidonCompleted() {
// do anything onComplete 
}
@OverridepublicvoidonError(Throwablee) {
// handle error
}
@OverridepublicvoidonNext(List<User> users) {
// do anything with response Log.d(TAG, "userList size : " + users.size());
for (Useruser : users) {
Log.d(TAG, "id : " + user.id);
Log.d(TAG, "firstname : " + user.firstname);
Log.d(TAG, "lastname : " + user.lastname);
}
}
}); /*--------------Example Two -> Getting an user----------------*/RxAndroidNetworking.get("https://fierce-cove-29863.herokuapp.com/getAnUser/{userId}")
.addPathParameter("userId", "1")
.build()
.getObjectObservable(User.class)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(newObserver<User>() {
@OverridepublicvoidonCompleted() {
// do anything onComplete 
}
@OverridepublicvoidonError(Throwablee) {
// handle error
}
@OverridepublicvoidonNext(Useruser) {
// do anything with response Log.d(TAG, "id : " + user.id);
Log.d(TAG, "firstname : " + user.firstname);
Log.d(TAG, "lastname : " + user.lastname);
}
});
/*-- Note : TypeToken and getParseObservable is important here --*/

Error Code Handling

publicvoidonError(Throwablee) {
if (einstanceofANError) {
ANErroranError = (ANError) e;
if (anError.getErrorCode() != 0) {
// received ANError from server// error.getErrorCode() - the ANError code from server// error.getErrorBody() - the ANError body from server// error.getErrorDetail() - just a ANError detailLog.d(TAG, "onError errorCode : " + anError.getErrorCode());
Log.d(TAG, "onError errorBody : " + anError.getErrorBody());
Log.d(TAG, "onError errorDetail : " + anError.getErrorDetail());
// get parsed error object (If ApiError is your class)ApiErrorapiError = error.getErrorAsObject(ApiError.class);
} else {
// error.getErrorDetail() : connectionError, parseError, requestCancelledErrorLog.d(TAG, "onError errorDetail : " + anError.getErrorDetail());
}
} else {
Log.d(TAG, "onError errorMessage : " + e.getMessage());
}
}

In RxJava, you can do too many things by applying the operators (flatMap, filter, map, mapMany, zip, etc) available in RxJava.

License

 Copyright (C) 2016 Amit Shekhar
Copyright (C) 2011 Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Contributing to Fast Android Networking

Just make pull request. You are in!