I want to add progressdialog into okhttp (asynchronous, not AsyncTask)
but i got this error:
Error: Can't create handler inside thread that has not called
Looper.prepare()
How to fix it in a proper way? I want to be sure that is the best way to do this.
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
Log.d("TAG_response", " brak neta lub polaczenia z serwerem ");
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
progress = ProgressDialog.show(SignUp.this, "dialog title",
"dialog message", true);
try {
Log.d("TAGx", response.body().string());
if (response.isSuccessful()) {
Headers responseHeaders = response.headers();
for (int i = 0, size = responseHeaders.size(); i < size; i++) {
//System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
Log.d("TAG2", responseHeaders.name(i));
Log.d("TAG3", responseHeaders.value(i));
}
Log.d("TAG", response.body().string());
progress.dismiss();
main_activity();
}
else{
progress.dismiss();
alertUserAboutError();
}
}
catch (IOException e){
Log.d("TAG", "error");
}
}
});
OkHttp runs the onResponse method on the same background thread which it does the http call on. Because you're doing an async call this means it will not be the Android main thread.
To run code on the main thread from onResponse you can use a Handler and Runnable:
client.newCall(request).enqueue(new Callback() {
Handler handler = new Handler(SignUp.this.getMainLooper());
#Override
public void onFailure(Call call, IOException e) {
//...
}
#Override
public void onResponse(Call call, Response response) throws IOException {
handler.post(new Runnable() {
#Override
public void run() {
// whatever you want to do on the main thread
}
});
}
Related
I have this issue with Volley library requests. I make calls to API and since they have to be updated very often I implemented timers. After some time it throws me error:"Throwing OutOfMemoryError “pthread_create (1040KB stack) failed: Try again. I used the method which was suggested in this post: Throwing OutOfMemoryError "pthread_create (1040KB stack) failed: Try again" when doing asynchronous posts using Volley , but for me it did not work, the data coming from API stopped updating. I thought it might be something with timers or it is just something I am doing wrong using Volley.
If you have any idea, feel welcome to share.
My printer class
public void setMenuInformation(String url, Context context, final VolleyCallback callback) {
Log.d("API", "Currently calling URL " + url);
if (eRequestQueue == null) {
eRequestQueue = Volley.newRequestQueue(context);
eRequestQueue.add(new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
String temp = response.substring(2, response.length() - 2);
Log.d("API", "Current menu" + response);
byte msgArray[];
try {
msgArray = temp.getBytes("ISO-8859-1");
currentMenu = msgArray[0];
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
menuInformation = response;
callback.onSuccess(menuInformation);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d("API", "Current menu Nope ");
callback.onFailure(error);
}
}));
}
}
Main Activity:
myPrinterDetails.setMenuInformation("http://" + nsdServiceInfo.getHost() + "/GetCurrentMenu",
MainActivity.this, new PrinterNew.VolleyCallback() {
#Override
public void onSuccess(String result) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.d("NSD", "Current menu response succeded and added to array " + nsdServiceInfo);
//services.add(myPrinterDetails);
mAdapter.notifyDataSetChanged();
}
});
Log.d("NSD", "CurrentMenu " + myPrinterDetails.getMenuInformation());
myTimer.schedule(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
myPrinterDetails.setMenuInformation("http://" + nsdServiceInfo.getHost() + "/GetCurrentMenu", MainActivity.this, new PrinterNew.VolleyCallback() {
#Override
public void onSuccess(String result) {
Log.d("NSD", "Current menu response added to timer " + nsdServiceInfo);
mAdapter.notifyDataSetChanged();
}
#Override
public void onFailure(Object response) {
Log.d("NSD", "Current menu timer failed");
}
});
}
});
}
}, 0, 2000);
}
#Override
public void onFailure(Object response) {
Log.d("NSD", "Current menu response failed");
}
});
}
};
}
I'm very new to android development. Trying to connect some site and get data from it. I have this function called only from onCreate in the main activity. Every time I turn virtual Android phone left or right I see new "run()" strings in EditText and requests in Wireshark. How to stop that properly?
Tried call.cancel() and mClient.dispatcher().cancelAll() inside OnResponse
protected void Load(String url) {
Request request = new Request.Builder()
.url(url)
.build();
mClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, final Response response) throws IOException {
if (response.isSuccessful()) {
mHandler.post(new Runnable() {
#Override
public void run() {
mEdit.setText(mEdit.getText() + "run()\n");
}
});
}
}
});
}
retrofit supports enqueue canceling, and it works great.
And i think if you will try to run this code - your client enqueues would be stoped
protected void Load(String url) {
Request request = new Request.Builder()
.url(url)
.build();
Call<Response> mCall = mClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
if (call.isCanceled()) {
Log.e(TAG, "request was cancelled");
}
else {
Log.e(TAG, "other larger issue, i.e. no network connection?");
}
}
#Override
public void onResponse(Call call, final Response response) throws IOException {
if (response.isSuccessful()) {
mHandler.post(new Runnable() {
#Override
public void run() {
mEdit.setText(mEdit.getText() + "run()\n");
}
});
}
}
});
mCall.cancel();
}
I don't know you project structure and what kind of patterns you using(MVP, MVVM or else), but in simple, this code can be improved by returning Call
protected void Load(String url): Call<Response>
And then you can hadle you request status, and if it longer than 5 seconds for example, you call call.cancel() and request is stopping.
onCreate is called every time configuration changes (for example you rotate your phone/emulator). For more info: https://developer.android.com/guide/components/activities/activity-lifecycle
You can save your response to prevent new request on every onCreate. Something like this:
MainActivity {
private Response savedResponse;
onCreate() {
if (savedResponse == null) {
Load(url)
}
}
}
and in your onResponse save the response:
#Override
public void onResponse(Call call, final Response response) throws IOException {
if (response.isSuccessful()) {
savedResponse = response; // <----
mHandler.post(new Runnable() {
#Override
public void run() {
mEdit.setText(mEdit.getText() + "run()\n");
}
});
}
}
However, correct way would be to separete network calls/requests from activity lifecycle and load data somewhere else (Service, WorkManager, ...)
I have web request helper class in my app using OKHttp3 via standard async method call. everything just work fine, but in my Splash Activity just for first run (after new installation) web request calling not work! but if I close the app and run again everything work fine.
here is my call back interface:
public interface WebResult<T> {
void onValue(T value);}
here is calling method
public void getStatus(final WebResult result) {
urlBuilder.addQueryParameter("action", "test");
urlBuilder.addQueryParameter("reqbody", cd.toJSON());
String url = urlBuilder.build().toString();
Request request = new Request.Builder()
.header("Authorization", AuthKey)
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
setHasError(true);
setMsg(e.getMessage());
}
#Override
public void onResponse(Call call, final Response response) throws IOException {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
} else {
iAct.runOnUiThread(new Runnable() {
public void run() {
try {
String s = response.body().string();
ServerStat r = new ServerStat();
r.fromJSON(s);
result.onValue(r);
return;
} catch (IOException e) {
}
}
});
}
}
});
}
and its my splash activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = this;
WebHelper wh = new WebHelper(context);
wh.getStatus(new WebResult() {
#Override
public void onValue(Object value) {
ServerStat r = (ServerStat) value;
if (r.getErrorCode() == 0) {
Toast.makeText(context, r.getErrorMsg(), Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, r.getErrorMsg(), Toast.LENGTH_LONG).show();
}
}
});
}
I replaced OKHttp3 with google Volley and it's work in my case!
I am using Retrofit 2.0 to make api calls with nesting multiple requests. All api's works fine individually.
But when i nested all retrofit, First request execute perfectly but after that when i register second request it's not callback in enqueue method (i.e. it's directly returning null without inserting enqueue's inner methods like onResponse, onFailure)
My Code :-
public class Main2Activity extends AppCompatActivity {
Gson gson;
JSONObject jsonResult=null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
gson=new GsonBuilder().create();
firstRequest(); //-- First retrofit request
}
private void firstRequest() {
Retrofit retrofit=new Retrofit.Builder().baseUrl(getResources().getString(R.string.Api_Url)).addConverterFactory(GsonConverterFactory.create(gson)).build();
CityRetailsApi service = retrofit.create(CityRetailsApi.class);
Call call_first= service.getMainCatFlag();
call_first.enqueue(new Callback() {
#Override
public void onResponse(Call call, Response response) {
Log.d("MainActivity", "Status Code = " + response.code());
if (response.isSuccessful()){
MainCatFlag result = (MainCatFlag) response.body(); //-- Get First request response
JSONObject json2nd = secondRequest(); //-- Second request
}
}
#Override
public void onFailure(Call call, Throwable t) {
Log.d("MainActivity", "Error");
}
});
}
private JSONObject secondRequest() {
try {
Retrofit retrofit=new Retrofit.Builder().baseUrl(getResources().getString(R.string.Api_Url)).addConverterFactory(GsonConverterFactory.create(gson)).build();
CityRetailsApi service = retrofit.create(CityRetailsApi.class);
Call call_second= service.getMainCat();
call_second.enqueue(new Callback() {
#Override
public void onResponse(Call call2, Response response1) {
Log.d("MainActivity", "Status Code = " + response1.code());
if (response1.isSuccessful()) {
MainCat result = (MainCat) response1.body();
if (result.getSuccess()==1)
{
try {
jsonResult= new JSONObject(new Gson().toJson(result));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
#Override
public void onFailure(Call call, Throwable t) {
Log.d("MainActivity", "Error");
}
});
}catch (Exception e){
Log.d("MainActivity", "Error= " + e);
}
return jsonResult;
}
}
In above code firstRequest() executed correctly and proving response but the secondRequest (inside firstRequest() enqueue method) not working fine. Not showing any error, success message in console. Can any one please help me to override this problem.
If any problem in my code, please let me know.
Thank you in advance.
You made a mistake that when you using retrofit enquene,it's called asynchronously, so you can't get the result outside of the callback method!
So, you should process your result inside the onResponse method like this:
private void secondRequest() {
try {
call_second.enqueue(new Callback() {
#Override
public void onResponse(Call call2, Response response1) {
Log.d("MainActivity", "Status Code = " + response1.code());
if (response1.isSuccessful()) {
MainCat result = (MainCat) response1.body();
if (result.getSuccess()==1)
{
try {
jsonResult= new JSONObject(new Gson().toJson(result));
// process your jsonResult here
...
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
#Override
public void onFailure(Call call, Throwable t) {
Log.d("MainActivity", "Error");
}
});
}catch (Exception e){
Log.d("MainActivity", "Error= " + e);
}
}
Previously I would simply pass the response.body().string() to OnPostExecute() method of an asyncTask to update TextViews of the UI. okHttp doesn't seem to have this option. Where is the equivalent or how to achieve this?
//Call to server to echo MySQL query
private void usersInZone() {
if (!isNetworkAvailable()) return;
rotate(true);
OkHttpClient client = new OkHttpClient();
RequestBody formBody = new FormBody.Builder()
.add("userId", String.valueOf(userId))
.add("channel", String.valueOf(subport + port))
.build();
Request request = new Request.Builder()
.url(SITE_URL + "inZone.php")
.post(formBody)
.build();
Call users = client.newCall(request);
users.enqueue(caller);
}
//okHttp Callback to parse echo response
caller = new Callback() {
#Override
public void onFailure(Call call, IOException e) {
if (RS != null) RS.makePoor(true);
}
#Override
public void onResponse(Call call, Response response) throws IOException {
LOG.l(String.valueOf(call));
if (response.isSuccessful()) {
try {
JSONObject data = new JSONObject(response.body().string());
updateFields(getString(R.string.inzone) + data.getInt("total") + "]", String.valueOf(data.getInt("zone")));
} catch (JSONException e) {
LOG.l("JSONException" + e);
}
} else if (RS != null) RS.makePoor(true);
}
};
//UI textViews i'm needing to update with the echoed results
private void updateFields(String total, String channelTotal){
subtitle.setText(total);
tvusercount.setText(channelTotal);
}
Where is the equivalent or how to achieve this?
OkHttp's onResponse() is not called on main thread. If you want to update UI in onResponse you must delegate this task to UI thread. Using Handler and posting Runnable is one way of doing so.
The callback is not called on the main thread. If you need to update the UI, a simple way to do that is to create a class that implemented Callback and post the items to the UI like so:
public abstract class UIMainCallback implements Callback {
private static final Handler mUIHandler = new Handler(Looper.getMainLooper());
abstract void failure(Request request, IOException e);
abstract void response(Response response);
#Override
public void onFailure(final Request request, final IOException e) {
mUIHandler.post(new Runnable() {
#Override
public void run() {
failure(request, e);
}
});
}
#Override
public void onResponse(final Response response) throws IOException {
mUIHandler.post(new Runnable() {
#Override
public void run() {
response(response);
}
});
}
}
Then your Caller can just implement the UIMainCallback instead of the interface.