I am trying to make a synchronous request to the server using RequestFuture but it's not working . The same request when done using asynchronous works fine.
This is my code:
public void fetchModules(){
JSONObject response = null;
RequestQueue requestQueue = Volley.newRequestQueue(getContext());
RequestFuture<JSONObject> future = RequestFuture.newFuture();
JsonObjectRequest request = new JsonObjectRequest(Url.ALL_MODULES_URL,null,future,future);
requestQueue.add(request);
try {
response = future.get(3, TimeUnit.SECONDS); // Blocks for at most 10 seconds.
} catch (InterruptedException e) {
Log.d(TAG,"interupted");
} catch (ExecutionException e) {
Log.d(TAG,"execution");
} catch (TimeoutException e) {
e.printStackTrace();
}
Log.d(TAG,response.toString());
}
I am getting a nullpointerexception:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String org.json.JSONObject.toString()' on a null object reference
at com.maths.app.AllModules.fetchModules(AllModules.java:85)
at com.maths.app.AllModules.onCreateView(AllModules.java:51)
at android.support.v4.app.Fragment.performCreateView(Fragment.java:2080)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1108)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1290)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:801)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1677)
at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:536)
at android.os.Handler.handleCallback(Handler.java:746)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5443)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
it's returning a null response. How can I solve this?
tl;dr;
You got deceived by the try-catch
Explanation:
Because the RequestFuture.get()is probably running on the UI thread you are really getting a java.util.concurrent.TimeoutException behind the scenes. That is the default behaviour when the calls gets executed on the main thread.
The try catch stops the app from crashing, nevertheless the response is still a null reference which crashes the app when you try to Logthe result.
If you comment the following line you will see that the app doesn't crash (there) anymore.
Log.d(TAG,response.toString());
Fix: Making the RequestFuture network call on another thread!
One way to do it:
public class TestVolley {
private String TAG = "SO_TEST";
private String url = "http://pokeapi.co/api/v2/pokemon-form/1/";
public JSONObject fetchModules(Context ctx){
JSONObject response = null;
RequestQueue requestQueue = Volley.newRequestQueue(ctx);
RequestFuture<JSONObject> future = RequestFuture.newFuture();
JsonObjectRequest request = new JsonObjectRequest(url,null,future,future);
requestQueue.add(request);
try {
response = future.get(3, TimeUnit.SECONDS); // Blocks for at most 10 seconds.
} catch (InterruptedException e) {
Log.d(TAG,"interrupted");
} catch (ExecutionException e) {
Log.d(TAG,"execution");
} catch (TimeoutException e) {
e.printStackTrace();
}
Log.d(TAG,response.toString());
return response;
}
}
The AsyncTask which will make the network call :
public class MyVolleyAsyncTask extends AsyncTask<String,String, JSONObject> {
private Context ctx;
public MyVolleyAsyncTask(Context hostContext)
{
ctx = hostContext;
}
#Override
protected JSONObject doInBackground(String... params) {
// Method runs on a separate thread, make all the network calls you need
TestVolley tester = new TestVolley();
return tester.fetchModules(ctx);
}
#Override
protected void onPostExecute(JSONObject result)
{
// runs on the UI thread
// do something with the result
}
}
Main Activity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// this is your old code which will crash the app
//TestVolley tester = new TestVolley();
//tester.fetchModules(this);
// Works!
new MyVolleyAsyncTask(this).execute();
}
}
result:
com.so.henriquems.testvolleyfuture D/SO_TEST: {"id":1,"pokemon":{"url":"http:\/\/pokeapi.co\/api\/v2\/pokemon\/1\/","name":"bulbasaur"},[...]
Hope this helps
cheers!
We can use RxJava.
Lets assume that the fetchModules method returns JSONObject
Observable<JSONObject> moduleObservable = Observable.defer(new Callable<ObservableSource<? extends JSONObject>>() {
#Override
public ObservableSource<? extends JSONObject> call() throws Exception {
return Observable.just(fetchModules());
}
});
In MainActivity
moduleObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new io.reactivex.Observer<JSONObject>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(JSONObject response) {
// Your response is here
}
#Override
public void onError(Throwable e) {
showAlert(context, e.getMessage());
}
#Override
public void onComplete() {
}
});
After researching this question myself, you CAN'T (at the moment of writing) do it in the main thread
I want Volley to do it in the main thread because I need to block until Volley finish getting response. And this is not possible, synchronous request on Volley must be in an async thread, not in the main thread.
The work around that I have done is, doing the subsequent codes inside the onResponse method of Volley request
Related
When to invoke doneSignal.await() in this scenario??
My requirement is to wait the return call until callback is received.
I tried invoking it in other thread and also in main thread. But no luck, callback is never received thereby freezing the app and initially when I was not using the CountDownLatch getCall was returning null value immediately without waiting for the callback. Below is my code
public UserSubscriptions getDocument(final String uid) {
final UserSubscriptions[] userSubscriptions = new UserSubscriptions[1];
final CountDownLatch doneSignal = new CountDownLatch(1);
try {
Thread getCall = new Thread(new Runnable() {
#Override
public void run() {
try{
DocumentReference docRef = db.collection("xyz").document(uid);
docRef.get()
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
userSubscriptions[0] = null;
TimerLog.e("document retrieve failed");
doneSignal.countDown();
}
})
.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
userSubscriptions[0] = documentSnapshot.toObject(UserSubscriptions.class);
TimerLog.e("document retrieved successfully " + userSubscriptions[0].toString());
doneSignal.countDown();
}
});
TimerLog.e("control exitted from run");
} finally {
TimerLog.e("Nads in finally");
}
}
});
getCall.start();
TimerLog.e("before");
doneSignal.await();
TimerLog.e("after");
} catch (InterruptedException e) {
TimerLog.e("exception in await "+e.getLocalizedMessage());
}
return userSubscriptions[0];
}
Console Output :
before
control exitted from run
in finally
This CountDownLatch did not work for me neither any mutual exclusive lock thing. The best way to do such type of thing where you are receiving a callback from server then you should not have that function return anything. Simple method is to have callback functionality.
I need to yield a JsonObject in a class MainActivity from a method doInBackground() in a class Post.
I instantiated the class Post, called the method in it which is being passed parameters into, and tried to assign it to a variable of type JSONObject.
This is the class Post:
class Post extends AsyncTask<String, Void, JSONObject> {
#Override
protected JSONObject doInBackground(String... args) {
JSONObject jsonObject = null;
try {
//Connect to the website
Connection.Response response =
Jsoup.connect(args[0])
.method(Connection.Method.POST)
.data("text", args[1])
.data("language", args[2])
.ignoreContentType(true)
.execute();
Document document = response.parse();
jsonObject = new JSONObject(document.text());
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException err) {
Log.d("Error", err.toString());
}
return jsonObject;
}
}
And this is how I tried to retrieve the object in the class MainActivity:
Post post = new Post();
JSONObject object = post.execute(stringurl, text, "en");
The Java error I get is incompatible types. Required is org.json.JSONObject and found is android.os.AsyncTask <java.lang.String, java.lang.Void, org.json.JSONObject>.
I should be able to capture the JSONObject... how?
You can declare a method in MainActivity which can be called from the AsyncTask once it has fetched the JSONObject:
private onObtainJSONObject(JSONObject jsonObject){
if(jsonObject != null){
// do something with the JSONObject
} else{
// something went wrong - maybe show an error message?
}
}
And you need to override onPostExecute() in the AsyncTask:
public void onPostExecute(JSONObject jsonObject){
// Note: this will be executed on the main thread
MainActivity.this.onObtainJSONObject(jsonObject);
}
If the AsyncTask is not an inner class of your Activity, you can use a callback (a simple interface) as follows
public interface PostCallback{
void onSuccess(JSONObject data);
void onError(Exception exception);
}
Then you let the AsyncTask have a field of type PostCallback and a setter setCallback(PostCallback).
In MainActivity:
Post post = new Post();
post.setPostCallback(new PostCallback(){
#Override
onSuccess((JSONObject data){
onObtainJSONObject(data);
}
#Override
onError(Exception exception){
// exception handling ...
}
});
JSONObject object = post.execute(stringurl, text, "en");
In Post:
private PostCallback callback;
private Exception exception;
public setPostCallback(PostCallback callback){
this.callback = callback;
}
#Override
protected JSONObject doInBackground(String... args){
// keep everything as before but when an Exception occurs,
// assign it to *exception* in the catch block
}
#Override
public void onPostExecute(JSONObject jsonObject){
// Note: this will be executed on the main thread
if(exception == null){
callback.onSuccess(jsonObject);
} else {
callback.onError(exception);
}
}
From my Login Activity (First Activity Opened) I always do a check if the token is still active on my server which is done through Async Task that does API call to server.
here's the code from LoginActivity :
private void checkIfAuthenticated(){
SharedPreferences reader_auth = getSharedPreferences(getString(R.string.auth_preferences), MODE_PRIVATE);
String auth_key = reader_auth.getString(getString(R.string.auth_access_key),null);
String mobile_token = reader_auth.getString(getString(R.string.auth_mobile_token),null);
if (auth_key != null) {
//THIS PART RUNS THE TOKEN CHECK TO SERVER
authGlobal = new AuthenticationGlobal(this);
// I WANT THIS FUNCTION TO FINISH FIRST BEFORE IT GOES TO THE NEXT PART OF THE CODE
authGlobal.runAuthenticationCheck(auth_key,mobile_token);
String Auth_Key = reader_auth.getString(getString(R.string.auth_access_key),null);
Log.d("Auth Key Check 0",Auth_Key);
if (Auth_Key != null) {
Log.d("Auth Key Check 1",Auth_Key);
MoveToDashboardActivity();
}
}
}
The runAuthenticationCheck(String,String) Code is located on another class (Because it was meant to be a global function which can be called from any function on any activity)
runAuthenticationCheck is located in AuthenticationGlobal Class, here's the code :
public void runAuthenticationCheck (String mobile_token, String Access_token) {
checkAuthTask = new checkAuthenticationTask(mobile_token, Access_token);
checkAuthTask.execute((Void) null);
}
public class checkAuthenticationTask extends AsyncTask<Void, Void, Boolean> {
private GetDataService service;
private String mobile_token;
private String access_token;
checkAuthenticationTask( String Access_token,String Mobile_token) {
/*Create handle for the RetrofitInstance interface*/
mobile_token = Mobile_token;
access_token = Access_token;
service = RetrofitClientInstance.getRetrofitInstance().create(GetDataService.class);
}
#Override
protected Boolean doInBackground(Void... params) {
// TODO: attempt authentication against a network service.
try {
Call<CheckAuthenticationResponse> call = service.checkAuthentication(access_token,mobile_token);
Response<CheckAuthenticationResponse> CheckAuthenticationResponse = call.execute();
if (CheckAuthenticationResponse.code() == 200){
} else{
//clear shared preferences
clearAuthentication();
Log.e("AuthKey Global","Expired0");
}
} catch (IOException ea) {
clearAuthentication();
Log.e("AuthKey Global","Expired1");
Log.e("AuthenticationResponseError Global","Network Went Wrong");
ea.printStackTrace();
}
return true;
}
#Override
protected void onPostExecute(final Boolean success) {
//mAuthTask = null;
//showProgress(false);
if (success) {
Log.e("AuthKey Global","Done");
} else {
// mPasswordView.setError(getString(R.string.error_incorrect_password));
clearAuthentication();
Log.e("AuthKey Global","Expired2");
}
}
#Override
protected void onCancelled() {
//mAuthTask = null;
//showProgress(false);
}
There are 2 Class / Activity : "LoginActivity" and "AuthenticationGlobal".
There are 3 Function :
checkIfAuthenticated => located in LoginActivity, Which in turn actually call another function from another class (Function number 2 : "runAuthenticationCheck")
runAuthenticationCheck => located in AuthenticationGlobal. which in calls a AsyncTask via .execute(...) command.
checkAuthenticationTask => located in AuthenticationGlobal. Which actually does the API Call to server.
From "LoginActivity" I run a function "checkIfAuthenticated" => which calls function "runAuthenticationCheck" located at "AuthenticationGlobal" => which runs a Task "checkAuthenticationTask" which does API Call to server and does stuff.
The problem is, when I called the first Function, the code doesn't wait until the function "checkIfAuthenticated" / "checkAuthenticationTask" is done. Is there a way for me to make the app wait until the task / function finish first??
Thank you
UPDATE :
I ONLY NEED TO ADD .get() at the end of .execute() and wrap it inside try catch.
public void runAuthenticationCheck (String mobile_token, String Access_token) {
checkAuthTask = new checkAuthenticationTask(mobile_token, Access_token);
try {
checkAuthTask.execute((Void) null).get();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Well. I just need to add a .get() on the execute() and wrap it inside a try catch.
A dumb mistake.
here's the updated code :
public void runAuthenticationCheck (String mobile_token, String Access_token) {
checkAuthTask = new checkAuthenticationTask(mobile_token, Access_token);
try {
checkAuthTask.execute((Void) null).get();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
The code below will make it more clear:
public static String TCMResponse(String params, final Context c) {
final String url = "https://115.248.161.106/ois/API/android/" + params;
new Thread(new Runnable() {
#Override
public void run() {
String response="";
try {
Document doc = Jsoup.connect(url).validateTLSCertificates(false).timeout(6000).get();
response = doc.text();
}
catch (IOException e) {
Log.d("Err","External OIS not reachable!");
}
// I want to return 'response' here, for the TCMResponse()
}
}).start();
}
So as you can see from the code, there is a function, TCMResponse() which takes the parameters of the url which i pass, and it does web scraping, i know all these can be done using volley/ JSONParser easily. But i am just experimenting, how to parse using web scraping.
So after the page is scraped, i need that function to return the response of the scraped page,
I've used Callable with executor service, but it again freezes the thread..
Have a look on what i've done:
public static String TCMResponse(String params, final Activity act) {
StrictMode.ThreadPolicy policy = new
StrictMode.ThreadPolicy.Builder()
.permitAll().build();
StrictMode.setThreadPolicy(policy);
final String url = "https://115.248.161.106/ois/API/android/" + params;
response="";
class MyBgThread implements Callable<String>
{
#Override
public String call() throws Exception {
try{
Document doc = Jsoup.connect(url).validateTLSCertificates(false).timeout(6000).get();
return doc.text();
}catch (Exception e)
{
Log.d("Exception",e.toString());
Snackbar.with(act, null)
.type(Type.ERROR)
.message("Something got wrong!")
.duration(Duration.LONG)
.show();
return "{'auth':'false'}";
}
}
}
Callable<String> worker = new MyBgThread();
ExecutorService ex = Executors.newSingleThreadExecutor();
Future<String> future = ex.submit(worker);
try{
response = future.get();
}catch(Exception e)
{
Log.d("Thread Ex",e+"");
}
ex.shutdown();
return response;
}
The main thread gets blocked because of your call to Future::get().
From the docs:
Waits if necessary for the computation to complete, and then retrieves its result.
which means; if the task Thread has not yet finished, the current Thread will wait until it returns a result.
I can see another problem in your code: you are showing a Snackbar, which is a UI component, in a Thread that is not the UI Thread.
Since you are working on Android, I would definitely use an AsyncTask, perform the expensive call in doInBackground(), then update the UI in onPostExecute().
I have two AsyncTasks running and the async task that is waiting for the result is just not getting the correct result.
I have a network class that runs like so:
public ArrayList<User> searchForFriends(String activeHash, TelephoneNumber telephone)
{
Object[] obj = {activeHash, telephone};
try
{
return new SearchForFriendsTelephone().execute(obj).get(Constants.TIMEOUT_TIME, Constants.TIMEOUT_UNIT);
}
catch (InterruptedException e)
{
return null;
}
catch (ExecutionException e)
{
return null;
}
catch (TimeoutException e)
{
return null;
}
}
private class SearchForFriendsTelephone extends AsyncTask<Object, Void, ArrayList<User>>
{
#Override
protected ArrayList<User> doInBackground(Object... searchTelephone)
{
if (config.getNetworkVersion() == config.NETWORK_PROTOCOL_VERSION_1)
{
TelephoneNumber tel = (TelephoneNumber) searchTelephone[1];
List<NameValuePair> params = new ArrayList<NameValuePair>(3);
params.add(new BasicNameValuePair(NetworkConfig.POST_ACTIVE_HASH, (String) searchTelephone[0]));
params.add(new BasicNameValuePair(NetworkConfig.POST_MOBILE_NUMBER_COUNTRY_CODE, tel.getCountryCode()));
params.add(new BasicNameValuePair(NetworkConfig.POST_MOBILE_NUMBER_RAW, tel.getNumberRaw()));
ServerCommunication csc = new ServerCommunication();
JSONObject jsonFoundFriends = csc.postToServer(config.getBaseUrl() + URL_FRIEND_SEARCH_MOBILE, params);
if (jsonFoundFriends == null || csc.networkError())
{
FriendNetworkCommunication.this.networkError = csc.getNetworkError();
return null;
}
return _processSearchFriends(jsonFoundFriends);
}
FriendNetworkCommunication.this.networkError = new NetworkError(NetworkLanguage.UNABLE_TO_PROCESS);
return null;
}
Anyway this works fine with no issues and pulls back the user/s. I know this as I tried the following code in the main ui thread and it populates a view just fine. When I call this code from another AsyncTask. I get a timeout error.
Code to all the searchForFriends code:
private class CompareNumbers extends AsyncTask<ArrayList<NameAndNumber>, Integer, Void>
{
#Override
protected Void doInBackground(ArrayList<NameAndNumber>... params)
{
for (NameAndNumber nameNumber : params[0])
{
try
{
FriendNetworkCommunication fnc = new FriendNetworkCommunication();
ArrayList<User> users = fnc.searchForFriends(CurrentUser.getInstance().getUserActiveHash(), new TelephoneNumber(String.valueOf(nameNumber.getNumber().getNationalNumber()), String.valueOf(nameNumber.getNumber().getCountryCode())));
if (users != null && users.size() == 1)
{
User u = users.get(0);
String[] s = nameNumber.getName().split(" ");
u.setFirstName(s[0]);
u.setLastName(s[1]);
((ArrayAdapter<User>) ((ListView) getView().findViewById(R.id.friend_add_fragment_search_cont_list)).getAdapter()).add(u);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
((ArrayAdapter<User>) ((ListView)getView().findViewById(R.id.friend_add_fragment_search_cont_list)).getAdapter()).notifyDataSetChanged();
return null;
}
}
Can I not run an asynctask that waits on another?
NOTE: This is all running in a fragment if this makes any difference?
NOTE2: The first Asynctask runs a network call and has to be run asynchronously and so I wanted it to be like this so if I wanted I could run it anywhere synchronously
try giving the .execute() of the second async task in the onpostexecute() of the first async task.
I have found the answer to my question and this is not possible.
A full answer can be found here:
Creating another AsyncTask inside of doInBackground