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();
}
Related
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
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()
I have an asyncTask in my application as below. I have to fetch data from realm(which is successfully stored in another activity) into this AsyncTask. Below is my AsyncTask code:
public class MakeAsyncRequest extends AsyncTask<Object, String, MakeAsyncRequest.ResponseDataType>{
public Context asyncContext;
private MakeAsyncRequest(Context context){
asyncContext = context;
}
private static final OkHttpClient client = new OkHttpClient();
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
private MakeRequest.ResponseHandler handler;
private String method;
private User user;
private Realm realm;
class ResponseDataType
{
InputStream inputStream;
String string;
String cookie;
}
MakeAsyncRequest(MakeRequest.ResponseHandler responseHandler, String type)
{
method = type;
handler = responseHandler;
}
#Override
protected ResponseDataType doInBackground(Object... params) {
try {
Requests requests = new Requests((Context) params[0]);
String url = params[1].toString();
String bodyJson = null;
if(method.equals("PUT") || method.equals("POST")) {
bodyJson = params[2].toString();
}
final Request.Builder builder;
Response response;
RequestBody body;
switch (method) {
case "GET": builder = new Request.Builder().url(url);
break;
case "DOWNLOAD": builder = new Request.Builder().url(url);
break;
case "POST": body = RequestBody.create(JSON, bodyJson);
builder = new Request.Builder()
.url(url)
.post(body)
.addHeader("Cookie", "key=value");
break;
case "PUT": body = RequestBody.create(JSON, bodyJson);
builder = new Request.Builder()
.url(url)
.put(body);
break;
default: builder = new Request.Builder().url(url);
}
builder.addHeader("Accept", "application/json");
realm = RealmController.initialize(this).getRealm();
final RealmResults<User> users = realm.where(User.class).findAllAsync();
user = users.first();
if(user.getCookie() !== null && !user.getCookie().isEmpty()){
builder.addHeader("cookie", "user.getCookie()");
}
response = client.newCall(builder.build()).execute();
ResponseDataType responseDataType = new ResponseDataType();
if(method.equals("DOWNLOAD")) { responseDataType.inputStream = response.body().byteStream(); }
else {
responseDataType.string = response.body().string();
responseDataType.cookie = response.headers().get("set-cookie");
CookieManager cManager = new CookieManager(null, CookiePolicy.ACCEPT_ALL);
cManager.getCookieStore().getCookies();
}
return responseDataType;
} catch (IOException e) {
return null;
}
}
#Override
protected void onPostExecute(ResponseDataType response) {
try {
if (method.equals("DOWNLOAD")) {
handler.onFinishCallback(response.inputStream);
}else {
handler.onFinishCallback(response.string, response.cookie);
CookieManager cManager = new CookieManager(null, CookiePolicy.ACCEPT_ALL);
cManager.getCookieStore().getCookies();
}
}catch (Exception e){
Log.d("hExceptionError", e.toString());
handler.onFinishCallback("{\n" +
" \"error\": \"error\"\n" +
"}","");
}
}
I am received the access error - "Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created." whenever the control tries to execute the Realm results or to get the first object from Realm.
Below is my RealmController which i created to control the realm instance:
public class RealmController {`public static RealmController initialize(Activity activity) {
if (instance == null) {
instance = new RealmController(activity.getApplication());
}
return instance;
}
public static RealmController initialize(MakeAsyncRequest activity) {
if (instance == null) {
instance = new RealmController(activity.);
}
return instance;
}
}`
I have my user model(Realm object) with setters and getters.
The point is - you cannot create and access Realm on different threads, i.e. create Realm instance in Activity and use it in .doInBackground() method. Create and release Realm immediately before and after transaction.
There may be another issue - don't register quer observer on background thread in AsyncTask - it doesn't have Looper initialized - use main thread or HandlerThread.
Release realm after it is no longer needed (you didn't in your code), because Realm has limited number of instances.
You can initiate a Realm instance in the doInBackground method of your AsyncTask like this:
private class MyAsyncTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... voids) {
Realm realm = Realm.getDefaultInstance();
// Do your Realm work here
realm.close();
return null;
}
}
It's important to note that you should always open and close a Realm instance in the same thread. In this case, since the AsyncTask is running in a background thread, you can open and close the Realm instance within the doInBackground 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
}
});
I'm just learning about AsyncTask and want to use it as a separate class, rather then a subclass.
For example,
class inetloader extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
String response = "";
DefaultHttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(urls[0]);
try {
HttpResponse execute = client.execute(httpGet);
InputStream content = execute.getEntity().getContent();
BufferedReader buffer = new BufferedReader(
new InputStreamReader(content));
String s = "";
while ((s = buffer.readLine()) != null) {
response += s;
}
} catch (Exception e) {
e.printStackTrace();
}
return response;
}
#Override
protected void onPostExecute(String result) {
Log.e("xx",result);
// how do I pass this result back to the thread, that created me?
}
}
and the main(ui) thread:
inetloader il = new inetloader();
il.execute("http://www.google.com");
//il.onResult()
//{
///do something...
//}
Thanks!
Use a interface. Something like:
interface CallBackListener{
public void callback();
}
Then do this in your UI thread:
inetloader il = new inetloader();
li.setListener(this);
il.execute("http://www.google.com");
In inetloader, add:
CallBackListener mListener;
public void setListener(CallBackListener listener){
mListener = listener;
}
then In postExecute(), do:
mListener.callback();
you can pass the activity instance to constructor and call activity function from there...
Like use interface :
public interface ResultUpdatable {
public void setResult(Object obj);
}
Implement this in the Activity and pass in the constructor of Async task and update the result from onPostExecute using setResult function.
inetloader il = new inetloader();
il.execute("http://www.google.com");
String result = il.get();//put it in try-catch
^^^^^^^^
here you get result which is in onPostExecute(String result)