HttpResponse.getEntity() NetworkOnMainThreadException [duplicate] - android

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
NetworkOnMainThreadException
For a long time, I've been using generic code that does http requests in an AsyncTask. The AsyncTask returns an HttpResponse object. Everything worked great and the GUI thread never froze or anything.
Now, suddenly, this creates a NetworkOnMainThreadException:
serverResponse.getEntity().getContent();
What the heck?? Why is getEntity() considered networking?? In my mind, that line merely converts a response to an inputstream and should not need a network connection. Who made this decision? WHY did they decide this should be networking?
The async task:
public class AsyncHttpTask extends AsyncTask<HttpRequestInfo, Integer, HttpRequestInfo> {
public AsyncHttpTask() {
super();
}
protected HttpRequestInfo doInBackground(HttpRequestInfo... params) {
HttpRequestInfo rinfo = params[0];
try{
HttpClient client = new DefaultHttpClient();
HttpResponse resp = client.execute(rinfo.getRequest());
rinfo.setResponse(resp);
}
catch (Exception e) {
rinfo.setException(e);
}
return rinfo;
}
#Override
protected void onPostExecute(HttpRequestInfo rinfo) {
super.onPostExecute(rinfo);
rinfo.requestFinished();
}
Callback interface:
public interface HttpCallback {
public void onResponse(HttpResponse serverResponse);
public void onError(Exception e);
}
HttpRequestInfo:
public class HttpRequestInfo {
private HttpUriRequest request_;
private HttpCallback callback_;
private Exception exception_;
private HttpResponse response_;
public HttpRequestInfo(HttpUriRequest request, HttpCallback callback) {
super();
request_ = request;
callback_ = callback;
}
public HttpUriRequest getRequest() {
return request_;
}
public void setRequest(HttpUriRequest request) {
request_ = request;
}
public HttpCallback getCallback() {
return callback_;
}
public void setCallback(HttpCallback callback) {
callback_ = callback;
}
public Exception getException() {
return exception_;
}
public void setException(Exception exception) {
exception_ = exception;
}
public HttpResponse getResponse() {
return response_;
}
public void setResponse(HttpResponse response) {
response_ = response;
}
public void requestFinished(){
if(exception_ != null){
callback_.onError(exception_);
}
else {
callback_.onResponse(response_);
}
}
}
Then I use jackson to convert the json response to an object. That's this is where the exception occurs:
#Override
public <T> T handleResponse(HttpResponse serverResponse, Class<T> typeOfResponse) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
T responseObject = null;
try {
responseObject = mapper.readValue(serverResponse.getEntity().getContent(),typeOfResponse); //THIS LINE IS EVIL
} catch (JsonParseException e) {
throw new ARException("Couldn't handle the response because the http response contained malformed json.",e);
} catch (JsonMappingException e) {
throw new ARException("Mapping the json response to the response object " + typeOfResponse + " failed.",e);
} catch (IllegalStateException e) {
throw new ARException("Couldn't convert the http response to an inputstream because of illegal state.",e);
} catch (IOException e) {
throw new ARException("Couldn't convert the http response to an inputstream.",e);
}
return responseObject;
}

Because you must work with network in separate thread and not main. You can use AsyncTask or Thread + Handler. If you are using AsyncTask all work with network you must perform in doInBackground part.

Related

Fail to connect to the webservice by Using Okhttp

I'm building an app that calls to a webservice. When i use HttpUrlConnection class to make a request to the webservice then it works, but i replace with OkHttpClient to make request then my app can't connect to the service, OnFailure method is always called with content: "OnFailure: failed to connect to todolistmobileapp-env.ap-south-1.elasticbeanstalk.com/13.232.26.212 (port 80) after 4ms"
Anyone tell me what problem i'm getting into?
public class RequestRegisterAuthor extends AppNetworkRequest {
public static final String TAG = RequestRegisterAuthor.class.getSimpleName();
private final String url = ToDoRestAPIs.baseRemoteHostUrl + ToDoRestAPIs.registerAuthor;
public RequestRegisterAuthor(APICallbackListener apiCallbackListener, Object jsonRequestBody){
super(apiCallbackListener);
request = new Request.Builder().url(url)
.addHeader(AppNetworkRequest.CONTENT_TYPE, AppNetworkRequest.JSON_CONTENT_TYPE)
.post(RequestBody.create(MediaType.parse(JSON_CONTENT_TYPE), jsonRequestBody.toString()))
.build();
}
#Override
public void makeBackEndRequest() {
new Thread(this).start();
}
#Override
public void run() {
okHttpClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call,final IOException e) {
handler.post(new Runnable() {
#Override
public void run() {
apiCallbackListener.onFailureCallback(e.getMessage());
Log.e(TAG + "- OnFailure", e.getMessage());
}
});
}
#Override
public void onResponse(Call call, Response response) throws IOException {
try {
if(response.code() == 201){
responseObject = new GsonBuilder().create().fromJson(response.body().string(), Author.class);
}
else{
responseObject = new Error(response.code(), response.message());
}
} catch (JsonSyntaxException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
responseObject = new Error(101, e.getMessage()); // pre-defined code.
}
handler.post(new Runnable() {
#Override
public void run() {
apiCallbackListener.onSuccessCallback(REQUEST_TYPE.REQUEST_REGISTER_AUTHOR, responseObject);
}
});
}
});
} }
Here is the logcat:
https://pastebin.com/3CNsGrPm
I believe HttpUrlConnection uses OkHttp under the hood, could you post your code for both methods of connecting?

Rxjava - UndeliverableException

I'm using reactive for android for my request to backend, the problem is that the request sometimes work and sometimes not.
and i get the exception
io.reactivex.exceptions.UndeliverableException: android.os.NetworkOnMainThreadException
in Mainactivity onCreate() i'm calling GetServices function which sends a post request
Get services
public Observable<Response> GetService() {
return Observable.fromCallable(new Callable<Response>() {
#Override
public Response call() throws Exception {
return RequestHelper.GetRequest("getvendoractiveservices");
}
});
}
public void GetServices()
{
GetService().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<Response>() {
#Override
public void onSubscribe(#io.reactivex.annotations.NonNull Disposable d) {
}
#Override
public void onNext(#io.reactivex.annotations.NonNull Response value) {
try {
Log.w("services",value.body().string()); //exception here
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onError(#io.reactivex.annotations.NonNull Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() {
}
});
}
my postrequest
public static String URI="http://192.168.137.1:4000/";
public static Response PostRequest(String api, FormBody formBody)
{
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(URI+api)
.header("carmen-token", LoginActivity.token)
.post(formBody)
.build();
try {
return client.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

OkHttp and Retrofit, refresh token with concurrent requests

In my application I implemented Retrofit to call WebServices and I'm using OkHttp to use Interceptor and Authenticator. Some requests need token, and I have implemented Authenticator interface to handle the refresh (following the official documentation). But I have the following issue : time to time in my app I have to call more than one request at once. Because of that, for one of them I will have the 401 error.
Here is my code for request calls :
public static <S> S createServiceAuthentication(Class<S> serviceClass, boolean hasPagination) {
final String jwt = JWT.getJWTValue(); //Get jwt value from Realm
if (hasPagination) {
Gson gson = new GsonBuilder().
registerTypeAdapter(Pagination.class, new PaginationTypeAdapter()).create();
builder =
new Retrofit.Builder()
.baseUrl(APIConstant.API_URL)
.addConverterFactory(GsonConverterFactory.create(gson));
}
OkHttpClient.Builder httpClient =
new OkHttpClient.Builder();
httpClient.addInterceptor(new AuthenticationInterceptor(jwt));
httpClient.authenticator(new Authenticator() {
#Override
public Request authenticate(Route route, Response response) throws IOException {
if (responseCount(response) >= 2) {
// If both the original call and the call with refreshed token failed,
// it will probably keep failing, so don't try again.
return null;
}
if (jwt.equals(response.request().header("Authorization"))) {
return null; // If we already failed with these credentials, don't retry.
}
APIRest apiRest = createService(APIRest.class, false);
Call<JWTResponse> call = apiRest.refreshToken(new JWTBody(jwt));
try {
retrofit2.Response<JWTResponse> refreshTokenResponse = call.execute();
if (refreshTokenResponse.isSuccessful()) {
JWT.storeJwt(refreshTokenResponse.body().getJwt());
return response.request().newBuilder()
.header(CONTENT_TYPE, APPLICATION_JSON)
.header(ACCEPT, APPLICATION)
.header(AUTHORIZATION, "Bearer " + refreshTokenResponse.body().getJwt())
.build();
} else {
return null;
}
} catch (IOException e) {
return null;
}
}
});
builder.client(httpClient.build());
retrofit = builder.build();
return retrofit.create(serviceClass);
}
private static int responseCount(Response response) {
int result = 1;
while ((response = response.priorResponse()) != null) {
result++;
}
return result;
}
The issue is simple, the first request will refresh the token successfully but others will failed because they will try to refresh a token already refreshed. The WebService return an error 500. Is there any elegant solution to avoid this ?
Thank you !
If I understand your issue, some requests are sent while the token is being updated, this gives you an error.
You could try to prevent all the requests while a token is being updated (with a 'synchronized' object) but this will not cover the case of an already sent request.
Since the issue is difficult to avoid completely, maybe the right approach here is to have a good fallback behavior. Handling the error you get when you've made a request during a token update by re-running the request with the updated token for instance.
Write Service.
public class TokenService extends Service {
private static final String TAG = "HelloService";
private boolean isRunning = false;
OkHttpClient client;
JSONObject jsonObject;
public static String URL_LOGIN = "http://server.yoursite";
String phone_number, password;
#Override
public void onCreate() {
Log.i(TAG, "Service onCreate");
jsonObject = new JSONObject();
client = new OkHttpClient();
SharedPreferences pref_phone = getSharedPreferences("Full_Phone", MODE_PRIVATE);
phone_number = pref_phone.getString("Phone", "");
SharedPreferences pref_password = getSharedPreferences("User_Password", MODE_PRIVATE);
password = pref_password.getString("Password", "");
isRunning = true;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Service onStartCommand");
try {
jsonObject.put("phone_number", phone_number);
jsonObject.put("password", password);
} catch (JSONException e) {
e.printStackTrace();
}
new Thread(new Runnable() {
#Override
public void run() {
for (; ; ) {
try {
Thread.sleep(1000 * 60 * 2);
} catch (Exception e) {
}
if (isRunning) {
AsyncTaskRunner myTask = new AsyncTaskRunner();
myTask.execute();
} else {
Log.d("CHECK__", "Check internet connection");
}
}
}
}).start();
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent arg0) {
Log.i(TAG, "Service onBind");
return null;
}
#Override
public void onDestroy() {
isRunning = false;
Log.i(TAG, "Service onDestroy");
}
String post(String url, JSONObject login) {
try {
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, login.toString());
okhttp3.Request request = new okhttp3.Request.Builder()
.url(url)
.post(body)
.build();
okhttp3.Response response = client.newCall(request).execute();
try {
return response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
String response;
private class AsyncTaskRunner extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {
}
#Override
protected String doInBackground(String... params) {
try {
response = post(
URL_LOGIN, jsonObject);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String result) {
Log.d("---OKHTTP---", response);
}
}
}

okhttp3 how to return value from async GET call

I am implementing Helper class in Android studio to service Activity
public void getLastId()
{
//init OkHttpClient
OkHttpClient client = new OkHttpClient();
//backend url
Request request = new Request.Builder()
.url("http://192.168.1.102:8080/aquabackend/public/customers/lastid")
.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 {
String jsonData = response.body().string();
try {
JSONObject jobject = new JSONObject(jsonData);
String id = jobject.getString("id");
//increment current id +1
String last_id = String.valueOf(Integer.parseInt(id)+1);
Log.i("new id", last_id);
} catch (Exception e) {
e.printStackTrace();
}
//Log.i("ok", response.body().string());
}
});
My function call in activity class
Helper helper = new Helper();
helper.getLastId();
//I want to get method to return lastId and then manipulate with the data
How can I make method return value of the id?
As it is an asynchronous process you won't be able to return a value from the method itself. However, you can use a callback to provide you the value when the asynchronous process has been completed. Below is an example of how you might want to do this.
public interface GetLastIdCallback {
void lastId(String id);
}
You would modify getLastId as follows:
public void getLastId(GetLastIdCallback idCallback) {
...
String last_id = String.valueOf(Integer.parseInt(id)+1);
idCallback.lastId(last_id);
...
}
Your Helper class usage would now look like this:
Helper helper = new Helper();
helper.getLastId(new GetLastIdCallback() {
#Override
public void lastId(String id) {
// Do something with your id
}
});
I'd suggest making your callback a bit more generic than I have suggested above. It could look like this:
public interface GenericCallback<T> {
void onValue(T value);
}
...
Helper helper = new Helper();
helper.getLastId(new GenericCallback<String>() {
#Override
public void onValue(String value) {
// Do something
}
});
If you used an interface like above you would be able to work with any return type.
Create a interface.
public interface Result{
void getResult(String id);
}
Now, pass interface to method as parameter.
Helper helper = new Helper();
helper.getLastId(new Result(){
#Override
void getResult(String id){
}
});
And In your method :
public void getLastId(final Result result)
{
//init OkHttpClient
OkHttpClient client = new OkHttpClient();
//backend url
Request request = new Request.Builder()
.url("http://192.168.1.102:8080/aquabackend/public/customers/lastid")
.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 {
String jsonData = response.body().string();
try {
JSONObject jobject = new JSONObject(jsonData);
String id = jobject.getString("id");
//increment current id +1
String last_id = String.valueOf(Integer.parseInt(id)+1);
Log.i("new id", last_id);
result.getResult(last_id);
} catch (Exception e) {
e.printStackTrace();
}
//Log.i("ok", response.body().string());
}
});
You have to create interface for it.
public interface getResponse {
void getJsonResponse(final String id);
}
In Your code :
public void getLastId(fianl getResponse response)
{
//init OkHttpClient
OkHttpClient client = new OkHttpClient();
//backend url
Request request = new Request.Builder()
.url("http://192.168.1.102:8080/aquabackend/public/customers/lastid")
.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 {
String jsonData = response.body().string();
try {
JSONObject jobject = new JSONObject(jsonData);
String id = jobject.getString("id");
//increment current id +1
String last_id = String.valueOf(Integer.parseInt(id)+1);
Log.i("new id", last_id);
if(response!=null){
response.getJsonResponse(last_id)
}
} catch (Exception e) {
e.printStackTrace();
}
//Log.i("ok", response.body().string());
}
});
In Activity :
public class HomeActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Helper helper = new Helper();
helper.getLastId(new getResponse(){
#Override
void getJsonResponse(String id){
}
});
}}
}

OkHttp trigger callback in originated class after finishing network actions

Here is the scenario: I have an Activity, named MainActivity, calling a OkHttp wrapper class, named NetworkManager to perform network post in background:
// In MainActivity
NetworkManager manager = new NetworkManager();
try {
manager.post("http://www.example.com/api/", reqObj); // reqObj is a JSONObject
} catch(IOException ioe) {
Log.e(TAG, ioe.getMessage());
}
Then, in the NetworkManager, I perform the POST action in asynchronous mode:
public class NetworkManager {
static String TAG = "NetworkManager";
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
void post(String url, JSONObject json) throws IOException {
//RequestBody body = RequestBody.create(JSON, json);
try {
JSONArray array = json.getJSONArray("d");
RequestBody body = new FormEncodingBuilder()
.add("m", json.getString("m"))
.add("d", array.toString())
.build();
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
// Asynchronous Mode
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
Log.e(TAG, e.toString());
// what should I put here?
}
#Override
public void onResponse(Response response) throws IOException {
Log.w(TAG, response.body().string());
// what should I put here?
}
});
} catch (JSONException jsone) {
Log.e(TAG, jsone.getMessage());
}
}
}
What I'm trying to achieve is to call a function in MainActivity after network POST is successful or failed. How can I achieve this?
You can create an interface with onFailure and onResponse then let YourActivity implement it. And, on NetworkManagertry to notify YourActivity using listener.
// MainActivity implements NetworkListener
NetworkManager manager = new NetworkManager();
manager.setOnNetWorkListener(this);
try {
manager.post("http://www.example.com/api/", reqObj); // reqObj is a JSONObject
} catch(IOException ioe) {
Log.e(TAG, ioe.getMessage());
}
void onFailure(Request request, IOException e) {
// call your activity methods
}
void onResponse(Response response) {
// call your activity methods
}
// ======NetworkManager class============
public class NetworkManager {
static String TAG = "NetworkManager";
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
public NetworkListenerv listener;
public void setOnNetWorkListener(NetworkListener listener) {
this.listener = listener
}
// Asynchronous Mode
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
Log.e(TAG, e.toString());
// what should I put here?
if (listener != null) {
listener.onFailure(request, e);
}
}
#Override
public void onResponse(Response response) throws IOException {
Log.w(TAG, response.body().string());
// what should I put here?
if (listener != null) {
listener.onResponse(response);
}
}
});
// Your interface;
public interface NetworkListener {
void onFailure(Request request, IOException e);
void onResponse(Response response);
}

Categories

Resources