I am working on an Android app which will need to parse a fair amount of XML and/or JSON.
Every file takes 3-4 seconds to parse and this is done in a AsyncTask. The parsed data is then inserted into my SQlite database.
My problem is that the UI gets very slow and unresponsive during the parsing.
I have verified, using DDMS that almost all of the CPU is spend parsing and this happens in another (AsyncTask) thread.
I'm primarily testing on a galaxy nexus which is slow but has two cores. I therefore do not understand why I am experiencing this UI slowdown. I can still feel the slowdown on my Nexus 7 2013 but it is much less of an issue
Do you have any idea how of I could progress to fix find the cause of this issue? Shouldn't it be possible to have a heavy load on a AsyncTask without getting a laggy UI when having two cores available?
Code example
The first piece below initiates Volley and requests a number of XML files.
public static void start_update(final Context context,
Subscription subscription) {
if (updateConnectStatus(context) == NO_CONNECT)
return;
mContext = context;
RequestManager.initIfNeeded(context);
RequestQueue requestQueue = RequestManager.getRequestQueue();
Cursor subscriptionCursor = Subscription.allAsCursor(context
.getContentResolver());
while (subscriptionCursor.moveToNext()) {
Subscription sub = Subscription.getByCursor(subscriptionCursor);
if (subscription == null || sub.equals(subscription)) {
StringRequest jr = new StringRequest(sub.getUrl(),
new MyStringResponseListener(context
.getContentResolver(), sub),
createGetFailureListener());
int MY_SOCKET_TIMEOUT_MS = 300000;
DefaultRetryPolicy retryPolicy = new DefaultRetryPolicy(
MY_SOCKET_TIMEOUT_MS,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
jr.setRetryPolicy(retryPolicy);
// Add the request to Volley
requestQueue.add(jr);
processCounts.incrementAndGet();
}
}
requestQueue.start();
}
When Volley has fetced an XML file the following callback is called:
static class MyStringResponseListener implements Listener<String> {
static FeedHandler feedHandler = new FeedHandler();
Subscription subscription;
ContentResolver contentResolver;
final JSONFeedParserWrapper feedParser = null;
public MyStringResponseListener(ContentResolver contentResolver,
Subscription subscription) {
this.subscription = subscription;
this.contentResolver = contentResolver;
}
#Override
public void onResponse(String response) {
new ParseFeedTask().execute(response); // Execute the parsing as a AsyncTask
}
private class ParseFeedTask extends AsyncTask<String, Void, Void> {
protected Void doInBackground(String... responses) {
Subscription sub = null;
String response = responses[0];
try {
sub = feedHandler.parseFeed(contentResolver, subscription,
response.replace("", "")); // Remove the Byte Order Mark
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedFeedtypeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
protected void onPostExecute(Long result) {
decrementProcessCount();
}
}
}
It is common for Android devices to have poor storage I/O performance and if the main thread needs to access the drive for whatever reason things could get sluggish. The storage is the bottleneck, not the number of cores.
Use the profiler to see exactly what methods are slow. You may find that the UI is stuck loading data from the drive. For instance, it is common to load bitmaps on the UI thread and never notice the lag under normal conditions. In this sort of situation, move all I/O operations off the UI thread as you already have with your background data processor.
Related
I have this piece of an activity:
public class ResultActivity extends AppCompatActivity implements ResultListener {
private String code = "";
private String data = "";
#Override
protected void onCreate(Bundle savedInstanceState) {
try {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_result);
code = intent.getStringExtra("code");
data = intent.getStringExtra("data");
MyExternal.DecodeAndSend(this, code, data);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
Where MyExternal is a class in other library.
The method DecodeAndSend is something like this:
public static boolean DecodeAndSend(ResultListener caller, String codigo, String data)
{
try {
ExecutorService pool = Executors.newFixedThreadPool(1);
HashMap<String,String> arguments = new HashMap<>();
Future<String> resultado = pool.submit(new ServerConnection(caller, url, arguments));
String status = resultado.get();
if (status.equals("OK"))
caller.OnSuccess();
else
caller.OnError(status);
pool.shutdown();
return true;
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return false;
}
Finally, ServerConnection class implements Callable<String> so I show you the call method:
#Override
public String call() throws Exception {
Thread.sleep(2000);
return "OK";
}
The call to Thread.sleep(2000); is actually a call to a web server to send some data.
The problem is that the ResultActivity does not show its layout until the call call returns.
What is missing in this code?
DecodeAndSend is called from the main thread. It calls Future.get() which waits for the job to finish, so it's blocking the main thread. You should call this method from a background thread as well. I think it would be okay to send it to your same thread pool since it is submitted after the first job that it will wait for.
You cannot return anything about the request results from this method, because it is asynchronous.
public static void DecodeAndSend(ResultListener caller, String codigo, String data)
{
ExecutorService pool = Executors.newFixedThreadPool(1);
HashMap<String,String> arguments = new HashMap<>();
Future<String> resultado = pool.submit(new ServerConnection(caller, url, arguments));
pool.submit(new Runnable() {
public void run () {
try {
String status = resultado.get();
if (status.equals("OK"))
caller.OnSuccess();
else
caller.OnError(status);
pool.shutdown();
return;
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
caller.OnError(null); // No status, only an exception
});
}
However, your ServerConnection class already takes a caller parameter, so it should probably just handle the callback itself. And depending on what you're doing in the callback, you might want to post the callback calls to the main thread.
By the way, convention in Java is to always start method names with a lower-case letter (camel case).
Feature.get() is a blocking call. The UI Thread is blocked waiting for that call to return, hence can't take care of drawing your layout. Try passing the result listener to ResultListener to the ServerConnection and use the two callbacks to update your UI accordingly
Future.get() is a blocking call - execution stops until the result arrives
The result can only be retrieved using method get when the computation has completed, blocking if necessary until it is ready.
So your Activity's onCreate method calls that stuff, and then blocks until call (which is running on another thread) returns its result. So onCreate doesn't finish, and the layout doesn't complete.
If you want to use that blocking code, but after the view has laid out, I'd use another part of the Activity lifecycle like onStart (set a flag so you only run it once!). Otherwise you'll need to use some other concurrency technique to get your result and use it. It depends on what you're actually doing with the result of your call function
I am trying to develop an audio processing related app in android. I have one thread(not the UI thread) in which I am doing an operation. I need to update the result of the operation while it is still going on. For the same I am using a Handler. I am just using a Toast to display the result inside the handler. As of now my thread is continuing to run for the first time alone and after displaying the first result the thread doesn't run anymore because of which results are not updated. I just came to know that while modifying variables shared by this Thread and the UI, I need to synchronize both the threads. Am I correct? If so how can I achieve it?
Thanks!!
EDIT
I am posting a part of the method which is running in my thread and my handler.
while(fulldatabuffcnt+200<=fulldatabuffer.size())
{
double[] windowdata=new double[200];
classlabel=0;
//classlabel_new=0;
int windowcnt=0;
for (int h=fulldatabuffcnt;h<fulldatabuffcnt+200;h++)
{
windowdata[windowcnt]=fulldatabuffer.get(h);
windowcnt++;
}
MFCCcoeffs=mfcc_inst.getParameters(windowdata);
classlabel=likeli_ref.llhmain(MFCCcoeffs);
try {
out.writeInt(fulldatabuffer.size());
} catch (IOException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
classlabel_array[ecount]=classlabel;
ecount++;
if (ecount==25)
{
synchronized(SharedData.globalInstance) {
SharedData.globalInstance.classlabel_new =occurence(classlabel_array);//<--shared variable classlabel_new getting modified
}
try {
out_max.writeInt(SharedData.globalInstance.classlabel_new);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
ecount=0;
uiCallback.sendEmptyMessage(0);
}
fulldatabuffcnt=fulldatabuffcnt+80;
}
if(fulldatabuffcnt+200>fulldatabuffer.size()){
AppLog.logString("Setting calclating thread to null");
calculatingThread = null;
}
try {
out.close();
out_max.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
private Handler uiCallback = new Handler () {
public void handleMessage (Message msg) {
int label_handler;
synchronized(SharedData.globalInstance) {
label_handler=SharedData.globalInstance.classlabel_new;
}
Toast.makeText(MFCC2Activity.this, "Classified label" +label_handler, Toast.LENGTH_SHORT).show();//<--trying to access classlabel_new
}
};
Yes, you should synchronize to make sure that your UI thread doesn't access variables that are only partially set up by your own thread.
I suggest that you have a singleton object what contains all the variables/data etc that you need to pass between the two threads. For example, suppose you need to share a string and a double between your own thread and the UI thread. Create a class SharedData with a singleton, e.g.
class SharedData {
public String aString;
public double aDouble;
public static SharedData globalInstance = new SharedData();
}
Then in your own thread where you want to set the data
synchronized(SharedData.globalInstance) {
SharedData.globalInstance.aString = "some string";
SharedData.aDouble = 42.0;
}
and in your UI thread
String aString;
double aDouble;
synchronized(SharedData.globalInstance) {
aString = SharedData.globalInstance.aString;
aDouuble = SharedData.aDouble;
}
// do something with aString and aDouble
If you do it like that, then there won't be any problems relating to partially set data being read by the UI thread.
I am using the Drive API v2 for android, and when I execute the following method my app seems to pause or wait, and no data is returned.
public About getAbout() throws InterruptedException, ExecutionException {
FutureTask<About> future = new FutureTask<About>(new Callable<About>() {
public About call() throws IOException {
About about = null;
try {
about = _driveService.about().get().execute();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return about;
}
});
About about = future.get();
return about;
}
Does anyone have any idea what I'm doing wrong?
You are creating a FutureTask, but you are never executing it (not on the current thread and not on any other). Then you call future.get() which will block until the operation is completed. Since you never actually perform the operation, it will wait forever.
To execute an operation on a background thread you could for example use http://developer.android.com/reference/android/os/AsyncTask.html
I get a small problem: I need using async task in cocos2d-x on Android.
private void parseJSONJava() throws ClientProtocolException, IOException, JSONException
{
STAJSONParser jPars = new STAJSONParser();
jPars.makeHttpRequest(String.format("%s/app/%s/json",STA_URL,STA_APP_UID));
}
But this code crash application with error Can't create handler inside thread that has not called Looper.prepare(). I solve this by adding runOnUiThread:
me.runOnUiThread(new Runnable(){
public void run(){
STAJSONParser jPars = new STAJSONParser();
jPars.makeHttpRequest(String.format("%s/app/%s/json",STA_URL,STA_APP_UID));
}
});
Where "me" is my Activity.
Code from STAJSONParser:
public JSONObject makeHttpRequest(String url) {
AsyncGetJson Task= new AsyncGetJson(url);
try {
return Task.execute().get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
AsyncGetJson task its a simple AsyncTask that get JSON from server.
So, my question: is this solution is right/wrong? Or you can give me other solution?
I don't see why you couldn't do that. You could also use libcurl like m.ding mentioned, along with pthreads and a json parser. But the problem there is that you'd need to manage the pthreads yourself. It's "messier" than just doing it the way you're doing it now. Then again, using the JNI isn't exactly pretty either. It's one big giant trade-off, probably leaning in favor of the JNI & Android Java SDK.
On iOS and Android, pthreads are the underlying threading mechanism, which are already managed for you when you use things like iOS's NSOperation and Android's AsyncTask (I'm assuming..)
I have an application which runs a long task and returns a value. While the task is running, a ProgressDialog shows the progress. After the task is done I want to show the result in a TextView. I run the task in a FutureTask.
My problem is that if I try to get the result, the .get() method of FutureTask blocks the UI Thread and I don't see the ProgressDialog (the TextView displays the result propertly).
My code for the task (pool is ExecutorService):
final FutureTask<String> future = new FutureTask<String>(new Callable<String>() {
#Override
public String call() {
return myLongTask();
}
});
pool.execute(future);
And afterwards I call updateProgressBar() in a Runnable which updates the ProgressDialog with a Handler:
Runnable pb = new Runnable() {
public void run() {
myUpdateProgressBar();
}
};
pool.execute(pb);
Now I'm getting the result, which blocks the UI Thread preventing the ProgressDialog to show up:
String result = future.get()
If I try to put the result inside the updateProgressBar() method (by passing the future as a parameter) after the ProgressDialog dismisses, an Exception is thrown:
Only the original thread that created a view hierarchy can touch its views.
Any ideas how to solve this problem? (I've heard about AsyncTasks but I can't figure out how to use them propertly.)
You are correct that you need either an AsyncTask or a Thread/Handler combination in order to not block the UI.
Neither approach is that tricky, and there are some good guides around that lay out how to do them. Here are some links that I'd recommend.
AsyncTask
Painless threading
Threading
Designing for responsiveness
Thread documentation
Handler documentation
Yes, I had similiar problem when implementing ExecutorService, the following code block the UI thread and need to be run on a separated thread:
String result = future.get()
Just create a class extending AsyncTask to handle the future.get() method like the following code example:
private class FutureTask extends AsyncTask<Future<String>, Void, String>{
#Override
protected PhotoToView doInBackground(Future<String>... params) {
Future<String> f = params[0];
try {
return f.get(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String futureResult) {
super.onPostExecute(futureResult);
// this method is run on UI thread
// do something with the result
// or simply hide the progress bar
// if you had previously shown one.
}
}
And run the future thread with:
FutureTask ft = new FutureTask();
ft.execute(future);
Hope this helps.