Update UI thread after network request in Volley Android library
Asked Answered
S

2

11

I decided to give Volley a try, so presently I have a lot of REST callouts to be done, so I usually create a RequestHandler and a ResponseHandler class which as their names suggest handle requests and responses respectively. I follow this pattern so that I don't write redundant code. I just pass in the dynamic query/url as parameter and using a switch case handle the response to each of the requests. But I am stuck with the below problem :

I have no way of updating my UI thread from where I call the RequestHandler class. What I have tried or already know so far :

  1. Make the UI elements(Textview, Listview) static and update them after the response comes.
  2. Pass in a context parameter and update the UI after the response is received.
  3. Write the request and response as inner classes within the Activity
  4. Get rid of Volley.

I was wondering, how you guys do it? Is there any pattern better than the Request/Response Handler pattern I follow? Any way of updating the UI thread following the same pattern?

Thanks in advance !

Subtropics answered 26/12, 2013 at 15:19 Comment(0)
S
3

I use volley, and this is what I do. The code goes anywhere in your activity.

import com.android.volley.Response.Listener;
import static com.android.volley.Response.ErrorListener;

Listener<YOURDATACLASS> successListener = new Listener<YOURDATACLASS>() {
    @Override
    public void onResponse(YOURDATACLASS data) {
        // Check to make sure that the activity hasn't been destroyed while the call was in flight.
        if (! isFinishing()) {
            //DO YOUR UI UPDATE, such as 
            TextView textview = (TextView) findViewById(R.id.yourtextview);
            textview.setText("blah blah blah");
        }
    }
};
ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            //DO SOMETHING ON FAILURE
        }

YOURAPICALL(successListener, failurelistener);
Stapler answered 26/12, 2013 at 16:4 Comment(11)
This will crash, if you will close an activity before network call is done.Nibbs
Yes, as with all callbacks in Android you have to add checks to make sure that your view is alive. I'll add an example to the answer.Stapler
Actually, just checking it will only partially fix the problem. You will still have a race condition between your thread and UI threadNibbs
Please explain further, I don't understand from your answer below how a race condition exists that will cause a crash. The isFinishing() block will get executed on the main thread, so the activity will have to be deallocated either before or after this block on the main thread. The if statement checks for that condition. Also, have you seen such a crash in practice?Stapler
Actually, you are right. I was under wrong impression that callback is called on a separate thread. How does system behave in such case, if activity was closed long time ago?Nibbs
In my experience, I had crashes from callbacks trying to update the UI after the activity was destroyed, as you originally pointed out. This if statement fixed them. However, our code is littered with these ugly checks in the volley callbacks, which is not ideal. I don't have a solution to that.Stapler
you should send out your calls in a service and communicate back with your activity if it still is bind to the serviceIndigenous
If you, at any point, get to crashes caused by callbacks from Volley, I have a solution: Implement @Override onDestroy() that calls RequestQue.cancelAll() (do not forget to call super.onDestroy(). Using that you will prevent accessing Views that do not exist.Larger
@Larger What if some requests have been received, but some of them are still to be received. Can we call somethings like RequestQueue.cancelPendingRequests() ?Bueno
@akshayrajkore This post answers your question, I believe: https://mcmap.net/q/692992/-android-cancel-volley-requestLarger
@Larger Thanks for sharing. I want to confirm: requests that have been started, cannot be cancelled. To handle the scenario when request has begun, and then activity is paused, we check if getActivity == null ?Bueno
D
0

This works for me.

Map<String, String> params = new HashMap<>();
    params.put("dep", DEP);

    CustomPostRequest request = new CustomPostRequest(Request.Method.POST, Uris.URL_GET_DEP_CAT, params,
            new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject jsonObject) {

                    List<PCatValues> valores = parsear_y_devolver_valores(jsonObject);

                    gestionar_entregas(valores);

                    //aqui quitar los dialogos

                }
            }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError volleyError) {

        }
    });

    ApiController.getInstance().addToRequestQueue(request);

ApiController is my Aplication singleton class.

public class ApiController extends Application {

public static final String TAG = ApiController.class.getSimpleName();

private RequestQueue mRequestQueue;
private ImageLoader mImageLoader;

private static ApiController mInstance;

@Override
public void onCreate() {
    super.onCreate();
    mInstance = this;
    /*FacebookSdk.sdkInitialize(getApplicationContext());
    try {
        PackageInfo info = getPackageManager().getPackageInfo(
                "com.example.android.facebookloginsample",  // replace with your unique package name
                PackageManager.GET_SIGNATURES);
        for (Signature signature : info.signatures) {
            MessageDigest md = MessageDigest.getInstance("SHA");
            md.update(signature.toByteArray());
            Log.e("KeyHash:", Base64.encodeToString(md.digest(), Base64.DEFAULT));
        }
    } catch (PackageManager.NameNotFoundException e) {

    } catch (NoSuchAlgorithmException e) {

    }*/

}

public static synchronized ApiController getInstance() {
    return mInstance;
}

public RequestQueue getRequestQueue() {
    if (mRequestQueue == null) {
        mRequestQueue = Volley.newRequestQueue(getApplicationContext());
    }
    return mRequestQueue;
}

public <T> void addToRequestQueue(Request<T> req, String tag) {
    req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
    getRequestQueue().add(req);
}

public <T> void addToRequestQueue(Request<T> req) {
    req.setTag(TAG);
    getRequestQueue().add(req);
}

public void cancelPendingRequests(Object tag) {

    if (mRequestQueue != null) {
        mRequestQueue.cancelAll(tag);
    }

}

}

Darra answered 22/7, 2017 at 20:55 Comment(1)
did you add ApiController into Manifest.xml?Trinee

© 2022 - 2024 — McMap. All rights reserved.