Update global variable by using a interface - java

I have a async class in my MainActivity.java
class Register extends AsyncTask<String, String, JSONObject> {
JSONObject json;
#Override
protected JSONObject doInBackground(String[] args) {
String function = args[3];
String email = args[2];
String password = args[1];
String name = args[0];
ContentValues params = new ContentValues();
params.put("username", name);
params.put("password", password);
params.put("function", function);
if (email.length() > 0)
params.put("email", email);
String URL = "https://lamp.ms.wits.ac.za/home/s2090704/index.php";
new PhpHandler().makeHttpRequest(act, URL, params, new RequestHandler() {
#Override
public void processRequest(String response) throws JSONException {
json = new JSONObject(response);
System.out.println(json); //outputs {response: " ...",message:"..."}
}
});
System.out.println(json); //outputs null
return json;
}
}
in doInBackground() PhpHandler processes details using OkHttp.
public class PhpHandler {
JSONObject json;
static String responseData = "";
public void makeHttpRequest(final Activity a, String url,
ContentValues params, final RequestHandler rh) {
// Making HTTP request
OkHttpClient client = new OkHttpClient();
FormBody.Builder builder = new FormBody.Builder();
for (String key : params.keySet()) {
builder.add(key, params.getAsString(key));
}
final Request request = new Request.Builder()
.url(url)
.post(builder.build())
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(#NotNull Call call, #NotNull IOException e) {
}
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
responseData = Objects.requireNonNull(response.body()).string();
//System.out.println(responseData);
a.runOnUiThread(new Runnable() {
#Override
public void run() {
try {
rh.processRequest(responseData);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
});
}
}
RequestHandler is an interface that processes request on the mainUiThread.
package com.example.registration;
import org.json.JSONException;
public interface RequestHandler{
void processRequest(String response) throws JSONException;
}
Now json doesn't update out of the processRequest method in doInBackground method of my async class Register.I know that interfaces make variables static and final is there any way to update the value of json?

processRequest method will be executed long after you return json from doInBackground, because makeHttpRequest performs an asynchronous http request.
Knowing this, you will probably want to re-design this class (there is no need to wrap already asynchronous request in AsyncTask), but if you really want to do it this way, you would have to wait for your request to complete before returning the json (for example, by using CountDownLatch).
CountDownLatch latch = new CountDownLatch(1);
someField = null;
AtomicReference<String> someValue = new AtomicReference<>();
// don't start new threads like this, im just trying to keep this example simple
new Thread() {
Thread.sleep(1000); // sleep for 1 second
someValue.set("abc"); // notice that because when using AtomicReference you assign it's value using `set` method instead of `=` operator, you can keep it as local variable instead of field class
latch.countDown(); // reduce latch count by one
}.run();
System.out.println(someValue.get()); // null - because the assignation will happen in one second
latch.await(); // this will force current thread to wait until the latch count reaches zero (initial was 1, passed to constructor)
System.out.println(someValue.get()); // "abc"
read more

Related

Passing value from onResponse to onPostExecute keep turning to null

I'm connecting to API with the async task. I need to pass two values from the API to the app, in onResponse I have logged the values and got them right. But in onPostExecute one of them keep turning into null
I have already tried to check different values from API and logs to see if the value that I'm looking for is there or if the connection is valid, everything went alright until it hits the onPostExecute where I'm getting only one value
public class ChooseLocationTask extends AsyncTask<String, Void, Void> {
OkHttpClient client = new OkHttpClient.Builder()
//default timeout for not annotated requests
.readTimeout(15000, TimeUnit.MILLISECONDS)
.connectTimeout(15000, TimeUnit.MILLISECONDS)
.writeTimeout(15000, TimeUnit.MILLISECONDS)
.build();
Request request;
private TextView location;
private TextView value;
String state;
Number probability;
String probablityString;
public ChooseLocationTask(TextView location, int selected, TextView value){
this.location = location;
this.value = value;
}
#Override
protected void onProgressUpdate(Void...values){
super.onProgressUpdate(values);
}
#Override
protected void onPreExecute(){
super.onPreExecute();
}
#Override
protected Void doInBackground(String... urls) {
request = new Request.Builder().url(urls[0]).build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
Log.d("CallMsg", String.valueOf(call));
}
#Override
public void onResponse(Call call, Response response) throws IOException {
Log.d("Response", String.valueOf(response));
try {
JSONObject jsonObject = new JSONObject(response.body().string());
JSONObject weather = jsonObject.getJSONObject("weather");
JSONObject location = weather.getJSONObject("location");
state = location.getString("state");
JSONObject percentage = jsonObject.getJSONObject("probability");
JSONObject calculated = percentage.getJSONObject("highest");
probability = calculated.getInt("value");
probablityString = probability.toString();
Log.d("percentage", probability.toString());
Log.d("String",probablityString);
Log.d("location",state);
} catch (JSONException e){
e.printStackTrace();
}
}
});
return null;
}
#Override
protected void onPostExecute(Void voids){
if(isCancelled()){
voids= null;
} else {
location.setText(state);
value.setText("your chance to see Northern lights today is" + probablityString);
Log.d("value", "onPostExecute: " + probablityString);
}
Log.d("post", "onPostExecute: " + probability);
}
}
Basically, all I need is advice on how to get this value, I feel like maybe I made mistake when converting it into a string, but in logs, in onResponse it shows quite alright, so I don't know. Thank you for all the advice
Retrofit has two techniques for performing requests, one synchronous (execute()) and another asynchronous (enqueue()). You are using the asynchronous technique, and as a result, when you go to execute your AsyncTask, your doInBackground() method immediately calls enqueue(), completes, and calls onPostExecute() before your request has completed.
You have two options. First, you can keep your current AsyncTask, but replace enqueue() for execute(). This will look a little like:
public class ChooseLocationTask extends AsyncTask<String, Void, Void> {
private final OkHttpClient client = new OkHttpClient.Builder()
//default timeout for not annotated requests
.readTimeout(15000, TimeUnit.MILLISECONDS)
.connectTimeout(15000, TimeUnit.MILLISECONDS)
.writeTimeout(15000, TimeUnit.MILLISECONDS)
.build();
private final Request request;
private final TextView location;
private final TextView value;
String state;
Number probability;
String probablityString;
public ChooseLocationTask(TextView location, int selected, TextView value){
this.location = location;
this.value = value;
}
#Override
protected Void doInBackground(String... urls) {
request = new Request.Builder().url(urls[0]).build();
try {
final Response<?> response = client.newCall(request).execute();
if(response.isSuccessful()) {
final JSONObject jsonObject = new JsonObject(response.body().string());
//etc...
}
} catch (IOException e) {
//...
}
//I'd recommend you return values as well, rather than assigning them to
//instance variables
return null;
}
#Override
protected void onPostExecute(Void voids){
if(isCancelled()){
voids= null;
} else {
location.setText(state);
value.setText("your chance to see Northern lights today is" + probablityString);
Log.d("value", "onPostExecute: " + probablityString);
}
Log.d("post", "onPostExecute: " + probability);
}
Or, you can get rid of your AsyncTask entirely, and just call enqueue()

Volley onResponse work after twice click

I need a little help :)
I'm using volley on my Android app and I wrote this codes.
public String validateCredentials(final String email, final String password) {
StringRequest strReq = new StringRequest(com.android.volley.Request.Method.POST,
LOGIN_URL, new com.android.volley.Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject serverResponse = new JSONObject(response);
Log.d("Result: ", serverResponse.getString("result"));
responseServ = serverResponse.getString("result");
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new com.android.volley.Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}) {
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
params.put(email, password);
return params;
}
};
AppController.getInstance().addToRequestQueue(strReq);
return responseServ;
}
When I click the button Log.d("Result: ", serverResponse.getString("result")); this code is work but return responseServ; is not send any data on first click.
My button onClick code is
Toast.makeText(activity, authModel.validateCredentials(email, password), Toast.LENGTH_SHORT).show();
How do I solve this problem?
Thanks in advance
Volley is asynchronous aka you make the call, then later the callback is executed (Log.d() part). But you are also synchronously returning value which is empty the first time, and only the second time return values.
Have in mind that the second time it returns the first result.
What you have to do is do all your work in onResponse()
PS: As you want to keep MVP pattern you can- Define callback Interface and pass it to validateCredentials(final String email, final String password, final OnLoginComplete callback) and then in onResponse() callback.loginComplete()

Creating a Network Thread as a Method

I'm very new at programming for Android - please bear with me.
I'm building an app that requires network access, using OKHttp. Since I will be making many similarly structured requests from my server, I created a class that handles all network-related tasks, as I like to keep things compartmentalized.
One method I'm working on is createNetworkThread from within my NetworkManager class. This particular method takes three arguments:
Context context, final String requestURI, final RequestBody formParameters
What I need assistance with is how to return the data received from this method so I can use and manipulate it in the calling Activity.
Here is the method in question:
public void createNetworkThread(Context context, final String requestURI, final RequestBody formParameters) {
if (!this.isConnected(context)) return;
Runnable runnable = new Runnable() {
#Override
public void run() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(requestURI).post(formParameters).build();
Response response = null;
// Send login request, get response //
try {
response = client.newCall(request).execute();
String stringResponse = response.body().string();
JSONObject jsonResponse = new JSONObject(stringResponse);
Log.d("Net", "Request send and received!");
} catch (IOException e) {
Log.d("Net", "Failed");
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
Here is the call from the Activity:
final NetworkManager Net = new NetworkManager(this);
...
final String requestURI = "http://192.168.1.111/videonow.club/apprequest/signup/thread.php";
final RequestBody formVars = new FormBody.Builder().add("email", strEmail).add("password", strPass1).add("first_name", strNameFirst).add("last_name", strNameLast).build();
Net.createNetworkThread(SignupActivity.this, requestURI, formVars);
What I need to know is how to get the JSON data from jsonResponse returned from the method (I know void doesn't allow this) so I can use the data.
Would it be better to have the jsonObject returned so I can use something like this:
SomeType response = Net.createNetworkThread(...);
Or, to have a class variable within NetworkManager that would be set by the method so it would be called to and referenced like this:
Net.createNetworkThread(...);
SomeType response = Net.someVariable;
Or is there some much more reasonable way to receive this data?
I'm also calling new OkHttpClient() twice - once in the activity, so I can build the requestBody post variables, as well as in the NetworkManager class itself. My instincts tell me this is redundant... if so, is there a way to make this more efficient?
Thanks in advance!
You can use OkHttp with AysncTask like this:
public class Webservice extends AsyncTask<String, String, UserResponse> {
private String TAG = Webservice.class.getSimpleName();
private static final String ENDPOINT = "YOUR_URL";
private static final Moshi MOSHI = new Moshi.Builder().build();
private static final JsonAdapter<UserResponse> CONTRIBUTORS_JSON_ADAPTER_RESPONSE = MOSHI.adapter(Types.newParameterizedType(UserResponse.class, UserResponse.class));
UserResponse webResponse;
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
String postBody = "postBody\n";
#Override
protected UserResponse doInBackground(String... parmas) {
OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder();
Call call = okHttpClient.build().newCall(new Request.Builder()
.url(ENDPOINT)
.post(RequestBody.create(JSON, postBody))
.addHeader("Content-Type", "application/json")
.build());
try {
Response response = call.execute();
adModelResponse = CONTRIBUTORS_JSON_ADAPTER_RESPONSE.fromJson(response.body().source());
} catch (IOException e) {
e.printStackTrace();
}
return webResponse;
}
#Override
protected void onPostExecute(UserResponse adModelResponse) {
}
}
And then in Activity call like this:
Webservice webservice = new Webservice();
webservice.execute("YOUR_PARAMETER");
Libraries Used:
okhttp-3.2.0, moshi-1.1.0, okio-1.8.0
Make NetworkManager Abstract and add one abstract method say public abstract void onResult(JSONObject response); and override this method like
final NetworkManager Net = new NetworkManager(this){
#Override
public void onResult(JSONObject response){
//do whatever you want here
}
};
And from the createNetworkThread when finished call this method as
.....
response = client.newCall(request).execute();
String stringResponse = response.body().string();
JSONObject jsonResponse = new JSONObject(stringResponse);
onResult(jsonResponse);
......
You can use callback interface to get your data back to your activity. Consider the example below:
public interface JsonResponse {
onResponseReceived(JSONObject response);
}
Then your createNetworkThread will looks like this:
public void createNetworkThread(Context context, final String requestURI, final RequestBody formParameters, JsonResponse responseCallback) {
if (!this.isConnected(context)) return;
Runnable runnable = new Runnable() {
#Override
public void run() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(requestURI).post(formParameters).build();
Response response = null;
// Send login request, get response //
try {
response = client.newCall(request).execute();
String stringResponse = response.body().string();
JSONObject jsonResponse = new JSONObject(stringResponse);
responseCallback.onResponseReceived(jsonResponse); // This line will return to your caller
Log.d("Net", "Request send and received!");
} catch (IOException e) {
Log.d("Net", "Failed");
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
And finally the caller:
Net.createNetworkThread(SignupActivity.this, requestURI, formVars, new JsonResponse() {
#Override
public void onResponseReceived(JSONObject response) {
// Do stuff in your activity
// eventually use runOnUiThread for your UI operations
}
});

Returned object from another class method

I feel like this is a very basic concept missunderstanding. In my Android app I make HTTP requests using Volley lib. At first I made the requests through a JsonObjectRequest in the Activity code and worked right, but now I separated the request code in a class apart so I can call it from any Activity. The new class has a method that returns the requested JSONObject but any "json action" I do over the returned object ends in an error.
Here is the new class code, JSONRequests:
public class JSONRequests {
private JSONObject mResponse;
private String mURL = "https://myurl.com/";
public JSONObject getJSONObject(final String credentials, final String path) {
final JsonObjectRequest mRequest = new JsonObjectRequest(mURL.concat(path), null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
mResponse = response;
try {
Log.d("RESPONSE", mResponse.getString("id")); // It works here
} catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}
) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Authorization", "Basic " + credentials);
return headers;
}
};
MyApplication.getInstance().getRequestQueue().add(mRequest);
return mResponse;
}
}
And here is how I call getJSONObject from MyActivity:
JSONRequests mRequest = new JSONRequests();
JSONObject mInfo = mRequest.getJSONObject(mEncodedCredentials, mPath);
try {
Log.d("RESPONSE", mInfo.getString("id")); // This doesn't work
} catch (JSONException e) {
e.printStackTrace();
}
When in the Activity file, I used "response" as a JSONObject and it worked, but now separated it won't. I don't know if is a error with JSONObject or just that I'm not getting the returned object in the correct way. Thanks in advance.

Assign a value to a final variable

Suppose I have this code:
public HttpResponse myFunction(...) {
final HttpResponse resp;
OnResponseCallback myCallback = new OnResponseCallback() {
public void onResponseReceived(HttpResponse response) {
resp = response;
}
};
// launch operation, result will be returned to myCallback.onResponseReceived()
// wait on a CountDownLatch until operation is finished
return resp;
}
Obviously I can not assign a value to resp from onResponseReceived because it is a final variable, BUT if it was not a final variable onResponseReceived could not see it.
Then, how can I assign a value to resp from onResponseReceived?
What I thought is to create a wrapper class for enclosing the resp object. The final object would be an instance of this wrapper class and I could assign the value to resp working on the object inside the final class (which is not final).
The code would be this one:
class ResponseWrapper {
HttpResponse resp = null;
}
public HttpResponse myFunction(...) {
final ResponseWrapper respWrap = new ResponseWrapper();
OnResponseCallback myCallback = new OnResponseCallback() {
public void onResponseReceived(HttpResponse response) {
respWrap.resp = response;
}
};
// launch operation, result will be returned to myCallback.onResponseReceived()
// wait on a CountDownLatch until operation is finished
return respWrap.resp;
}
What do you think about this solution?
java.util.concurrent.atomic.AtomicReference
Standard practice is to use a final AtomicReference, which you can set and get. This adds the benefit of thread safety as well :) As you mentioned, a CountDownLatch is helpful in waiting for completion.
Your solution is as valid as any other. Other popular choices include the one element array
final HttpResponse[] resp = new Response[1];
// In the callback
resp[0] = response;
// After the operation
return resp[0];
and the generic wrapper
public class Ref<T> {
public T value;
}
final Ref<HttpResponse> resp;
// In the callback
resp.value = response;
// After the operation
return resp.value;
You can combine the hand-back and the wait into one using a SynchronousQueue (exception handling omitted)
public HttpResponse myFunction(...) {
final Queue<HttpResponse> resp = new SynchronousQueue<HttpResponse>();
OnResponseCallback myCallback = new OnResponseCallback() {
public void onResponseReceived(HttpResponse response) {
resp.put(response);
}
};
return resp.take();
}
The change I would make would be to use an AtomicReference since this is obviously multi-threaded and you wouldn't have to write your own wrapper. Otherwise, seems reasonable to me.
You can make it mutable and final ;) The simplest approach is to use na array but an AtomicReference can also be used.
public HttpResponse myFunction(...) {
final HttpResponse[] resp = { null };
OnResponseCallback myCallback = new OnResponseCallback() {
public void onResponseReceived(HttpResponse response) {
resp[0] = response;
}
};
// launch operation, result will be returned to myCallback.onResponseReceived()
// wait on a CountDownLatch as soon as operation is finished
return resp[0];
}
or
public HttpResponse myFunction(...) {
final AtomicReference<HttpResponse> resp = new AtomicReference<HttpResponse>();
OnResponseCallback myCallback = new OnResponseCallback() {
public void onResponseReceived(HttpResponse response) {
resp.set(response);
}
};
// launch operation, result will be returned to myCallback.onResponseReceived()
// wait on a CountDownLatch as soon as operation is finished
return resp.get();
}

Categories

Resources