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, ...)
Related
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 for download a file.the problem is enqueue method not called. there is no error and no catch. nothing happen, where is my mistake?
this is my interface:
public interface ApiService {
#GET("uploads/{file_name}")
Call<ServerResponse> downloadFile(#Path("file_name") String fileName);
}
and this is my downloading code :
private void downloadFile() {
progressDialog.show();
// Map is used to multipart the file using okhttp3.RequestBody
File file = new File(mediaPath);
// Parsing any Media type file
RequestBody requestBody = RequestBody.create(MediaType.parse("*/*"), file);
ApiService getResponse = ApiClient.getClient().create(ApiService.class);
Call<ServerResponse> call = getResponse.downloadFile(file.getName());
call.enqueue(new Callback<ServerResponse>() {
#Override
public void onResponse(Call<ServerResponse> call, Response<ServerResponse> response) {
ServerResponse serverResponse = response.body();
if (serverResponse != null) {
if (serverResponse.getSuccess()) {
Toast.makeText(getApplicationContext(), serverResponse.getMessage(),Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(), serverResponse.getMessage(),Toast.LENGTH_SHORT).show();
}
} else {
assert serverResponse != null;
Log.v("Response", serverResponse.toString());
}
progressDialog.dismiss();
}
#Override
public void onFailure(Call<ServerResponse> call, Throwable t) {
}
});
}
Probably, you are referring that the method "enqueue()" is called, but it's not the onResponse one.
Please wait some time (a minute is fine) and make sure that onResponse nor onFailure are called.
If you want more info, you can use the HTTP logging interceptor
#POST("uploads/{file_name}")
Observable<Document> getDocumentsList(#Path("file_name") String fileName);
and the code webservice call should be
Observable<Document> listObservable = mApiService.getDocumentsList(path);
subscribe(listObservable, new Consumer<Document>() {
#Override
public void accept(Document resourceDtos) throws Exception {
fillDocs(resourceDtos.getData());
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
fillDocs(throwable.getMessage());
}
});
I want to get string JSON response to the main activity after making the connection I know it is running in different thread so please help me how I can get it and then return it from this method;
public class MakeNetworkConnection {
private String mBaseUrl;
private String mApiKey;
private String mContentType;
private String mJsonResponce;
public MakeNetworkConnection(String baseUrl, String apiKey, String contentType) {
mBaseUrl = baseUrl;
mApiKey = apiKey;
mContentType = contentType;
}
public String startNetworkConnection() throws IOException {
OkHttpClient client=new OkHttpClient();
Request request=new Request.Builder().url("http://content.guardianapis.com/sections?api-key=1123456").build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if(response.isSuccessful()){
mJsonResponce=response.body().string();
}
}
});
return mJsonResponce;
}
}
The problem here is about understanding how asynchronous tasks work. When you are calling client.newCall(request).enqueue(new Callback(), it will run in the background thread (off the main thread) and control will pass to the next line which is return mJsonResponce; And thus, it will always return null.
What you should do is to pass a callback method which will be called when the response is successful. You can create an interface to return the result:
public interface NetworkCallback {
void onSuccess(String repsonse);
void onFailure();
}
Pass an object of this interface while making the network request and call appropriate method when network request finishes.
One more thing you will have take care is that OkHttp doesn't return the response on the main thread so you will have to return the response on UI/main thread if you are going to update any UI. Something like this will work.
#Override
public void onResponse(Call call, Response response) throws IOException {
if(response.isSuccessful()){
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
//return response from here to update any UI
networkCallback.onSuccess(response.body().string());
}
});
}
}
Instead of using calls asynchronously, you could use it synchronously with execute().
public String startNetworkConnection() throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://content.guardianapis.com/sections?api-key=1123456")
.build();
return client.newCall(request)
.execute()
.body()
.string();
}
after advice from Rohit Arya i did the following:
public class OkHttpUtil {
public interface OKHttpNetwork{
void onSuccess(String body);
void onFailure();
}
public void startConnection(String url, final OKHttpNetwork okHttpCallBack) throws IOException {
OkHttpClient client=new OkHttpClient();
Request request=new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()){
okHttpCallBack.onSuccess(response.body().string());
}
}
});
}
}
in the MainActvity i did the following :
OkHttpUtil okHttpUtil=new OkHttpUtil();
try {
okHttpUtil.startConnection("http://content.guardianapis.com/sections?api-key=8161f1e9-248b-4bde-be68-637dd91e92dd"
, new OkHttpUtil.OKHttpNetwork() {
#Override
public void onSuccess(String body) {
final String jsonResponse=body;
runOnUiThread(new Runnable() {
#Override
public void run() {
//show the response body
Toast.makeText(MainActivity.this,jsonResponse, Toast.LENGTH_SHORT).show();
}
});
}
#Override
public void onFailure() {
//do something
}
});
} catch (IOException e) {
e.printStackTrace();
}
Change this line
mJsonResponce=response.body().toString();
To
mJsonResponce=response.body().string();
Hope help you.
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.
I have created a helper class to handle all of my http calls in my app. It is a simple singleton wrapper for okhttp that looks like this (I have omitted some unimportant parts):
public class HttpUtil {
private OkHttpClient client;
private Request.Builder builder;
...
public void get(String url, HttpCallback cb) {
call("GET", url, cb);
}
public void post(String url, HttpCallback cb) {
call("POST", url, cb);
}
private void call(String method, String url, final HttpCallback cb) {
Request request = builder.url(url).method(method, method.equals("GET") ? null : new RequestBody() {
// don't care much about request body
#Override
public MediaType contentType() {
return null;
}
#Override
public void writeTo(BufferedSink sink) throws IOException {
}
}).build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Request request, Throwable throwable) {
cb.onFailure(null, throwable);
}
#Override
public void onResponse(Response response) throws IOException {
if (!response.isSuccessful()) {
cb.onFailure(response, null);
return;
}
cb.onSuccess(response);
}
});
}
public interface HttpCallback {
/**
* called when the server response was not 2xx or when an exception was thrown in the process
* #param response - in case of server error (4xx, 5xx) this contains the server response
* in case of IO exception this is null
* #param throwable - contains the exception. in case of server error (4xx, 5xx) this is null
*/
public void onFailure(Response response, Throwable throwable);
/**
* contains the server response
* #param response
*/
public void onSuccess(Response response);
}
}
Then, in my main activity, I use this helper class :
HttpUtil.get(url, new HttpUtil.HttpCallback() {
#Override
public void onFailure(Response response, Throwable throwable) {
// handle failure
}
#Override
public void onSuccess(Response response) {
// <-------- Do some view manipulation here
}
});
onSuccess throws an exception when the code runs :
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the
original thread that created a view hierarchy can touch its views.
From my understanding, Okhttp callbacks run on the main thread so why do I get this error ?
** Just as a side note, I have created HttpCallback interface to wrap Okhttp's Callback class because I wanted to change the behaviour of onResponse and onFailure so I could unite the logic of handling failed responses due to i/o exception and failed responses due to server problems.
Thanks.
From my understanding, Okhttp callbacks run on the main thread so why do I get this error ?
This is not true. Callbacks run on a background thread. If you want to immediately process something in the UI you will need to post to the main thread.
Since you already have a wrapper around the callback you can do this internally in your helper so that all HttpCallback methods are invoked on the main thread for convenience.
As Jake Wharton suggested, I had to run the callbacks on the main thread explicitly.
So I wrapped the calls to the callbacks with Runnable like this:
private void call(String method, String url, final HttpCallback cb) {
...
client.newCall(request).enqueue(new Callback() {
Handler mainHandler = new Handler(context.getMainLooper());
#Override
public void onFailure(Request request,final Throwable throwable) {
mainHandler.post(new Runnable() {
#Override
public void run() {
cb.onFailure(null, throwable);
}
});
}
#Override
public void onResponse(final Response response) throws IOException {
mainHandler.post(new Runnable() {
#Override
public void run() {
if (!response.isSuccessful()) {
cb.onFailure(response, null);
return;
}
cb.onSuccess(response);
}
});
}
});
}
I know it's an old question, but recently I encountered the same issue. If you need to update any view, you will need to use runOnUiThread() or post the result back on the main thread.
HttpUtil.get(url, new Callback() { //okhttp3.Callback
#Override
public void onFailure(Call call, IOException e) { /* Handle error **/ }
#Override
public void onResponse(Call call, Response response) throws IOException {
String myResponse = response.body().string();
//Do something with response
//...
MyActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
//Handle UI here
findViewById(R.id.loading).setVisibility(View.GONE);
}
});
}
});
According to Retrofit documentation Callback methods are executed on UI thread by default until you provide a callback executor to Retrofit OR when using custom method return types using CallAdapterFactory
Michael's above response is a pretty good solution for having your callback happen in a calling Activity and I used it as a guide for my solution that I will outline below (solution in Kotlin);
Interface for HttpCallback
interface HttpCallback {
fun onResponse(response: Response)
fun onFailure(call: Call, e: IOException)
}
Implementation of the Interface. I pass the calling activity as a weak reference to perform UI updates.
class ImageUploadCallback(activity: WeakReference<Activity>) : HttpCallback {
val _activity = activity
override fun onResponse(response: Response) {
if(_activity.get() != null){
val activty = _activity.get()!!
activty.runOnUiThread(Runnable {
//Update the UI to your hearts content
});
}
}
override fun onFailure(call: Call, e: IOException) {
//Your implemtnation here
}
}
Calling HttpProxy class from the activity, passing the callback as a parameter
HttpProxy.UploadImage(
imageToUpload.name,
imageToUpload,
MEDIA_TYPE!!,
//our callback
ImageUploadCallback(WeakReference(this))
And finally, the code in the HttpProxy class that takes in the callback;
fun UploadImage(filename: String, sourceImageFile: File, mediaType: MediaType, callback: HttpCallback) {
//building up our request...and then calling
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
callback.onFailure(call, e)
}
override fun onResponse(call: Call, response: Response) {
response.use {
try{
callback.onResponse(response)
}
catch(e: Exception){
}
}
}
});
}
What i would do is extend okhttp callback and call failure / response on main thread so caller don't have to care about posting them on main thread.
Call call;
call.enqueue(new OkHttpCallbackOnMain() {
#Override
void onCallFailure(Call call, IOException e) {
// update UI
}
#Override
void onCallResponse(Call call, Response response) {
// update UI
}
});
abstract class OkHttpCallbackOnMain implements Callback {
abstract void onCallFailure(Call call, IOException e);
abstract void onCallResponse(Call call, Response response));
#Override
public void onFailure(#NonNull Call call, #NonNull IOException e) {
getMainHandler().post(() -> onCallFailure(call, e));
}
#Override
public void onResponse(#NonNull Call call, #NonNull Response response) {
getMainHandler().post(() -> onCallResponse(call, response));
}
private Handler getMainHandler() {
return new Handler(Looper.getMainLooper());
}
}