Best approach to use Volley - android

What is the best way to use volley? First I used volley as they provided so sometime it throws
java.lang.IllegalStateException because the activity has been finish till the response from volley return.
So I went online and find a solution using event bus. So right know I am using event bus, register a event on onResume and unregister it on onPause, and on onResponse post the registered event.
But I want some generic and better approach.
PS: There is also a library doing the same thing OttoVolleyDoneRight I don't want to use that too.

You should have a singleton instance of Volley - which holds all volley parts there (such as RequestQueue). You should init this singleton as part of your application object (or event as part of your main activity, and then destroy it in onDestroy).
Here's a sample of how to create a Volley singleton:
https://developer.android.com/training/volley/requestqueue.html
I don't think that working with events and volley is correct. You should have a concrete listener for each request you make. What you can do is simple: Create a class (or an inner static class) that implements Volley Listener/ErrorListener and holds your activity in a WeakReference - once callbacks from volley are called just check if your activity reference still exists and do what you want. If it doesn't exist then it was probably was closed (and GC picked up the WeakReference).
This way you avoid memory leaks (leaking the activity) and handle your callbacks correctly.
** I don't know what your app is doing but I'm pretty sure that you don't really need to handle network callbacks in your activity but rather in some adapter you have (of a ListView for example).
Pass a new instance of this listener to Volley as a callback.
public class MyImageListener extends implements Response.Listener<T>, Response.ErrorListener {
private WeakReference<Activity> mActivity;
private final String mUrl;
public MyImageListener(String url, Activity activity) {
mUrl = url;
mActivity = new WeakReference<>(activity);
}
#Override
public void onErrorResponse(VolleyError error) {
Activity activity = mActivity.get();
if (activity != null) {
// Activity is alive, do what you need with it
}
}
#Override
public void onResponse(T result) {
Activity activity = mActivity.get();
if (activity != null) {
// Activity is alive, do what you need with it
}
}
}
Cheers!

Related

Is there any alternative of Intent to send data for Class (Not Activity)?

Previously, I was using activities in my project and was sending data using Intent from one activity to another which works perfectly fine.
Now requirement changes, and I have to show all things on Dialogs, instead of activities, so there will separate 3-4 dialog class and single activity.
Now I want the same flow on dialog also, but there is a problem to pass data temporarily exactly how intent works!
I tried with Singleton, but the problem is it remains data until the whole lifecycle, but I don't want that.
I can't use the interface also because there are lots of things to pass.
Also, I can't use bundle fundle n all those, because this all depends on runtime, I meant it depends upon if user fill input
Question: How can I pass data from one class to another class or activity? and it should not save value for the whole lifecycle.
statically sending data is an option but its not good way, because memory to static variables is assigned at Application level and can be cleared when memory needed. The best way is to use
Object Oriented approach
For example if you have a class, You can send data in class constructor, or can send it through function call
class class1
{
public class1(Object data) { // constructor
// you can use this data
}
//// Or through function call
public void func(Object data) { // this method can be called by other classes which has its object
// you can use this data
}
}
Now lets assume you have another class
class class2
{
class1 obj = new class1(your_data_object); // if you want to send through constructor
void someMethod() {
obj.func(your_data_object); // send data whatever you want to send
}
}
Obviously your case will not be as simple as my example, but to handle complex cases you can implement interfaces.
Interface Example
define an interface
interface myListener {
public void listen(Object data);
}
now lets say you want to call class2 method from class1. then class2 must implement this interface.
public class class2 implements myListener {
#override
public void listen(Object data)
{
/// you got data here, do whatever you want to do that with that data.
}
}
Now in class1 if you have interface object you can call class2 method
interfaceRef.listen(your_data);
Try with EventBus or BroadCastReceivers to pass data accordingly in local variables.
EventBus is a publish/subscribe event bus for Android and Java. EventBus... simplifies the communication between components. decouples event senders and receivers. performs well with Activities, Fragments, and background threads.
http://greenrobot.org/eventbus
First Register to EventBus in your Activity
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
Now pass the data from anywhere ,whether it is activity/fragment/background service etc etc etc like :
EventBus.getDefault().postSticky(new MessageEvent("your data here");
Now in your activity receive this message like :
#Subscribe(sticky = true,threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Log.e("TAG","Event Received");
Log.e("TAG",event.getData);
}

Avoiding memory leak while calling retrofit 2

By following this article I found that calling Retrofit enqueue() on onCreate() method may cause a memory leak.
Here is what the article says, doing this:
Calling Retrofit in the main thread
public class MoviesActivity extends Activity {
private TextView mNoOfMoviesThisWeek;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_movies_activity);
mNoOfMoviesThisWeek = (TextView) findViewById(R.id.no_of_movies_text_view);
MoviesRepository repository = ((MoviesApp) getApplication()).getRepository();
repository.getMoviesThisWeek()
.enqueue(new Callback<List<Movie>>() {
#Override
public void onResponse(Call<List<Movie>> call,
Response<List<Movie>> response) {
int numberOfMovies = response.body().size();
mNoOfMoviesThisWeek.setText("No of movies this week: " + String.valueOf(numberOfMovies));
}
#Override
public void onFailure(Call<List<Movie>> call, Throwable t) {
// Oops.
}
});
}
}
Now if this network call runs on a very slow connection and before the call ends, the Activity is rotated or destroyed somehow, then the entire Activity instance will be leaked.
I tried to do the same thing on my app. I called a big content (240 objects) usign enqueue() in onCreate() method. Then while the content was loading I rotated the device multiple times and LeakCanary showed me a memory leak in the Activity as the article said.
Then I tried two approachs to avoid the memory leak:
First option
Calling retrofit execute() method on a background thread using static inner class.
Calling Retrofit in a background thread
private static class RetrofitCall extends AsyncTask<Void, Void, List<Show>> {
private WeakReference<TextView> numberOfShows;
public RetrofitCall(TextView numberOfShows) {
this.numberOfShows = new WeakReference<>(numberOfShows);
}
#Override
protected List<Show> doInBackground(Void... voids) {
List<Show> showList = new ArrayList<>();
if (!isCancelled()) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(TvMazeService.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
TvMazeService service = retrofit.create(TvMazeService.class);
try {
Response<List<Show>> response = service.getShows().execute();
if (response.isSuccessful()) {
showList = response.body();
}
return showList;
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
#Override
protected void onPostExecute(List<Show> shows) {
super.onPostExecute(shows);
TextView textView = numberOfShows.get();
if (textView != null) {
String number = String.valueOf(shows.size());
textView.setText(number);
}
}
}
Then I tried to get the memory leak using LeakCanary again and it happened that the memory leak was gone.
Second option
Using ViewModel.
As you can see in the documentation, while using ViewModel I called retrofit asynchronous in the ViewModel class and when the screen is rotated (activity is destroyed) it does not need to load the data again as it remains saved.
This approach also did not give the a memory leak and was the best while talking about memory.
Questions
1) Then, using ViewModel to call Retrofit is the best option and it really avoid memory leak?
2) Is there any problem to call retrofit using enqueue() in onCreate() as MoviesActivity does?
3) In this approaches, which one is the best to make a call to authenticate a user?
1) Using ViewModel in the correct way does not cause memory leaks and is a good option. You can see the google's video explanation, and also this lecture talking about the difference between MVP and MVVM. This second lecture gives a really good explanation about the topic.
2) Calling retrofit enqueue() in onCreate() is a problem and it causes a memory leak. The problem is that the first time you start your activity it calls retrofit, then when you rotate your device, all the activity is destroyed and recreated again. If you rotate the device before the data is loaded completed, retrofit will be called for the second time when onCreate() is called again, and if you keep doing it 10 times, retrofit will be called 10 times, and then you stop rotating the device. The result from the calls will start to come, bzzz :( the result will be displayed 10 times because you called it 10 times. This implies in a huge memory leak. If you implement this approach and use LeakCanary you will see the leak.
3) What is the best approach?
Using enqueue() method in onCreate() is definitely not good.
Static inner classes (using AsyncTask) is good, but it does not survive to configuration changes because you need to cancel it in onDestroy(). This is why it does not cause a memory leak because the Task is canceled in onDestroy().
MVP is a really good approach for making retrofit calls. You can learn more in this medium article and the source code is here.
Read about the differences between MVP and MVVM as in this article.
Finally, Google is advising devs to use ViewModel in these scenarios.
You can follow my discussion in another question. Where we are talking about the same subject but while sign in a user to the server.
The reason why you will got memory leaks if calling enqueue() in onCreate() is that the enqueued calls will hold a reference to your activity instance, because the callback instance(anonymous class) passed to it is holding a reference to the enclosing class instance. As long as you cancel it before onDestroy(), there won't be a problem.

What process is the best way to send on API and save SQLiteDatabase?

I'm new on Android and working an big app which has sending data to API and saving it on SQlite. All of this process is on one class file . But it leaves me on an error. Sometimes the device hanged. other scenario is the data is incomplete . I have read about Intent Service and Services and I want to learn about the two, but I'm wondering how to get all of my data from UI and put it on services. May I know How?
It depends on the nature of the application. If this should happen in response to a user input...you could well use an AsyncTask. Otherwise, a background service could also do the job.
What you should NEVER do is run a network operation and/or database access on the main UI thread.
Services can receive data via intents. The way to send these intents depend on the type of service (Started, Bound or both). There are plenty of resources out there you can read...here's one from Android documentation...
https://developer.android.com/guide/components/services
An Example of an AsyncTask
The example below shows an implementation of AsyncTask that fetches a user's details from a network resource...
public class FetchUserTask extends AsyncTask<String,Void, UserDTO> {
private FetchUserTaskListener listener;
#Override
protected UserDTO doInBackground(String...params){
if(params == null || params.length == 0)
return null;
String userID = params[0];
UserDataProvider provider = new UserDataProvider(userID);
try {
return provider.get(userID);
}
catch(Exception ex){
//log the error
return null;
}
}
#Override
protected void onPostExecute(UserDTO user){
if(listener != null)
listener.onCompleted(user);
}
public void setListener(FetchUserTaskListener listener){
this.listener = listener;
}
public interface FetchUserTaskListener{
void onCompleted(boolean success);
}
}
How'd you use this AsyncTask?
For example, in an Activity, you would use it as below...
public class UserDetailsActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
//instantiate activity...
super.onCreate(savedInstanceState);
setContentView(R.layout.whatever_layout);
fetchUser(userId);
}
private void fetchUser(String userID){
FetchUserTask task = new FetchUserTask();
task.setListener(new FetchUserTaskListener<UserDTO>() {
#Override
public void onCompleted(UserDTO user) {
//CAUTION: make sure the activity hasn't been stopped before
//accessing any UI elements and/or context
}
}
task.execute(userID);
}
}
Note
You can (and will need to) make the example(s) above a bit more sophisticated. For example you can have the FetchUserTaskListener's onCompleted method return also an error message if an error occurred.
You will also need to check whether the activity has been paused or stopped before you access any context-bound data otherwise you might get an ILlegalStateException.
Make use of SQLiteOpenHelper class and it has methods to be overridden in your own class by extending SQLiteOpenHelper. Create Add, Update, Delete, Get methods as per your requirement in this class and keep this class as Singleton pattern. User Asynctasks to call those methids and you are done.
Hope that helps you visualise things in better way.

Should I worry about memory leaks and using WeakReference with Volley in Android

After reading this article, I started thinking about memory leaks with Volley.
Usually, the listeners implemented with Volley have either an implicit or explicit reference to the outer class (the activity). for example:
JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.GET,
url, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
updateLayout();
}
}
in this case there is an implicit reference... or I may want to create a custom JsonObjectRequest to internalize the response handling, and need to pass in a reference to the calling activity in its constructor.
Now lets say I start a web request, but before the response comes back, I navigate away from the activity that started the request. From what I understand the JsonObjectRequest object would keep a reference to my activity and prevent it from being Garbage collected.
-Am I understanding this correctly, is this a legitimate fear?
-Does the Volley library automatically deal with this?
-If am creating a custom JsonObjectRequest and passing in a "this" (reference to activity), do I need to create a WeakReference to the activity?
Based on looking at the volley code, calling cancel doesn't really avoid the memory leak because the reference never gets cleared and the reference isn't weak. Calling cancel only avoids Volley from delivering the response to the listener.
My solution to the problem would have to be cloning and modifying the library myself.
One of the solutions can be to make base ErrorListener reference inside of base Request.java to be weak reference. And similarly the same can be done to the Listener inside of JsonRequest.java.
The other solution can be to manually clear the reference upon cancel being called. inside of cancel(), set the mErrorListener and mListener to null. With this solution though, you'll have to remove the final modifier from the field declaration otherwise you wouldn't be allowed to set the reference to null.
Hope this helps.
I hava faced memory leak when using volley just like the way you write here.everytime I new a new listener.
I used leakcanary to detect leaking.
When I read about your article, http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html ,and used WeakReference to activity and callback interface(myself customed), it solved.
I used volley as a singleton.
public interface VolleyCallback {
void onSuccess(JSONObject result);
void onFailed(String error);
}
private static class SListener implements Response.Listener<JSONObject> {
private final WeakReference<Activity> activityWeakReference;
private final WeakReference<VolleyCallback> callbackWeakReference;
public SListener(Activity activity, VolleyCallback callback) {
activityWeakReference = new WeakReference<Activity>(activity);
callbackWeakReference = new WeakReference<VolleyCallback>(callback);
}
#Override
public void onResponse(JSONObject jsonObject) {
Activity act = activityWeakReference.get();
VolleyCallback vc = callbackWeakReference.get();
if (act != null && vc != null) {
LogUtil.d(TAG, act.toString() + " " + jsonObject.toString());
something you need to do;
vc.onSuccess(jsonObject);
}
}
I also read this answer,How to use WeakReference in Java and Android development? ,the second answer give an example just like your article provide.It's good.
I am probably late to this party by a year but I just figured a way to solve this issue. Here it is:
public interface VolleyCallback {
void onSuccess(JSONObject result);
void onFailed(String error);
}
private static class SListener implements VolleyCallback {
private final WeakReference<MainActivity> activityWeakReference;
public SListener(MainActivity activity, VolleyCallback callback) {
activityWeakReference = new WeakReference<>(activity);
}
#Override
void onSuccess(JSONObject result){
}
#Override
void onFailed(String error){
}
}
here you can use activityWeakReference.get() to access all the UI elements of the MainActivity too.
Found this from http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html. This way we don't need to cancel any requests in onStop(). Remember to use activityWeakReference.get().isFinishing && activityWeakReference.get()!=null to avoid crashes when the activity does not exist.

Best practice to implement Retrofit callback to recreated activity?

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();
}
}
}
}

Categories

Resources