I have a Bound Service which responsible for downloading files and thus it knows the downloading status/progress. And the UI (Fragment or Activity) has to show/update download progress from the service.
Actually i think the common way is to use BroadcastReciever or a CallBack from Activity. But i heard somewhere about using RxJava (ReactiveX Programming) and mediator class (and Dagger to inject it into both service and activity) which is mentioned below.
So my question is how to handle RxJava with these bunch of stuff? Any Code Samples? Is there another efficient way than using intents?
Resource:
More efficient way of updating UI from Service than intents? [ see the first answer update ]
Required of RxJava/RxAndroid
1) OBSERVABLES
2) OBSERVERS
3) SUBSCRIBE
Plan where you need to place your 1, 2, 3,
OBSERVABLES go where the data is created, so In your case SERVICE
OBSERVERS go where the data needs to be consumed(or displayed), so that's your ACTIVITY
SUBSCRIBE goes anywhere where you have access to OBSERVABLE & OBSERVER, so lets use ACVITITY for that
Procedure and Code:
Firstly,
Prepare your OBSERVABLE in service like this
class MyService extends Service {
static PublishSubject<String> data = PublishSubject.create();
#Override
public void onStarCommand(Intent intent,int i, int j){
# DO THIS ANYWHER WHERE YOU GENERATE DATA
data.onNext("Hello");
}
public static Observable<String> getObservable(){
return data;
}
}
Secondly,
Prepare your OBSERVER(and OBSERVABLE) in Activity's onCreate
Observable<String> observable = MyService.getObservable();
Observer<String> observer = new Observer<String>() {
#Override
public void onCompleted() {
Log.d(TAG, "onCompleted: ");
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "onError: ",e);
}
#Override
public void onNext(String text) {
Log.d(TAG, "DATA reveived here: "+text);
}
};
Lastly
Link both OBSERVER and OBSERVABLE in Activity, else Observable will not respond,
use this again in onCreate
observable.subscribe(observer);
DONE, Now when even the data is triggered from Service using onNext(), the data arrives in Activity
The Binder subclass you use when something binds to your Service can expose a method that returns an Observable which emits progress data.
The caveat with this approach is that you have two resources which you need to release when the Activity instance is no longer valid: the service connection and the observable subscription.
Related
I have a rather general question.
Assuming I have a RecyclerView in some kind of a MainActivity. The content of this RecyclerView is being updated in multiple places in other activities.
For example there could be the possibility to make new entries by starting a new Activity and saving it there.In that case I would intuitively start that activity with startActivityForResult() and receive the changes in the onActivityResult() method in the MainActivity.
But lets say deeper inside the application, there is the possibility to delete all entries. Now how do we notify the MainActivity about this change in the dataset? I thought about setting a flag of some kind and clearing it after the content has been updated in the MainActivity. But somehow using global variables does not really follow the principle of proper encapsulation, does it?
Sorry for this vague question, but I find it quite hard to properly handle information flow in Android in a elegant manner, so here we are.
How about a local broadcast? You can find the idea of broadcast in this document. You need local broadcast and it is preferred if you want to pass data within your app only.
Android apps can send or receive broadcast messages from the Android system and other Android apps, similar to the publish-subscribe design pattern. These broadcasts are sent when an event of interest occurs. For example, the Android system sends broadcasts when various system events occur, such as when the system boots up or the device starts charging. Apps can also send custom broadcasts, for example, to notify other apps of something that they might be interested in (for example, some new data has been downloaded).
You can use Handler to pass the Message in Activity and then You have to update RecyclerView. Like,
1) In Activity.
public static Handler mHandler = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
if(msg.what == 1223){
//update RecyclerView
}
return false;
}
});
2) pass message When you want to update RecyclerView
Message msg = new Message();
msg.what = 1223;
Activity1.mHandler.sendMessage(msg);
You can use EventBus to handle it.
Define a class for your event
public static class MyEvent {
int event;
/* define your fields */
}
And prepare your subscriber in main activity
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMyEvent(MyEvent myEvent) {
switch(myEvent.event) {
/* Do what you need */
}
};
Now where you need to make change, call your subscriber like this:
MyEvent myEvent = new MyEvent();
myEvent.event = 1;
EventBus.getDefault().post(myEvent);
You can read more about EventBus in here
If you were using RxJava2, RxAndroid. Then you could try this.
Create a Bus:
public final class RxBus {
private static final BehaviorSubject<Object> behaviorSubject = BehaviorSubject.create();
public static BehaviorSubject<Object> getSubject() {
return behaviorSubject;
}
}
In your WaitingActivity where you want to receive data(where you want not to use onActivityResult in your case)
Disposable disposable = RxBus.getSubject().
subscribeWith(new DisposableObserver<Object>() {
#Override
public void onNext(Object o) {
if (o instanceof DataObject) {
//((DataObject) o).getValue();
}
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
});
In your activity where you want to send data
RxBus.getSubject().onNext(dataObject);
startActivity(new Intent(CurrentActivity.class, WaitingActivity.class));
Finally don't forget to dispose your disposable to avoid memory leaks in your WaitingActivity
#Override
protected void onDestroy() {
super.onDestroy();
disposable.dispose();
}
Your data should be separate from view, in model. If some other activity changes data ideally recycler view must be updated from there. So no matter which activity does what, when you refresh data on load or resume of your recycler view you will always get correct results.
I'm new to RxJava and I need to integrate it into an existing project. I need to refactor the existing code, adding Observables for networking (Socket IO).
Currently when a network request is made (client -> server) a callback (interface) is added to a HashMap and once the request is completed, it will deliver the data back to the caller:
// Singleton
public class API {
public void checkTicket(String ticketId, final String networkRequestId, Callback callback) {
// Add the callback to the hashmap
registerCallback(networkRequestId, callback);
JSONObject json = RequestFactory.createTicketCheckerRequest(ticketId);
// Make the network request
getSocket().checkTicket(json, new Callback() {
#Override
public void onRequestDone(Response response) {
// Retrieve the callback
callback = getCallback(networkRequestId);
// Don't keep reference, remove from hashmap
unsubscribeCallback(networkRequestId);
// Check if it's unsuccessful and build the corresponding error response
if (!response.isSuccess()) {
// build custom error response
response = ResponseFactory.buildError(response);
}
// Deliver response from server
callback.onRequestDone(response);
}
});
}
}
It can be called from Activities and Fragments:
private void checkTicket() {
String ticketId = editText.getText().toString();
API.getInstance().checkTicket(ticketId, REQUEST_ID_CHECK_TICKET, new Callback() {
#Override
protected void onRequestDone(Response response) {
textView.setText(response.getData());
}
});
}
#Override
public void onDestroy() {
super.onDestroy();
// Removes callback from HashMap in case of the UI is destroyed before the arrives
API.getInstance().unsubscribe(REQUEST_ID_CHECK_TICKET);
}
The above code works but it's really tight with the UI's lifecycle and sometimes it's causing memory leak, because onDestroy() is not getting called (if you navigate between activities and Android OS kills the "paused" activities from the stack) or because the anonymous inner classes (callbacks) which are holding a reference to the UI, and from now on I need to support orientation change.
This is the code that I have implemented using RxJava:
API:
public Observable<Response> checkTicket(String ticketId) {
return Observable.create(subscriber -> {
JSONObject json = RequestFactory.createTicketCheckerRequest(ticketId);
// Make the network request
getSocket().checkTicket(json, new Callback() {
#Override
public void onRequestDone(Response response) {
subscriber.onNext(response);
subscriber.onComplete();
}
});
});
}
This is how it's called from the UI:
private CompositeDisposable mDisposables = new CompositeDisposable();
private void checkTicket() {
//////
Disposable disposable = API.getInstance().checkTicket(ticketId)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(result -> {
textView.setText(result.getData());
});
mDisposables.add(disposable);
}
#Override
public void onStop() {
super.onStop();
if (!mDisposables.isDisposed()) {
mDisposables.dispose();
}
}
The above RxJava is working, however if an orientation change occurs the data is not returned because the Observer is unsubscribed.
Is the above implementation correct?
How should I subscribe without executing the request? Subscribe and wait for data change.
Another alternative would be EventBus but this is just Plan B. EventBus fits exactly my requirements, subscribe and wait for data change, but I want to evict boilerplate.
I have read other articles by using Fragment's setRetainInstance(true) but what if I need to use it from an Activity? What if I don't want to retain the state of the Fragment?
People suggested to use MVVM or MVP architecture, but I don't have the time to refactor the entire project.
I will suggest you move to MVVM. With your presented code, it is not that hard. Here is a sample code of how it will look like
Your ModelView
public class MyViewModel extends ViewModel {
private CompositeDisposable mDisposables = new CompositeDisposable();
private MutableLiveData<Response> response;
public LiveData<Response> getResponse() {
if (response == null) {
response = new MutableLiveData<Response>();
loadData();
}
return response;
}
private void loadData() {
Disposable disposable = API.getInstance().checkTicket(ticketId)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(result -> {
response.postValue(result.getData());
});
mDisposables.add(disposable);
}
void onCleared()
{
super.onCleared();
mDisposables.clear(); //no more leaks. It takes care of lifecycle for you
}
}
Your Activity
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getResponse().observe(this, response -> {
// update UI
textView.setText(response); //response = Response object from Live data
});
}
}
If you don't want to handle configuration changes and want to cache data from observables, you can use BehaviorSubjects and a hot observable. This will allow you to get the most recent item that the observable published.
Other than that, I suggest you use the ViewModel from the architecture components. It will allow you to create a component that is bound to the activity but will not be affected by the lifecycle (except termination, obviously). Surprisingly enough, ViewModelProviders are implemented as fragments with setRetainInstance(true). You don't have to completely refactor the entire app. Just move the ones that you want to preserve during configuration changes.
You need to consider the logical scope of your network requests, and this is entirely separate from whether you're using RxJava. Background tasks like network requests need to be owned by an Android component (Application, Activity, etc.) with the appropriate lifetime. The usual way to make activity-scoped background tasks survive a config change is to host them in a retained fragment. You would still do that if you were using RxJava.
Android OS kills the "paused" activities from the stack
This doesn't happen unless something has changed in Android 8 or newer. The documentation suggests that the framework could destroy individual activities in the backstack, but currently it only destroys the entire task when it's in the background. Your app is correct and future-proof if and only if it works with the "don't keep activities" developer option on.
I working on android project with clean architecture.
I have the below class:
public abstract class RxBaseInteractor<T, Params> {
private final CompositeDisposable disposables;
public RxBaseInteractor() {
this.disposables = new CompositeDisposable();
}
abstract public Observable<T> buildUseCaseObservable(Params params);
public void execute(DisposableObserver<T> observer, Params params) {
Preconditions.checkNotNull(observer);
final Observable<T> observable = this.buildUseCaseObservable(params)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
addDisposable(observable.subscribeWith(observer));
}
public void dispose() {
if (!disposables.isDisposed()) {
disposables.dispose();
}
}
protected void addDisposable(Disposable disposable) {
Preconditions.checkNotNull(disposable);
Preconditions.checkNotNull(disposables);
disposables.add(disposable);
}
}
So execute(..) take a DisposableObserver and then there is a dispose() method which is called to dispose this observable.
In my case the observable may come from WebApi using retrofit or cache using Realm.
Now in the presenter onDestroy(), i called the interactor.dispose() like:
#Override public void destroy() {
super.destroy();
myInteractor.dispose();
}
which is called after that from the view:
#Override public void onDestroy() {
super.onDestroy();
if (getPresenter() != null) {
getPresenter().destroy();
}
}
I fully understanding the architecture and also i understand disposing un-managed network or database resources but i need to fully understand if in this case the dispose of observable really matter as i thought that Retrofit or Realm auto manage closing a connections and disposing there resources.
I think it's not related to dispose realm or retrofit resources but it may be related to unsubscribe on the observable it self as i checked the documentation and i found :
Class DisposableObserver: An abstract Observer that allows asynchronous cancellation by
implementing Disposable. All pre-implemented final methods are
thread-safe.
Use the public dispose() method to dispose the sequence from within an
onNext implementation.
But i still not understand the benefits of using it. Is it for unsubscribe from the observable when destroying the view so it will go from onNext() to onComplete() and close the subscription on the emitter?
The reason behind using dispose method is because after the system initiate the view (activity or fragment), the subscription gets start and then you have decided to go back or initiate another view while the older subscription is still getting executed and didn't finish its job. This means that it's still in the memory which will cause a memory leak. So you have to call dispose method for unsubscribe.
Adding more to #abozaid's answer, When older subscription is still On and in the meantime, our user switches to other view (activity or fragment) or closes older view (or application itself), it'll definitely leak memory.
But, if we were observing observable for UI updation with AndroidSchedulers.mainThread() scheduler, then our code would crash because at the time of updating UI, the view and context would have gone away (or destroyed).
myObservable.observeOn(AndroidSchedulers.mainThread()) // like this
One other point, I can add here is that, even if we handle the crash by putting precaution in code, the subscription running unused would hamper performance at some stage.
I am new in Android and Retrofit and I am facing one problem.
I want to have my lets say "ServerCommunication" class (singelton) where all Retrofit magic is done and it will have public methods where REST calls are done.
I want to use this "ServerCommunication" instance in my activities to call Rest service, but thats it. Application logic should be done in activity. So this way some activity Login calls method Login(POJORequest) in "ServerCommunication) where actual REST call via Retrofit framework is done and some POJOResponse is returned. So Activity doesn't care about REST communication while ServerCommunication doesn't care about what logic that should be applied to response from REST service since.
With retrofit 2 I do not understand how I can block Activity to wait for response from retrofit and how it can be returned. Well, I might think I can use some callback methods in activity so those methods can be called from ServerCommunication" in OnPostExecute() to apply some logic based on data from response. It's just I think it should be simpler approach.
Well, to clarify all this mess above imagine simple case: You have data in you main activity, you pass this data to your communication class where REST call is done and response is received. This response must be validated in order to continue. And you want this validation to be done in main activity and NOT in communication class.
What is pattern to do that in Android with Retrofit2 ?
Thank you in advance
What I normally do:
Create your Interface (where you have all your REST methods - GET & POST etc)
Create a class that does the actual calls with corresponding methods (refer to the interface REST methods). I would call it something like ServiceAPIImplementor. This is where you actually create your Retrofit adapter.
In your activity, create an instance of your implementor class and call the methods and pass the expected arguments.
After calling the methods, you should probably show a progress dialog to let the user know that something is going on.
When the onResponse or onFailure method is called, use an Event pattern (EventBus library?) to notify the activity that the network operation has been completed. Once the activity has received the notification, it should then dismiss the progress dialog and update the UI accordingly - with the newly received data or completed operation (expected outcome).
I hope this helps you get closer to what you are trying to achieve!
Service interface (IPhotoService):
#GET("/photos/kudos")
Call<String> fetchKudos(#Header("Authorization") String authorization,
#Query("offset") int offset, #Query("mt") boolean mt);
Service impl (PhotoService):
private GoApiProvider<IPhotoService> mGoProvider = new GoApiProvider<>();
public Promiser<List<Photo>, HttpError> fetchKudos() {
return new Promiser<>((resolve, reject) ->
mGoProvider.getService(IPhotoService.class).fetchKudos(mSession.getToken(),
mOffsetKudos, true).enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if (response.isSuccessful()) {
PhotoParser JSON = new PhotoParser();
try {
mOffsetKudos = mOffsetKudos + 20;
resolve.run(JSON.photosFromJson(response.body()));
} catch (JSONException e) {
Log.e("fetchKudos", e.toString());
}
} else {
reject.run(new HttpError(response.code(), response.message()));
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
reject.run(new HttpError(YPErrorType.Undefined.getType(), t.getMessage()));
}
})
);
}
Activity or Fragment :
private void loadPhoto() {
new PhotoService().fetchKudos()
.success(this::resultSucceeded)
.error(this::resultError);
}
private void resultSucceeded(List<Photo> photos) {
mPhotoAdapter.setItems(photos);
}
private void resultError(HttpError httpError) {
httpErrorToast(httpError);
}
If you want to use Promizer: Click here
I'm switching to Retrofit and trying to understand proper architecture for using it with async callbacks.
For example I have an interface:
interface RESTService{
#GET("/api/getusername")
void getUserName(#Query("user_id") String userId,
Callback<Response> callback);
}
And I run this from main activity:
RestAdapter restAdapter = new RestAdapter.Builder()
.setServer("WEBSITE_URL")
.build();
RESTService api = restAdapter.create(RESTService.class);
api.getUserName(userId, new Callback<Response> {...});
Then user rotates the device and I have newly created activity... What was happen here? How can I get response to the new activity (I assume that api call in background will execute longer than first activity life). Maybe I must use static instance of callback or what? Please show me the right way...
Use otto.
There are a lot of samples to mix otto and retrofit, for example https://github.com/pat-dalberg/ImageNom/blob/master/src/com/dalberg/android/imagenom/async/FlickrClient.java
Or read this post http://www.mdswanson.com/blog/2014/04/07/durable-android-rest-clients.html
It answers on almost all questions
For potential long running server calls i use an AsyncTaskLoader. For me, the main advantage of Loaders are the activity-lifecycle handling. onLoadFinished is only called if your activity is visible to the user. Loaders are also shared between activity/fragment and orientation changes.
So i created an ApiLoader which uses retrofits synchronous calls in loadInBackground.
abstract public class ApiLoader<Type> extends AsyncTaskLoader<ApiResponse<Type>> {
protected ApiService service;
protected ApiResponse<Type> response;
public ApiLoader(Context context) {
super(context);
Vibes app = (Vibes) context.getApplicationContext();
service = app.getApiService();
}
#Override
public ApiResponse<Type> loadInBackground() {
ApiResponse<Type> localResponse = new ApiResponse<Type>();
try {
localResponse.setResult(callServerInBackground(service));
} catch(Exception e) {
localResponse.setError(e);
}
response = localResponse;
return response;
}
#Override
protected void onStartLoading() {
super.onStartLoading();
if(response != null) {
deliverResult(response);
}
if(takeContentChanged() || response == null) {
forceLoad();
}
}
#Override
protected void onReset() {
super.onReset();
response = null;
}
abstract protected Type callServerInBackground(SecondLevelApiService api) throws Exception;
}
In your activity you init this loader like this:
getSupportLoaderManager().initLoader(1, null, new LoaderManager.LoaderCallbacks<ApiResponse<DAO>>() {
#Override
public Loader<ApiResponse<DAO>> onCreateLoader(int id, Bundle args) {
spbProgress.setVisibility(View.VISIBLE);
return new ApiLoader<DAO>(getApplicationContext()) {
#Override
protected DAO callServerInBackground(ApiService api) throws Exception {
return api.requestDAO();
}
};
}
#Override
public void onLoadFinished(Loader<ApiResponse<DAO>> loader, ApiResponse<DAO> data) {
if (!data.hasError()) {
DAO dao = data.getResult();
//handle data
} else {
Exception error = data.getError();
//handle error
}
}
#Override
public void onLoaderReset(Loader<ApiResponse<DAO>> loader) {}
});
If you want to request data multiple times use restartLoader instead of initLoader.
I've been using a kind of MVP (ModelViewPresenter) implementation on my Android apps. For the Retrofit request I made the Activity calls it's respective Presenter, which in turn makes the Retrofit Request and as a parameter I send a Callback with a custom Listener attached to it (implemented by the presenter). When the Callback reach onSuccess or onFailure methods I call the Listener's respective methods, which calls the Presenter and then the Activity methods :P
Now in case the screen is turned, when my Activity is re-created it attaches itself to the Presenter. This is made using a custom implementation of Android's Application, where it keeps the presenters' instance, and using a map for recovering the correct presenter according to the Activity's class.
I don't know if it's the best way, perhaps #pareshgoel answer is better, but it has been working for me.
Examples:
public abstract interface RequestListener<T> {
void onSuccess(T response);
void onFailure(RetrofitError error);
}
...
public class RequestCallback<T> implements Callback<T> {
protected RequestListener<T> listener;
public RequestCallback(RequestListener<T> listener){
this.listener = listener;
}
#Override
public void failure(RetrofitError arg0){
this.listener.onFailure(arg0);
}
#Override
public void success(T arg0, Response arg1){
this.listener.onSuccess(arg0);
}
}
Implement the listener somewhere on the presenter, and on the overrode methods call a presenter's method that will make the call to the Activity. And call wherever you want on the presenter to init everything :P
Request rsqt = restAdapter.create(Request.class);
rsqt.get(new RequestCallback<YourExpectedObject>(listener));
Firstly, your activity leaks here because this line:
api.getUserName(userId, new Callback {...})
creates an anonymous Callback class that holds a strong reference to you MainActivity. When the device is rotated before the Callback is called, then the MainActivity will not be garbage collected. Depending on what you do in the Callback.call(), your app may yield undefined behaviour.
The general idea to handle such scenarios is:
Never create a non-static inner class (or an anonymous class as mentioned in the problem).
Instead create a static class that holds a WeakReference<> to the Activity/Fragment.
The above just prevents Leaks. It still does not help you get the Retrofit call back to your Activity.
Now, to get the results back to your component (Activity in your case) even after configuration change, you may want to use a headless retained fragment attached to your Activity, which makes the call to Retrofit. Read more here about Retained fragment - http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)
The general idea is that the Fragment automatically attaches itself to the Activity on configuration change.
I highly recommend you watch this video given at Google I/O.
It talks about how to create REST requests by delegating them to a service (which is almost never killed). When the request is completed it is immediately stored into Android's built-in database so the data is immediately available when your Activity is ready.
With this approach, you never have to worry about the lifecycle of the activity and your requests are handled in a much more decoupled way.
The video doesn't specifically talk about retrofit, but you can easily adapt retrofit for this paradigm.
Use Robospice
All components in your app which require data, register with the spice service. The service takes care of sending your request to the server (via retrofit if you want). When the response comes back, all components which registered get notified. If there is one of them not available any more (like an activity which got kicked because of rotation), it's just not notified.
Benefit: One single request which does not get lost, no matter whether you rotate your device, open new dialogs/fragments etc...
Using Retrofit2 to handle orientation change. I was asked this in a job interview and was rejected for not knowing it at the time but here it is now.
public class TestActivity extends AppCompatActivity {
Call<Object> mCall;
#Override
public void onDestroy() {
super.onDestroy();
if (mCall != null) {
if (mCall.isExecuted()) {
//An attempt will be made to cancel in-flight calls, and
// if the call has not yet been executed it never will be.
mCall.cancel();
}
}
}
}