Android SetText in OkHttp Async callback causing crash - java

I have an android activity that executes an Asynchronous Okhttp call, when the activity is loaded ( called from the onStart method as getActiveRequests()).
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Button btLogout;
UserLocalStore userLocalStore;
String username;
String userEmail;
String recentAppName;
String recentRequestTime;
String isExpired;
TelephonyManager telephonyManager;
OkHttpClient client;
TextView tvRecentAppName, tvRecentRequestTime, tvIsExpired;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
userLocalStore = new UserLocalStore(this);
telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
TextView tvUserName = (TextView) findViewById(R.id.userName);
TextView tvUserEmail = (TextView) findViewById(R.id.userEmail);
tvRecentAppName = (TextView) findViewById(R.id.recentAppName);
tvRecentRequestTime = (TextView) findViewById(R.id.recentRequestTime);
tvIsExpired = (TextView) findViewById(R.id.isExpired);
client = new OkHttpClient();
username = userLocalStore.getLoggedInUser().name;
userEmail = userLocalStore.getLoggedInUser().email;
tvUserEmail.setText(userEmail);
tvUserName.setText(username);
btLogout = (Button) findViewById(R.id.btLogout);
btLogout.setOnClickListener(this);
}
#Override
protected void onStart() {
super.onStart();
if(authenticateUser() == true){
getActiveRequests();
}else{
startActivity(new Intent(this, LoginActivity.class));
}
}
What I want to do Is update the UI once the Http call has been made using the SetText methods. Here is my call, implemented in the GetActiveRequests() method.
client.newCall(request)
.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
String hello = "failed call";
}
#Override
public void onResponse(Call call, Response response) throws IOException {
final String responseData = response.body().string();
if (responseData != null) {
Gson gson = new Gson();
JsonElement element = gson.fromJson(responseData, JsonElement.class);
JsonObject jsonObject = element.getAsJsonObject();
final AccessRequest request = gson.fromJson(jsonObject, AccessRequest.class);
recentAppName = request.AppName.toString();
recentRequestTime = request.RequestTime.toString();
if (request.IsExpired)
isExpired = "Has Expired";
else isExpired = "Active";
tvRecentAppName.setText(recentAppName);
tvRecentRequestTime.setText(recentRequestTime);
tvIsExpired.setText(isExpired);
}
}
});
The problem I am having is that when the debugger reaches the SetText lines of code, it is causing the app to crash and close. I am at a loss as to how I can solve this but I assume it has something to with the Okhttp Async call not being able to update the UI, as my setText methods are working fine in onCreate().

That's because views' updates can only be done on UI thread and OkHttp's onResponse runs on background thread. Try to run that on main thread like this:
#Override
public void onResponse(Call call, Response response) throws IOException {
final String responseData = response.body().string();
if (responseData != null) {
Gson gson = new Gson();
JsonElement element = gson.fromJson(responseData, JsonElement.class);
JsonObject jsonObject = element.getAsJsonObject();
final AccessRequest request = gson.fromJson(jsonObject, AccessRequest.class);
recentAppName = request.AppName.toString();
recentRequestTime = request.RequestTime.toString();
if (request.IsExpired)
isExpired = "Has Expired";
else isExpired = "Active";
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
//Handle UI here
tvRecentAppName.setText(recentAppName);
tvRecentRequestTime.setText(recentRequestTime);
tvIsExpired.setText(isExpired);
}
});
}
}
Similarly if you have update any views on onFailure, do it on UI thread.

Related

AsyncTask not working with okhttp post request

The way the app behaves when I try to run it is black screen -> back to main activity.
Here's my code:
public class ResultsActivity extends AppCompatActivity {
final TextView textView = findViewById(R.id.textView);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_results);
new NetworkTask().execute();
}
public void backToMain(View v) {
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
private class NetworkTask extends AsyncTask<Void, Void, String> {
#Override
protected String doInBackground(Void... params) {
final String image = getExternalCacheDir() + "/image.jpeg";
File file = new File(image);
try {
final MediaType MEDIA_TYPE_JPEG = MediaType.parse("image/jpeg");
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("images", "image.jpeg",
RequestBody.create(file, MEDIA_TYPE_JPEG))
.addFormDataPart("organs", "leaf")
.build();
Request request = new Request.Builder()
.url("insert url here")
.post(requestBody)
.build();
OkHttpClient client = new OkHttpClient();
Response response = client.newCall(request).execute();
String result = response.body().string();
return result;
} catch(IOException e) {
e.printStackTrace();
return "error";
}
}
#Override
protected void onPostExecute(String result) {
if(result.equals("error")) {
textView.setText("error");
} else {
textView.setText(result);
}
}
}
I am fairly certain that the post request works just fine because I have tested it using:
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
So, maybe the issue lies with the way I put it in AsyncTask.
As Carl says, I put this line of code on doInBackground method. My problem has been solved.
#Override
protected String doInBackground(String... params) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
//...
}
Using AsyncTask is necessary in some cases such as sending a queue of images.

No content displays when app runs on recyclerViewPager

This is the code I am Using.
public class MainActivity extends AppCompatActivity {
public ArrayList<String> ImageUrls = new ArrayList<>();
public ArrayList<String> ImageNames = new ArrayList<>();
public ArrayList<String> ImageDesc = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initImages();
}
private void initImages(){
final OkHttpClient client = new OkHttpClient();
final Request request = new Request.Builder()
.url("http://url.in/wp-json/wp/v2/posts?_embed")
.build();
#SuppressLint("StaticFieldLeak") AsyncTask<Void, Void, String> asyncTask = new AsyncTask<Void, Void, String>() {
private static final String TAG = "SlideFragment";
#Override
protected String doInBackground(Void... params) {
try {
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {
Log.d(TAG, "doInBackground: REsponse Un Successfull - 56");
return null;
}
String Data = response.body().string();
response.body().close();
return Data;
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "doInBackground: Exceptione on line63");
return null;
}
}
#Override
protected void onPostExecute(String Data) {
super.onPostExecute(Data);
if (Data != null) {
Log.d(TAG, "onPostExecute: line72");
try {
JSONArray json = new JSONArray(Data);
for (int i = 0; i < json.length(); i++) {
JSONObject post = json.getJSONObject(i);
String title = post.getJSONObject("title").getString("rendered");
String description = post.getJSONObject("content").getString("rendered");
String imgURL = post.getJSONObject("_embedded").getJSONArray("wp:featuredmedia").getJSONObject(0).getJSONObject("media_details").getString("file");
String imagUrl = "http://url.in/wp-content/uploads/" + imgURL;
ImageNames.add(title);
ImageDesc.add(description);
ImageUrls.add(imagUrl);
Log.d(TAG, "onPostExecute: " + ImageNames);
}
}catch(JSONException j){
j.printStackTrace();
Log.d(TAG, "onPostExecute: on line 121");
}
}
}
};
asyncTask.execute();
initRecycler();
}
private void initRecycler(){
RecyclerViewPager mRecyclerView = (RecyclerViewPager) findViewById(R.id.list);
// setLayoutManager like normal RecyclerView, you do not need to change any thing.
LinearLayoutManager layout = new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false);
mRecyclerView.setLayoutManager(layout);
//set adapter
//You just need to implement ViewPageAdapter by yourself like a normal RecyclerView.Adpater.
RecyclerViewAdapter adapter = new RecyclerViewAdapter(ImageUrls, ImageNames, ImageDesc, this);
mRecyclerView.setAdapter(adapter);
}
}
I have run the same code with local data i..e the ArrayList with hardcoded data. It works. But If I try with API data It shows Nothing. I have checked the ArrayList with logging. It is fine.
I don't know where I am Wrong.
UPDATE
Thanks to #sonhnLab. In the code I have removed initRecycler(); from initImages(); and added to onPostExecute();. That worked.
Due to the asynchronous nature of Asynctask, the following line: "initRecycler();" doesn't necessarily gets called after completion of the network request hence no content. Remember, any task that depends on the asynchronous response needs to be implemented inside response method, in this case inside onPostExecute().
With the Help of sonhnlab I have successfully got the desired output.
I have made this initRecycler(); call into onPostExecute() call. so when the information is ready from the API call it initiates the Recycler.
I have Updating the Code in the question.
You should call initRecyler() onPostExecute when async task is completed

string comparison in android / java

I have got a JSON response from an API that I have converted to a string and trying to compare it for true or false value,
On the log cat I can see the result:
{
"message": "success",
"status": "Auth_Successful",
"response": "Authentication successful"
}
I am trying fit it into an if statement like below
I have tried most of the comparison methods(==, .equals(), .compareTo()) but not getting any result
Can anyone let me know what is the correct way to approach it as I am still new to Java and Android. I have seen a lot of similar posts but unable to figure out.
Thank you very much for your time and assistance in this matter.
package com.example.shiben.fabapp;
public class MainActivity extends AppCompatActivity {
private Request request;
private static final String Tag = MainActivity.class.getName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button LoginButton = (Button) findViewById(R.id.loginButton);
EditText userName = (EditText) findViewById(R.id.userName);
EditText userPassword = (EditText) findViewById(R.id.userPassword);
final TextView displayTest = (TextView) findViewById(R.id.displayTest);
LoginButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody body = RequestBody.create(mediaType, "username=xxxxxxxxx&password=xxxxxxxxx");
Request request = new Request.Builder()
.url("http://9.xxx.xxx.xxx/test/xxxxx_api.aspx")
.post(body)
.addHeader("cache-control", "no-cache")
.addHeader("content-type", "application/json")
.build();
//execute the request
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
Log.i(Tag, e.getMessage());
}
#Override
public void onResponse(Call call, Response response) throws IOException {
Log.i(Tag, response.body().string());
String result = response.body().toString();
//if (result==("{\"message\":\"success\",\"status\":\"Auth_Successful\",\"response\":\"Authentication successful\"}")){
if (result.compareTo("{\"message\":\"success\",\"status\":\"Auth_Successful\",\"response\":\"Authentication successful\"}")==0) {
//if (result.equals("{\"message\":\"success\",\"status\":\"Auth_Successful\",\"response\":\"Authentication successful\"}")) {
TastyToast.makeText(MainActivity.this, "String Comparison Success", TastyToast.LENGTH_LONG, TastyToast.SUCCESS);
}
}
});
}
});
}
}
You should parse the JSON string and compare the message.
if (message.equals("success"))
If you don't like to parse, you may try this one (Bad practice):
if(response.contains("success"))
String result = response.body().toString(); doesn't work. Please use string() method instead of toString()
#Override
public void onResponse(Response response) throws IOException {
if (response.isSuccessful()) {
doSomething(response.body().string());
}
}
private void doSomething(String response) {
}
Try this in your code .
String result = response.body().toString();
if(TextUtils.isEmpty(result)){
Toast.makeText(this, "result is null", Toast.LENGTH_SHORT).show();
return;
}
try {
JSONObject jsonObject = new JSONObject(result);
String message = jsonObject.optString("message");
String status = jsonObject.optString("status");
String response = jsonObject.optString("response");
if (TextUtils.equals("success", message)) {
TastyToast.makeText(MainActivity.this, "String Comparison Success", TastyToast.LENGTH_LONG, TastyToast.SUCCESS);
}
} catch (JSONException e) {
e.printStackTrace();
}
The problem is related with OkHttp
This is because when you called the following:
Log.i(Tag, response.body().string());
String result = response.body().toString();
String result will be empty because you've already called response.body() in Log. When you called it, it will be emptied.
So, you need to save it to the result before calling it from the log. Something like this:
String result = response.body().toString();
Log.i(Tag, result);
Here from the documentation:
The response body can be consumed only once.
This class may be used to stream very large responses. For example, it
is possible to use this class to read a response that is larger than
the entire memory allocated to the current process. It can even stream
a response larger than the total storage on the current device, which
is a common requirement for video streaming applications.
Because this class does not buffer the full response in memory, the
application may not re-read the bytes of the response. Use this one
shot to read the entire response into memory with bytes() or string().
Or stream the response with either source(), byteStream(), or
charStream().
I used volley instead of okhttp and got it sorted ,
Below is the code
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getName();
private Button btnSendRequest;
private RequestQueue mRequestQueue;
//creating a string request
private StringRequest stringRequest;
private String url = "http://9.xxx.xxx.xxx/test/xxxxxxx.aspx";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnSendRequest = (Button) findViewById(R.id.loginBtn);
btnSendRequest.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//on click of the button send request and print the response
//initializing request queue and string request in sendRequestAndPrintResponse
sendRequestAndPrintResponse();
}
});
}
private void sendRequestAndPrintResponse() {
mRequestQueue = Volley.newRequestQueue(this);
stringRequest = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.i(TAG, "Success" + response.toString());
String result = response.toString();
TextView displayText = (TextView) findViewById(R.id.displayText);
displayText.setText(result);
if (result.equalsIgnoreCase("{\"message\":\"success\",\"status\":\"Auth_Successful\",\"response\":\"Authentication successful\"}")){
Toast.makeText(getApplicationContext(), "comparison successful", Toast.LENGTH_LONG).show();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.i(TAG, error.toString());
}
}){
#Override
protected Map<String, String> getParams()
{
Map<String, String> params = new HashMap<String, String>();
params.put("username", "someone#example.com");
params.put("password", "myPassword");
return params;
}
};
mRequestQueue.add(stringRequest);
}
}
If anyone could let me know what went wrong with okhttp, it would be very helpful for future reference.

Android AsyncTask Error with Facebook SDK

I used Facebook SDK in Android App for Login. I can login with Facebook. I want to insert AccessToken and Email to my database. I solved get Email but I can get email from user with Async. Then I use AsyncTask For Insert data to database. When I used AsyncTask with Facebook SDK, I got exception.
AsyncTask #5 java.lang.RuntimeException: An error occured while executing doInBackground()
And My Code
private JSONParser jParser = new JSONParser();
private String AccessToken;
public String Email;
private LoginAPI api = new LoginAPI();
#Override
protected void onCreate(Bundle savedInstanceState) {
FacebookSdk.sdkInitialize(getApplicationContext());
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
loginButton = (LoginButton)findViewById(R.id.login_button);
if(Profile.getCurrentProfile()!=null){
user = Profile.getCurrentProfile();
new LoginAPI().execute();
}
loginButton.setReadPermissions("public_profile");
loginButton.setReadPermissions("email");
callbackManager = CallbackManager.Factory.create();
LoginManager.getInstance().registerCallback(callbackManager,
new FacebookCallback<LoginResult>() {
#Override
public void onSuccess(LoginResult loginResult) {
user = Profile.getCurrentProfile();
AccessToken = loginResult.getAccessToken().getToken();
GraphRequest.newMeRequest(loginResult.getAccessToken(), new GraphRequest.GraphJSONObjectCallback() {
#Override
public void onCompleted(JSONObject user, GraphResponse graphResponse) {
Email = user.optString("email");
api.execute();
}
}).executeAsync();
}
#Override
public void onCancel() {
AlertDialog alertMessage = new AlertDialog.Builder(Login.this).create();
alertMessage.setMessage("Iptal");
alertMessage.show();
}
#Override
public void onError(FacebookException exception) {
AlertDialog alertMessage = new AlertDialog.Builder(Login.this).create();
alertMessage.setMessage(exception.toString());
alertMessage.show();
}
});
}
class LoginAPI extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
protected String doInBackground(final String... args) {
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new NameValuePair() {
#Override
public String getName() {
return "Email";
}
#Override
public String getValue() {
return Email;
}
});
params.add(new NameValuePair() {
#Override
public String getName() {
return "AccessToken";
}
#Override
public String getValue() {
return AccessToken;
}
});
JSONObject json = jParser.makeHttpRequest("WebServiceURL", "POST", params); //Exception Line
try {
if (json != null) {
Sonuc = json.getBoolean("Result");
Mesaj = json.getString("Message");
JSONObject _uye = json.getJSONObject("Data");
if(_uye!=null){
localUser = new User(_uye.getInt("UserID"),
_uye.getString("FacebookID"),
_uye.getString("NameSurname"),
_uye.getString("Email"),
_uye.getString("ProfileImage"));
}
else{
localUser = new User(1,"","","","");
}
}
} catch (JSONException e) {
return e.getMessage().toString();
}
return null;
}
protected void onPostExecute(String file_url) {
runOnUiThread(new Runnable() {
public void run() {
if(Sonuc){
Intent welcomeAct = new Intent(getBaseContext(), Welcome.class);
welcomeAct.putExtra("User",localUser);
Login.this.startActivity(welcomeAct);
}
else{
AlertDialog alertMessage = new AlertDialog.Builder(Login.this).create();
alertMessage.setMessage(Mesaj);
alertMessage.show();
}
}
});
}
}
I solved this problem. I wrote AsyncTask code in OnActivityResult event. When Authenticate process finished, I call AsyncTask.

Networking with Volley

Please could you help me with a network request I am trying here.
I have 2 Classes Network.class and MainActivity.class. I have a TextView in the MainActivity Class that I would like to be replaced with the text I get from the Network call in the Network Class. Problem I am currently having is I cant initiate the network call in the Network Class when the MainActivity Class is loaded when the application starts?
Below is the Code to MainActivity:
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView)findViewById(R.id.text);
String test = Network.userName;
tv.setText(test);
}
}
and below is the network class that I would like to do the network call and the response will need to replace the text in the TextView in the MainActivity Class.
Network Class:
public class Network extends Activity{
public static String userName;
private String jsonResponse;
String url_home = "http://www.someurl.com";
private void postData(final TextView tv) {
final RequestQueue request = Volley.newRequestQueue(this);
JsonObjectRequest postReq = new JsonObjectRequest(Request.Method.GET, url_home, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
jsonResponse = "";
for(int i = 0; i< response.length(); i++) {
String userName = response.getString("DOTWBannerHD");
System.out.println("++++++++++++++++++++++++++++++++++++++++++++userName = " + userName);
jsonResponse += userName;
System.out.println("++++++++++++++++++++++++++++++++++++++++++++JsonResponse = " + jsonResponse);
}
tv.setText(jsonResponse);
} catch (JSONException e){
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
System.out.println("Error [" + error + "]");
}
}) {
#Override
public Map getHeaders() throws AuthFailureError {
Map headers = new HashMap();
headers.put("Accept", "application/json");
System.out.println(headers);
return headers;
}
};
request.add(postReq);
}
}
I am very new to Android and am battling to call the postData method from the second activity, in the MainActivity? The issue I get is that the TextView has text hard coded in the XML but when I run the Application it is blank? It's like, either the response is blank, but I doubt its that because the code I put in the Network Class (System.out.println("++++++++++++++++++++++++++++++++++++++++++++userName = " + userName);) isn't showing up in the Terminal which makes me think that its not running the postData method at all or the response is not working but it just sets the TextView to blank?
You cannot change the GUI from an async-task.
As JsonObjectRequest works asynchronous you should run tv.setText(jsonResponse); on the main thread using:
runOnUiThread(new Runnable() {
public void run() {
tv.setText(jsonResponse);
}
});
Following up on my comment, the reason your are not seeing anything in the terminal is because you're not calling you postData method so it's never executed.
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView)findViewById(R.id.text);
Network network = new Network();
network.postData(tv);
}
}
and make Network a normal class not an Activity.
public class Network{
////The variables and your postData method here
}

Categories