Is it possible to display a message or make changes on the UI thread once Task callInBackground is finished executing?
Something like following:
Task.callInBackground(new Callable<String>() {
#Override
public String call() {
for(int i=0; i<3; i++){
Log.i("I=", String.valueOf(i));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String obj = "";
return null;
}
}).onSuccess(new Continuation<String, Object>() {
#Override
public Object then(Task<String> task) throws Exception {
Log.i("I=", "Counter complete");
Toast.makeText(MainLoanMemberActivity.this, "Finished", Toast.LENGTH_SHORT).show();
btnAgriLoan.setText("LOL");
return null;
}
});
At the moment there is no Toast message displayed and there is no crash as well.
Looking for an equivalent of AsyncTask's onPostExecute in Bolts Framework where one can add changes to UI.
Didn't realized that there are EXECUTOR's types that you can mentioned with each helper function like so: (Task.UI_THREAD_EXECUTOR)
Task.callInBackground(new Callable<String>() {
#Override
public String call() {
for(int i=0; i<3; i++){
Log.i("I=", String.valueOf(i));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String obj = "";
return null;
}
}).onSuccess(new Continuation<String, Void>() {
public Void then(Task<String> object) throws Exception {
Toast.makeText(MainLoanMemberActivity.this, "Finished", Toast.LENGTH_SHORT).show();
btnAgriLoan.setText("LOL");
return null;
}
}, Task.UI_THREAD_EXECUTOR);
Docs helped!
public void getTerms(boolean showDialog) {
service.getTermsFromServer().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new SingleSubscriber<String>() {
#Override
public void onSuccess(String value) {
try {
JSONObject jsonObject = new JSONObject(value);
JSONObject data = jsonObject.getJSONObject("data");
String content = data.getString("content");
String id = data.getString("id");
if (showDialog) {
***signUpView.showDialog(content, id)***;
} else {
agreeTerms(id);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onError(Throwable error) {
Log.e(getClass().getName(), "Error : " + new Gson().toJson(error.getStackTrace()));
ErrorCheck.processError(error, gson, signUpView);
}
});
}
Please help me in testing this code. I have attached the method which i want to test. Here I want to verify that showDialog method gets called
Attaching the Unit test code also
#Test
public void testGetTermsCalled(){
String terms= "{\"data\":{\"id\":\"67f07c7a482542\",\"content\":\"<h3>Part of the test</h3>\",\"timestamp\":1484768675815,\"timestampFormatted\":\"2017-01-18T19:44:35\"},\"metadata\":null,\"version\":{\"id\":\"v1\",\"versionStatus\":\"candidate\",\"message\":null}}";
TestSubscriber<String> testSubscriber = new TestSubscriber<>();
signUpService.getTermsFromServer().just(terms).subscribe(testSubscriber);
signUpPresenter.getTerms(true);
Mockito.verify(signUpView).showDialog("<h3>Part of the test</h3>","67f07c71-1707-4b7a-a168-d7d05a482542");
}
Thanks!!!
Use RxJavaPlugins.setInitIoSchedulerHandler and RxAndroidPlugins.registerSchedulersHook to specify your own TestScheduler, then use its advanceTimeBy method to make some time pass, then verify that the expected calls happened.
public void getTestDats(String unique_id) {
final String tag = "testList";
String url = Constants.BASE_URL + "test_module.php";
Map<String, String> params = new HashMap<String, String>();
params.put("user_id", SharedPreferenceUtil.getString(Constants.PrefKeys.PREF_USER_ID, "1"));
params.put("unique_id", unique_id);//1,2,3,4,5
DataRequest loginRequest = new DataRequest(Method.POST, url, params, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
switch (response.optInt("unique_id")) {
case 1:
//task 1
break;
case 2:
//task 2
break;
default:
//nothing
}
}
}, new ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
//I want to know which unique_id request is failed
}
});
loginRequest.setRetryPolicy(new DefaultRetryPolicy(20000, 0, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
AppController.getInstance().addToRequestQueue(loginRequest, tag);
}
I'm trying to identity which request is failed with having unique_id.
I'm calling getTestDats("1") function with unique_id. And function called 10 times and all the api call in addToRequestQueue.
When API go into Success part its working as per code.
But when API go into Error part I didn't identity the request.
Is there any way to know my request param so I can retry with particular unique_id request.
set a field in loginRequest and in onErrorResponse access the field like loginRequest.getUniqueId()
Alternatively, create a seperate class that implements Response.Listener and ErrorListener
Response Listener class:
public class MyReponseListener implements Response.Listener<JSONOBject>{
private long uniqId;
public MyResponseListener(long uniqId){
this.uniqId = uniqId;
}
#Override
public void onResponse(JSONObject response) {
System.out.println("response for uniqId " + uniqId);
// do your other chit chat
}
}
ErrorListener class:
public class MyErrorListener implements ErrorListener{
private long uniqId;
public MyErrorListener(long uniqId){
this.uniqId = uniqId;
}
#Override
public void onErrorResponse(VolleyError error) {
System.out.println("Error for uniqId : " + uniqId);
}
}
Now call it like:
DataRequest loginRequest = new DataRequest(Method.POST, url, params, new MyResponeListener(uniqId), new MyErrorListener(uniqId));
Now if you want some code of the calling class to be accessible in the ErrorListener class then do the following:
1. In calling class put the codes you want to access in methods
2. Create an interface with those method
3. The calling class will implement that interface
4. Pass the interface to constructor of the MyErrorListener or MyResponseListener
for example an activity calls the volley request, on error you want to show a message.
put that show error codes in a method:
public void showMessage(int errorCode){
//message according to code
}
now create an interface
public interface errorMessageInterface{
void showMessage(int errorCode);
}
the activity will implement errorMessageInterface and pass this to the constructor of MyErrorListener and save it in a field.
Inside onErrorResponse, you will call
field.showMessage()
You can parse error response in the same way as you parse success response. I use similar solution in my projects.
public class VolleyErrorParser {
private VolleyError mError;
private String mBody;
private int mUniqueId = -1;
public VolleyErrorParser(VolleyError e){
mError = e;
parseAnswer();
parseBody();
}
private void parseBody() {
if (mBody==null)
return;
try{
JSONObject response = new JSONObject(mBody);
mUniqueId = response.getOptInt("unique_id");
}catch (JSONException e){
e.printStackTrace();
}
}
private void parseAnswer() {
if (mError!=null&&mError.networkResponse!=null&&mError.networkResponse.data!=null){
mBody = new String(mError.networkResponse.data);
}
}
public String getBody(){
return mBody;
}
public int getUniqueId(){
return mUniqueId;
}
}
Use:
...
, new ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
int id = new VolleyErrorParse(error).getUniqueId();
switch (id) {
case -1:
//unique id not found in the answer
break;
case 1:
//task 1
break;
case 2:
//task 2
break;
default:
//nothing
}
}
}
...
Just add this code to identify which type of error you are facing.Add this in your onError() method :
if (error instanceof TimeoutError) {
Log.e(TAG, "TimeoutError");
} else if (error instanceof NoConnectionError) {
Log.e(TAG,"tNoConnectionError");
} else if (error instanceof AuthFailureError) {
Log.e(TAG,"AuthFailureError");
} else if (error instanceof ServerError) {
Log.e(TAG,"ServerError");
} else if (error instanceof NetworkError) {
Log.e(TAG,"NetworkError");
} else if (error instanceof ParseError) {
Log.e(TAG,"ParseError");
}
Log the unique_id before making a request i.e; after params.put("unique_id", unique_id);//1,2,3,4,5. And also once you get the response in onResponse() method. And cross verify what exactly is happening.
most of the solutions here will "work" but they are too complex .. for me :)
here is the simplest option with least code change I can think of:
...
final Map<String, String> params = new HashMap<String, String>();
params.put("user_id", SharedPreferenceUtil.getString(Constants.PrefKeys.PREF_USER_ID, "1"));
params.put("unique_id", unique_id);//1,2,3,4,5
DataRequest loginRequest = new DataRequest(Method.POST, url, params, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
switch (params.get("unique_id")) {
case 1:
//task 1
break;
case 2:
//task 2
break;
default:
//nothing
}
}
...
All the above answers seem to be correct.But i recommend you to do this in an optimized way. If you will add error handling code in all onErrorResponse() then it will create duplication. So create a seperate method in Utils or some other class and just call that method by passing error object to the method. Also you can inflate some dialog or toast to display an error message.
public static void handleError(final Context context, String alertTitle,
Exception exception, String logTag) {
if (context != null) {
if (exception instanceof TimeoutError)
message = context.getString(R.string.TimeoutError);
else if (exception instanceof NoConnectionError)
message = context.getString(R.string.NoConnectionError);
else if (exception instanceof AuthFailureError)
message = context.getString(R.string.AuthFailureError);
else if (exception instanceof ServerError)
message = context.getString(R.string.ServerError);
else if (exception instanceof NetworkError)
message = context.getString(R.string.NetworkError);
else if (exception instanceof ParseError)
message = context.getString(R.string.ParseError);
message = exception.getMessage();
DialogHelper.showCustomAlertDialog(context, null,
alertTitle, message, "ok",
new OnClickListener() {
#Override
public void onClick(DialogInterface dialog,
int which) {
}
}, null, null);
}
}
I think you have to make one conman method on Base class. As given bellow which I used in my code for calling php web api
/**
* <h1> Use for calling volley webService </h1>
*
* #param cContext Context of activity from where you call the webService
* #param mMethodType Should be POST or GET
* #param mMethodname Name of the method you want to call
* #param URL Url of your webService
* #param mMap Key Values pairs
* #param initialTimeoutMs Timeout of webService in milliseconds
* #param shouldCache Web Api response are stored in catch(true) or not(false)
* #param maxNumRetries maximum number in integer for retries to execute webService
* #param isCancelable set true if you set cancel progressDialog by user event
* #param aActivity pass your activity object
*/
public void callVolley(final Context cContext, String mMethodType, final String mMethodname, String URL,
final HashMap<String, String> mMap, int initialTimeoutMs, boolean shouldCache, int maxNumRetries,
Boolean isProgressDailogEnable, Boolean isCancelable, final Activity aActivity) {
mMap.put("version_key_android",BuildConfig.VERSION_NAME+"");
if (!isOnline(cContext)) {
//showErrorDailog(aActivity, Constant.PleaseCheckInternetConnection, R.drawable.icon);
} else {
StringRequest jsObjRequest;
int reqType = 0;
String RequestURL = URL.trim();
queue = Volley.newRequestQueue(cContext);
if (isProgressDailogEnable) {
customLoaderDialog = new CustomLoaderDialog(cContext);
customLoaderDialog.show(isCancelable);
customLoaderDialog.dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
// finish();
}
});
}
if (mMethodType.trim().equalsIgnoreCase("GET"))
reqType = com.android.volley.Request.Method.GET;
else if (mMethodType.trim().equalsIgnoreCase("POST"))
reqType = com.android.volley.Request.Method.POST;
if (RequestURL.equals(""))
RequestURL = Constant.BASE_URL;
else
RequestURL = URL;
if (Constant.d) Log.d("reqType", reqType + "");
jsObjRequest = new StringRequest(reqType, RequestURL, new com.android.volley.Response.Listener<String>() {
#Override
public void onResponse(String response) {
if (Constant.d) Log.d("response==>" + mMethodname, "" + response);
if (customLoaderDialog != null) {
try {
customLoaderDialog.hide();
} catch (Exception e) {
e.printStackTrace();
}
}
if (response == null || response.length() == 0) {
IVolleyRespose iVolleyRespose = (IVolleyRespose) aActivity;
iVolleyRespose.onVolleyResponse(404, response, mMethodname);
} else {
JSONObject json_str;
try {
json_str = new JSONObject(response);
int status = json_str.getInt("status");
if (status == 100) {
AlertDialog alertDialog = new AlertDialog.Builder(aActivity).create();
alertDialog.setTitle(getResources().getString(R.string.app_name));
alertDialog.setMessage(json_str.getString("message") + "");
alertDialog.setCancelable(false);
alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
try {
Intent viewIntent =
new Intent("android.intent.action.VIEW",
Uri.parse(Constant.playStoreUrl));
startActivity(viewIntent);
}catch(Exception e) {
Toast.makeText(getApplicationContext(),"Unable to Connect Try Again...",
Toast.LENGTH_LONG).show();
e.printStackTrace();
}
dialog.dismiss();
// return;
}
});
alertDialog.show();
} else {
IVolleyRespose iVolleyRespose = (IVolleyRespose) aActivity;
iVolleyRespose.onVolleyResponse(RESPONSE_OK, response, mMethodname);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}, new com.android.volley.Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError arg0) {
// TODO Auto-generated method stub
IVolleyRespose iVolleyError = (IVolleyRespose) aActivity;
iVolleyError.onVolleyError(404, "Error", mMethodname);
if (customLoaderDialog != null) {
customLoaderDialog.hide();
}
}
}) {
#Override
protected Map<String, String> getParams() {
String strRequest = "";
try {
strRequest = getWebservicejsObjRequestforvolley(mMethodname, mMap);
if (Constant.d) Log.d("Request==>", strRequest + "");
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Map<String, String> params = new HashMap<>();
params.put("json", strRequest);
return params;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<>();
params.put("Content-Type", "application/x-www-form-urlencoded");
return params;
}
};
//if(Constant.d) Log.d("Request==>", jsObjRequest+"");
jsObjRequest.setTag(mMethodname);
jsObjRequest.setShouldCache(shouldCache);
jsObjRequest.setRetryPolicy(new DefaultRetryPolicy(initialTimeoutMs, maxNumRetries, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
queue.add(jsObjRequest);
}
}
Please observe that here we make one interface for getting response and error.
Using Interface you can get method name on both response and error so you can identify which web api is successfully called and which give error. You should extend base class to Activity and also implement Interface which you made for getting volley response. Here in above code I show how to bind interface to activity. when you call api by passing activity context.
Let's say I have this Dashboard.java:
public class DashboardActivity extends ActionBarActivity {
private TextView login_response;
private static String TAG = DashboardActivity.class.getSimpleName();
final static String API_URL_ACCOUNT = "http://www.example.com/apiv2/account";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dashboard);
login_response = (TextView) findViewById(R.id.login_response);
Intent intent = getIntent();
if(intent.hasExtra("TOKEN"))
{
String token = intent.getStringExtra("TOKEN");
getShopName(token);
}
else
{
}
And this is the getShopName method:
private void getShopName(String token) {
JsonObjectRequest req = new JsonObjectRequest(API_URL_ACCOUNT + "?token=" + token, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
VolleyLog.v("Response:%n %s", response.toString(4));
JSONArray account = response.getJSONArray("account");
//Log.d(TAG, "Account: "+account.toString());
JSONObject shop = account.getJSONObject(0);
String name_shop = shop.getString("name_shop");
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.e("Error: ", error.getMessage());
}
});
// add the request object to the queue to be executed
VolleyController.getInstance().addToRequestQueue(req);
}
My goal is to have
if(intent.hasExtra("TOKEN"))
{
String token = intent.getStringExtra("TOKEN");
String shop_name = getShopName(token);
}
The "shop_name" in variable, to reuse in other part.
So, I know that void doesn't return nothing, but, I tried to edit like this answer, without success:
How can I return value from function onResponse of Volley?
Thank you
The issue is not returning a value from a JsonObjectRequest, but rather that you're trying to do an asynchronous operation in a synchronous way.
Here is a great explanation: Asynchronous vs synchronous execution, what does it really mean?
And to your specific question: I advise using an AsyncTask for your network operation.
I would like to use the information of 'result' in the XMLRPCMethod. When the thread is finished the correct data is in the result object.
This is a code snipped from my OpenerpRPC.java class.
class XMLRPCMethod extends Thread {
private String method;
private Object[] params;
private Handler handler;
public Object result;
private OpenerpRpc callBack;
public XMLRPCMethod(String method, OpenerpRpc callBack) {
this.method = method;
this.callBack = callBack;
handler = new Handler();
}
public void call() {
call(null);
}
public void call(Object[] params) {;
this.params = params;
start();
}
#Override
public void run() {
try {
result = client.callEx(method, params);
handler.post(new Runnable() {
public void run() {
try {
callBack.resultcall(result);
} catch (XMLRPCException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
} catch (final XMLRPCFault e) {
handler.post(new Runnable() {
public void run() {
Log.d("Test", "error", e);
}
});
} catch (final XMLRPCException e) {
handler.post(new Runnable() {
public void run() {
Throwable couse = e.getCause();
if (couse instanceof HttpHostConnectException) {
Log.d(TAG, "error"+uri.getHost());
} else {
Log.d("Test", "error", e);
}
Log.d("Test", "error", e);
}
});
}
}
}
My result call in the OpenerpRpc class looks like:
public void resultcall(Object result) throws XMLRPCException{
allres=result;
if (rtype.equals("login")){
//Isn't impossible cast the result var with (String) because cause crash..why?
userid=""+result;
}
if (rtype.equals("read")){
//Isn't impossible cast the result var with (String) because cause crash..why?
// userid=""+result;
}
// name of callback function to use in parent class (MainActivity) for receive data
this.parent.oerpcRec(rtype,allres);
}
This is how i can receive the data in mainactivity
#SuppressWarnings("unchecked")
public void oerpcRec(String rtype,Object res) throws XMLRPCException{
if (rtype=="login"){
connector.setModel("res.users");
Object[] Ids = {Integer.parseInt(connector.userid)};
// set here the fields you wont loads
Object[] values={"name"};
connector.Read(Ids,values);
}
if(rtype=="read"){
Object[] ret=(Object[])res;
Map<String, Object> map1 = (Map<String, Object>) ret[0];
if(ret.length > 1){
}
}
}
But how can i get this information in my mainactivity? I only get the information of the login id value. When I put a breakpoint in the thread it only goes to the function resultcall when I try to login.
...
public void onClick(View v) {
try {
//here set user and pass for login
connector.Login(USER,PASS);
Object[] ids = {31,30,28,26};
Object[] params ={"partner_id","tax_line","section_id","invoice_line"};
connector.Read(ids,params);
//get information of openERP for specific id's
} catch (XMLRPCException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Use an interface
public interface MyListener {
public void callback(Object result);
}
Your MainActivity must implement the interface
public class MyActivity extends Activity implements MyListener {
...
...
...
#override
public void callback(Object result) {
// getting the result value.
}
}
So when your thread finish, execute the callback() method:
MyListener ml;
ml.callback(result);
and the callback() method of you MainActivity will receive the object.