How can I store data from OnPostExecute()? - java

I am trying to get the id numbers of foods from the USDA database. My issue is that I can get the JSON data, but I can not take it out of the OnPostCreate() function. When I iterate through JSONdataWIthIDs in the OnPostCreate() all the data is there. But when I try to use the data someplace else, it is empty. How do I store the data into an array?
Here is the constructor.
public USDA(String nameOfFoodToSearchFor) {
setJSONdataWIthIDs(nameOfFoodToSearchFor);
// This returns 0
Log.i("TAG", "SONdataWIthIDs size: " + JSONdataWIthIDs.size());
// This does not work.
for(String s: JSONdataWIthIDs)
{
Log.i("TAG OUTPUT", s);
}
}
Here is the code with the OnPostCreate.
private void setJSONdataWIthIDs(String nameOfFood) {
final String url1 = "https://api.nal.usda.gov/ndb/search/?format=json&q=" + nameOfFood + "&sort=n&max=25&offset=0&api_key=x5qM9v8PkjZrTf2cVSHzoK7y4GsSBgoQEmJsbwqV";
AsyncTask asyncTask = new AsyncTask() {
#Override
protected Object doInBackground(Object[] objects) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url1)
.build();
Response response = null;
try {
response = client.newCall(request).execute();
return response.body().string();
}
catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Object o) {
String lines[] = o.toString().split("\\r?\\n");
for(int i = 0; i < lines.length; i++)
{
JSONdataWIthIDs.add(lines[i]);
}
// This works
for(String s : JSONdataWIthIDs)
{
Log.i("TAG", s);
}
}
}.execute();
}

The problem is quite simple. The Log.i("TAG", "SONdataWIthIDs size: " + JSONdataWIthIDs.size()); statement is executed before the onPostExecute(). So the JSONdataWIthIDs is empty.
The call to async task is done asynchronously (doInBackground()). And you are saving the value to JSONdataWIthIDs in onPostExecute(). So you can access the id's only after the async task is done.
How do I store the data into an array?
The data is already stored into the array in onPostExecute(). The only problem is that you can access it only after the onPostExecute() is called.

onPostExecute is where the execution flow comes back from the worker thread (that is what supports AsyncTask) to the UI thread. Your log line,
Log.i("TAG", "SONdataWIthIDs size: " + JSONdataWIthIDs.size());
happens in the UI thread and is running in parallel with the working thread at that moment. So the result is not available.

Related

How socket.io get response with synchronous call on android?

I'm writing a android chat application with socket.io-client-java.I want to check whether the client user exist at first.So I need to send a command like "user/exist" to server url and get the response from server.I need to wait the server response then can go to next step.But the socket.io use the asynchronous callback.For getting the response synchronous I known the Furture and Callable only.So I tried the way using code as below:
//this is request method using socket.io
public JSONObject request(final String method,final String url,final JSONObject data){
final JSONObject responseObj = new JSONObject();
if (mSocket.connected()) {
mSocket.emit(method, reqObj, new Ack() {
#Override
public void call(Object... objects) {
System.out.println("get Ack");
try {
responseObj.put("body", (JSONObject) objects[0]);
}catch (JSONException e){
e.printStackTrace();
}
}
})
}
}
//this is Callable call implement
#Override
public JSONObject call(){
return request("get","https://my-chat-server/user/exist",new JSONObject());
}
//this is call method in activity
ExecutorService executor = Executors.newCachedThreadPool();
Future<JSONObject> response = executor.submit(mApiSocket);
executor.shutdown();
JSONObject respObj = new JSONObject();
JSONObject respBody = new JSONObject();
try {
respObj = response.get();
respBody = respObj.getJSONObject("body");
}catch (ExecutionException e){
}catch(InterruptedException e1){
}catch(JSONException e2){
}
But it dose not work.The respObj is null.
How can i get the reponse synchronous?
I am a green hand on java and forgive my poor chinese english.
Any help would be appreciated!
I known the js can use Promise and await like below:
//request method
static request(method, url, data) {
return new Promise((resolve, reject) => {
this.socket.emit(method,
{
url: url,
method,
data,
},
async (res) => {
if (res.statusCode == 100) {
resolve(res.body, res);
} else {
throw new Error(`${res.statusCode} error: ${res.body}`);
reject(res.body, res);
}
}
)
})
}
//call method
response = await mSocket.request('get','https://my-chat-server/user/exist', {
first_name: 'xu',
last_name: 'zhitong',
});
I'm not sure this is the best way but we can wait for the callback as follows:
#Nullable
Object[] emitAndWaitForAck(#NotNull String event, #Nullable Object[] args,
long timeoutMillis) {
Object[][] response = new Object[1][1];
Semaphore lock = new Semaphore(0);
socketClient.emit(event, args, ackArgs -> {
response[0] = ackArgs;
lock.release();
});
try {
boolean acquired = lock.tryAcquire(timeoutMillis, TimeUnit.MILLISECONDS);
if (acquired) {
return response[0];
}
} catch (InterruptedException ignored) {
}
return null;
}
Assuming your socket.io server returns one argument containing the body (or null) you would call it something like this:
String method = "get";
String url = "https://my-chat-server/user/exist";
long timeoutMillis = 5000;
Object[] args = emitAndWaitForAck(method, new String[]{url}, timeoutMillis);
JSONObject response = (JSONObject) args[0];

keep data obtained in onResponse method available throughout the class

Basically in my android app I want user to search cities around the world, thus I am using an api to get all the cities of the world and storing in an ArrayList, this has been done in the onResponse method of okhttp library and after that the list becomes empty. This array list holds values only in onResponse but I want to use it in my entire class after the execution. Can anyone give me any ideas on that? Here is the code.
onCreate(){
OkHttpClient client = new OkHttpClient();
final Request request = new Request.Builder()
.url("https://raw.githubusercontent.com/David-Haim/CountriesToCitiesJSON/master/countriesToCities.json")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
}
#Override
public void onResponse(Response response) throws IOException {
try {
fullObject = new JSONObject(response.body().string());
JSONArray s = fullObject.names();
for(int i=0; i<s.length(); i++) {
JSONArray citiesOfOneCoutry = null;
citiesOfOneCoutry = fullObject.getJSONArray(s.getString(i));
for(int j=0; j<citiesOfOneCoutry.length();j++) {
allCities.add(citiesOfOneCoutry.getString(j));
}
Log.d(TAG, "onResponse: in for "+allCities.size());
}
Log.d(TAG, "onResponse: outside for "+allCities.size()); //gives full size.
} catch (JSONException e) {
e.printStackTrace();
}
Log.d(TAG, "onResponse: outside try "+allCities.size()); //gives full size
}
});
Log.d(TAG, "outside response inside oncreate"+allCities.size()); //gives 0
}
I see in the logs that message from outside onResponse one is first and then the callback is getting executed. that is quite understandable but I want trick to get this ArrayList after response execution.
That is the nature of asynchronous operations, they don't complete in the order you wrote them. allCities data will not be available in your onCreate method because it hasn't had a chance to execute yet. The trick to using it outside of onResponse is to move the code that relies on the response to its own method.
private void updateUI() {
// Your code that relies on 'allCities'
}
and then in onResponse, call updateUI (or whatever you call it) after you populate allCities --
#Override
public void onResponse(Response response) throws IOException {
try {
fullObject = new JSONObject(response.body().string());
JSONArray s = fullObject.names();
for(int i=0; i<s.length(); i++) {
JSONArray citiesOfOneCoutry = null;
citiesOfOneCoutry = fullObject.getJSONArray(s.getString(i));
for(int j=0; j<citiesOfOneCoutry.length();j++) {
allCities.add(citiesOfOneCoutry.getString(j));
}
Log.d(TAG, "onResponse: in for "+allCities.size());
}
Log.d(TAG, "onResponse: outside for "+allCities.size()); //gives full size.
} catch (JSONException e) {
e.printStackTrace();
}
Log.d(TAG, "onResponse: outside try "+allCities.size()); //gives full size
updateUI();
}

how to stop asyncTask android

In my android project I use many AsynTask in one activity. I need to stop one AsynTask when I start other.
I'm using myAsynTask.cancel(true); in my android project. But it does stop the AsynTask.
protected String doInBackground(String... args) {
HashMap<String, String> params = new HashMap<>();
params.put("id", args[0]);
Log.d("get value: ", params.toString());
JSONObject json = jParser.makeHttpRequest(url_comment, "GET", params);
Log.d("All matches: ", json.toString());
if(isCancelled()) {
finish();
}
else {
try {
int success = json.getInt(TAG_SUCCESS);
if (success == 1) {
JSONmatches = json.getJSONArray(TAG_vedio);
for (int i = 0; i < JSONmatches.length(); i++) {
JSONObject c = JSONmatches.getJSONObject(i);
String title = c.getString(TAG_title);
String url = c.getString(TAG_url);
HashMap<String, String> map = new HashMap<String, String>();
map.put(TAG_title, title);
map.put(TAG_url, url);
arrayList22.add(map);
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
return null;
}
You actively have to check for isCancelled while executing your loop in doInBackground.
You should break the loop if isCanceled is true.
From the official Android documentation:
A task can be cancelled at any time by invoking cancel(boolean).
Invoking this method will cause subsequent calls to isCancelled() to
return true. After invoking this method, onCancelled(Object), instead
of onPostExecute(Object) will be invoked after
doInBackground(Object[]) returns. To ensure that a task is cancelled
as quickly as possible, you should always check the return value of
isCancelled() periodically from doInBackground(Object[]), if
possible (inside a loop for instance.)

random com.android.volley.NoConnection error, java.io.InterruptedIOException, statuscode=0

I have a native android app using volley framework to fetch data from a PHP server end script.
It worked well on most time, but I have 20% percentage failure.
The error says:
com.android.volley.NoConnection, java.io.InterruptedIOException.
I debugged that I found the statuscode = 0, which obviously was wrong.
I have no idea what can be the reason? Since it is working most time so there should be no obvious error code.
FYI, those PHP script on the server end works very well for my IOS app.
Please allow me post my code here:
retryConnBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
txtOut.append("\n");
txtOut.append("Button with Retry Click");
Log.d("Click", "Button Click");
final String url = "https://www.myserver.com/api/getBalanceInfoTest?token=7ff3317a4f3dc07d0c297a7d16d2049c&t=" + System.currentTimeMillis();
//final String url = "http://192.168.1.23/base/test/";
JsonObjectRequest getRequest = new JsonObjectRequest(Request.Method.GET, url, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
txtOut.append("\n");
txtOut.append("Result with Retry:");
txtOut.append(response.toString());
Log.d("Response", response.toString());
VolleyLog.e("Response:", response.toString());
}
},
new Response.ErrorListener(){
#Override
public void onErrorResponse(VolleyError error) {
txtOut.append("\n");
txtOut.append("Error with Retry:");
txtOut.append(error.toString());
Log.d("Error.Response", error.toString());
VolleyLog.e("Error:", error.getMessage());
}
});
getRequest.setRetryPolicy(new DefaultRetryPolicy(5000, 5, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
queue.add(getRequest);
queue.start();
}
});
}
And for more information, the output of my PHP script is:
{"hsaBalance":"1000.00"}, created by Json_encode() function of PHP.
I have fixed this bug.
It is not a network issue.
queue.add(getRequest);
queue.start();
should be
queue.add(getRequest);
So the key is we should remove queue.start().
Michael Cheng is right,because volley had start the RequestQueue when we call newRequestQueue as below:
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
and when we call start, volley will call stop to “make sure any currently running dispatchers are stopped”,in stop method volley does this below:
public void stop() {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
for (int i = 0; i < mDispatchers.length; i++) {
if (mDispatchers[i] != null) {
mDispatchers[i].quit();
}
}
}
and the quit method does this below:
public void quit() {
mQuit = true;
interrupt();
}
maybe you can see the reason,why interrupted.
More, interrupt method does this below:
public void interrupt() {
// Interrupt this thread before running actions so that other
// threads that observe the interrupt as a result of an action
// will see that this thread is in the interrupted state.
nativeInterrupt();
synchronized (interruptActions) {
for (int i = interruptActions.size() - 1; i >= 0; i--) {
interruptActions.get(i).run();
}
}
}
the reason maybe this as metioned above:
Interrupt this thread before running actions so that other threads that observe the interrupt as a result of an action will see that this thread is in the interrupted state.
You are having sometimes problems with your connection. Look at InterruptedIOException API:
InterruptedIOException Signals that an I/O operation has been interrupted. An InterruptedIOException is thrown to indicate that an input or output transfer has been terminated because the thread performing it was interrupted.
so only you can do is to catch the possible exceptions occuring when converting JSon and having a workaround for this.
// rest of your code...
final String url = "https://www.myserver.com/api/getBalanceInfoTest?token=7ff3317a4f3dc07d0c297a7d16d2049c&t=" + System.currentTimeMillis();
try {
JsonObjectRequest getRequest = new JsonObjectRequest(Request.Method.GET, url, null,
// rest of your code...
queue.add(getRequest);
queue.start();
} catch (InterruptedIOException e) {
// do something when fail print error, show a toast
System.out.err("Error, connection interrupted" + e.getMessage());
Toast.makeText(getApplicationContext(), "press button again", Toast.LENGTH_LONG).show();
}

Android how to avoid i/o problems when periodically writing to a file on internal memory

I'm writing an app which records various sensor (location for one) and device specific meta data to a file and then transmits the file to a server. It is not a background service - the app only needs to write and transmit files while the app is active (no need to set alarms to wake up a service). I'd like to write a row to the file every time onLocationChanged() is called (though location data is not the only data being written) - or at least at a similar rate at which onLocationChanged() is called. onLocationChanged() is currently being called once/second, but we may end up recording data at a higher rate (possibly 2-3x/second). That seems like a fair amount of i/o to the internal memory.
I currently have everything working (proof of concept), but I need to improve on the methods I'm using to write to the file. I'm writing a row to the file each time onLocationChanged() is called, which is probably not wise and it seems is causing i/o to stack up. I've read other similar questions which touch on various methods (new threads, alarms, timer tasks, handlers, etc.), but I couldn't locate answers that were specific to what I'm trying to do. I've also considered other methods like caching/buffering data and only writing to internal storage on a less frequent basis (every 10 seconds?), or possibly writing to a SQLite db and exporting to a file later. What can I do to best uncouple (assuming that's what I need to do) the file code from the sensor code and ensure timely updates to the file? I also need to ensure that all data gets written to the file.
UPDATE:
The solution I ended up using involves appending to a StringBuilder for a set number of rows (one row per call to onLocationChanged()). After appending 10 rows worth of data, effectively buffering, I'm handing off the StringBuilder to an AsyncTask, where the data is written to file.
Have been in a somewhat similar situation during a pervasive positioning course.
Here is what we did:
FileWriter
The writing to file part was not an issue for us as we only collected GPS locations and not other sensor data. For us an implementation like below was sufficient. It is not clear if you have to write the data to the same file, if not, then you should be able to use it directly.
public class FileOutputWriter {
private static String pathString = "";
private static String sensorString = "";
public static void setPath(Context context, String path) {
pathString = path;
File pathFile = new File(pathString);
pathFile.mkdirs();
}
public static void writeData(String data, String sensor, boolean append) {
File file = new File(pathString + "/" + sensor+ ".log");
long timeStamp = System.currentTimeMillis();
BufferedWriter out = null;
try {
out = new BufferedWriter(new FileWriter(file, append));
out.write(timeStamp + ":" + data);
out.newLine();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Uploader
To upload data to the server we create a cue of LogEntries (change this to your own dataholder object or simply String).
public class DataLogger {
static Vector<LogEntry> log = new Vector<LogEntry>();
public static void addEnty(LogEntry entry) {
Log.d("DEBUG", entry.getStrategy() + " added position to logger " + entry.getLocation());
log.add(entry);
}
public static Vector<LogEntry> getLog() {
return log;
}
public static void clear() {
log.clear();
}
}
Notice that Vector is thread-safe.
Finally we implemented a UploaderThread, responsible for periodically inspecting the DataLogger cue and upload added entries.
public class UploaderThread extends Thread {
public static LinkedList<String> serverLog = new LinkedList<String>();
Boolean stop = false;
Context c;
public UploaderThread(Context c) {
this.c = c;
}
public void pleaseStop() {
stop = true;
}
#Override
public void run() {
while(!stop) {
try {
if(DataLogger.log.size() > 0 && Device.isOnline(c)) {
while(DataLogger.log.size() > 0) {
LogEntry logEntry = DataLogger.getLog().get(0);
String result = upload(logEntry);
serverLog.add("("+DataLogger.log.size()+")"+"ServerResponse: "+result);
if(result != null && result.equals("OK")) {
DataLogger.getLog().remove(0);
} else {
Thread.sleep(1000);
}
}
} else {
serverLog.add("Queue size = ("+DataLogger.log.size()+") + deviceIsonline: "+Device.isOnline(c));
}
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private String upload(LogEntry entry) {
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://yoururl/commit.php");
try {
// Add your data
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("tracename",sessionName +"-"+ entry.getStrategy().toString()));
nameValuePairs.add(new BasicNameValuePair("latitude", Double.toString(entry.getLocation().getLatitude())));
nameValuePairs.add(new BasicNameValuePair("longitude", Double.toString(entry.getLocation().getLongitude())));
nameValuePairs.add(new BasicNameValuePair("timestamp", Long.toString(entry.getTimestamp())));
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
// Execute HTTP Post Request
HttpResponse response = httpclient.execute(httppost);
if(response != null) {
InputStream in = response.getEntity().getContent();
String responseContent = inputStreamToString(in);
return responseContent;
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private String inputStreamToString(InputStream is) throws IOException {
String line = "";
StringBuilder total = new StringBuilder();
// Wrap a BufferedReader around the InputStream
BufferedReader rd = new BufferedReader(new InputStreamReader(is));
// Read response until the end
while ((line = rd.readLine()) != null) {
total.append(line);
}
// Return full string
return total.toString();
}
}
The thread is simply started in the first activity of your app:
UploaderThread ut;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FileOutputWriter.setPath(this, Environment.getExternalStorageDirectory()
.getAbsolutePath());
ut = new UploaderThread(this);
ut.start();
}
#Override
protected void onDestroy() {
super.onDestroy();
ut.pleaseStop();
}
Hope this gets you on the way
The recommendation is to do all file I/O on a separate thread. You can set up a producer/consumer structure, where the producer is the UI thread that generates write requests to a queue and the consumer is a worker thread that wakes up when a write request is queued and updates the file/data base/whatever.
This will work unless your definition of "timely updates" is rather strict. However, since you are considering queueing things for 10 seconds, my guess is that this is not an issue.
EDIT:
To deal with the queue possibly backing up, you should arrange for the consumer to process everything on the queue whenever it wakes up. If the I/O simply cannot keep up, even with batch processing like that, you could arrange for the queue to drop elements once it hits a maximum size. You might also need to reduce the frequency at which onLocationChanged() is called.

Categories

Resources