Android - NetworkOnMainThreadException with okhttp websocket - android

I have a threading problem with Android okhttp Websockets. I am doing a chat application, where I need to write and read from our server using websocket.
I read in other posts that with Okhttp, I should use StrictMode.ThreadPolicy.Builder().permitAll() as it has some issues etc. I added it.
But I keep getting this error when I try to sendMessage:
W/System.err: android.os.NetworkOnMainThreadException
09-13 17:13:32.198 22766-22766/com.microsoft.translator
Note that I am sending Message on a different thread, not on main thread.
Here is my code. Appreciate any help.
create websocket :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_chat);
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
// lot other init stuff
createWebSocket();
}
private void createWebSocket() {
okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.url(WSS_URL + token)
.build();
webSocketCall = WebSocketCall.create(okHttpClient, request);
webSocketCall.enqueue(this);
}
#Override
public void onOpen(WebSocket webSocket, Response response) {
try {
Log.d(TAG, "onOpen: " + response.message());
String message = createJsonMessage("Hello");
byte[] messageBytes = message.getBytes();
showToastOnUIThread("OnOpen sending message");
webSocket.sendMessage(WebSocket.PayloadType.TEXT, new Buffer().write(messageBytes));
this.webSocket = webSocket;
//startRepeatingTask();
} catch (IOException e) {
e.printStackTrace();
}
}
private void sendMessage(final String msgString) {
Runnable postMessageThread = new Runnable() {
#Override
public void run() {
String message = createJsonMessage(msgString);
byte[] messageBytes = message.getBytes();
try {
if (webSocket != null) {
try {
webSocket.sendMessage(WebSocket.PayloadType.TEXT, new Buffer().write(messageBytes));
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(ChatActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
} catch (Exception ex) {
ex.printStackTrace();
Toast.makeText(ChatActivity.this, ex.getMessage(), Toast.LENGTH_SHORT).show();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
postMessageThread.run();
}

I think this code is a simple example. If you don't use token to auth, you can remove addHeader.
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("your_name_input", "your_value")
.build();
Request request = new Request.Builder()
.url("your_url")
.post(requestBody)
.addHeader("name_your_token", "your_token")
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
final String yourResponse = response.body().string();
if(response.isSuccessful()){
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, "Ok: "+yourResponse,Toast.LENGTH_SHORT).show();
}
});
}else{
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, "Ok: "+yourResponse,Toast.LENGTH_SHORT).show();
}
});
}
}
});
And you also add permission in manifest
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

I think your code should be like this:
new Thread(new Runnable() {
public void run() {
// call send message here
}
}).start();
Or you can use Asyntask

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?

Android Studio how to use Request in Fragments

I want to make this request in a fragment but the errors, as could I do?, thanks! I have many errors as .Builder() or OkHttpClient() when I want to implement it in a Fragment so I do not say any error in particular
String url = "https://freegeoip.net/json/"
Request mRequest = new Request.Builder()
.url(url)
.build();
Context mContext // A UI context
new OkHttpClient().newCall(mRequest).enqueue(new Callback() {
Handler mainHandler = new Handler(mContext.getApplicationContext().getMainLooper());
#Override
public void onResponse(Call call, Response response) {
String responseBody = null;
try {
responseBody = response.body().string();
} catch (final IOException e) {
mainHandler.post(new Runnable() {
#Override
public void run() {
// handle error
}
});
}
final String finalResponseBody = responseBody;
mainHandler.post(new Runnable() {
#Override
public void run() {
// handle response body
try {
JSONObject locationObject = new JSONObject(finalResponseBody);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
#Override
public void onFailure(Call call, final IOException e) {
mainHandler.post(new Runnable() {
#Override
public void run() {
mNetworkListener.onNetworkError(e.getMessage());
}
});
}
});

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);
}
}
}

Encrypt URL Issue in OKHttp

Here Issue is I have to pass the parameter in the URL API but issue is json works in Postman-->Body-->raw
by passing parameters:
{"from":"abc","to":"pqr","amount":"10000"}
result is:
{
"errFlag": "0",
"errMsg": "Success",
"result": "1745738346.397"
}
But same thing we pass in OkHttp we get the other result
here is my code Okhttp:
b1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (isNetworkAvailable()) {
RequestBody formBody = new FormBody.Builder()
.add("from", "abc")
.add("to", "pqr")
.add("amount", "10000")
.build();
try {
post(Baseurl, formBody, new Callback() {
#Override
public void onFailure(Call call, IOException e) {
Log.e("JSONDemo", "IOException", e);
}
#Override
public void onResponse(Call call, Response response) throws IOException {
String res = response.body().string();
Log.e("res", " " + res);
try {
JSONObject jsonObject=new JSONObject(res);
String errFlag=jsonObject.getString("errFlag");
if(errFlag.equals("0")){
String a=jsonObject.getString("result");
Log.e("hello........",a);
}else{
Log.e("Error", "Wrong");
}
runOnUiThread(new Runnable() {
#Override
public void run() {
// you can access all the UI componenet
}
});
} catch (Exception e) {
Log.e("JSONDemo", "onResponse", e);
e.printStackTrace();
}
}
});
} catch (Exception e) {
Log.e("JSONDemo", "Post Exception", e);
}
}
}
});
private final OkHttpClient client = new OkHttpClient();
Call post(String url, RequestBody formBody, Callback callback) throws IOException {
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
Call call = client.newCall(request);
call.enqueue(callback);
return call;
}
Output is:
{"errNum":"404","errFlag":"1","errMsg":"Some fields are missing"}
I just want errFlag=0 then pass the result else not.
Appreciate for help
#Override
protected String doInBackground(String... params) {
OkHttpClient client = new OkHttpClient();
formBody.add("version", version);
formBody.add("device_id", device_id);
formBody.add("platform", "android");
RequestBody body = formBody.build();
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try {
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {
result = response.toString();
} else {
}
result = response.body().string();
} catch (Exception ex) {
ex.printStackTrace();
}
return result;
}
Try with this code if it helps you then I ll give you one generic class for this.
Try as follows,
String postBodyParamsJson = "{\"from\":\"abc\",\"to\":\"pqr\",\"amount\":\"10000\"}";
RequestBody body = RequestBody.create(JSON, postBodyParamsJson);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();

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