The first time the Asynctask executes takes 30 or more seconds. It happens when I enter the activity. After that, subsequents call to the Asyntask(when I enter the activity again from the previous activity) takes only 4 or 5 seconds, which I consider to be "acceptable".
Here is the code where I execute the AsyncTask
#Override
public void onLocationChanged(Location location) {
if (location!=null) {
mlastLocation=location;
double latitud=Double.parseDouble(getIntent().getStringExtra("latitud").replace("Latitud:", ""));
double longitud=Double.parseDouble(getIntent().getStringExtra("longitud").replace("Longitud:", ""));
LatLng origen= new LatLng(latitud,longitud);
LatLng destino=new LatLng(mlastLocation.getLatitude(),mlastLocation.getLongitude());
if (mCount==0) {
FillVariablesAsyncTask tareaAsincrona = new FillVariablesAsyncTask();
tareaAsincrona.execute(origen, destino);
mCount++;
}
}
}
And here the code of the AsyncTask, where onPostExecute updates members variables and update the UI.
private class FillVariablesAsyncTask extends AsyncTask<LatLng,Void,Document>{
#Override
protected Document doInBackground(LatLng... params) {
md=new GMapV2Direction();
LatLng origen=new LatLng(params[0].latitude,params[0].longitude);
LatLng destino=new LatLng(params[1].latitude,params[1].longitude);
Document doc = md.getDocument(origen, destino, GMapV2Direction.MODE_WALKING);
/*mUbicacionActual = md.getStartAddress(doc);
mDuration=md.getDurationText(doc);
mDistancia=md.getDistanceText(doc);*/
return doc;
}
#Override
protected void onPostExecute(Document doc) {
super.onPostExecute(doc);
mUbicacionActual = md.getStartAddress(doc);
mDuration=md.getDurationText(doc);
mDistancia=md.getDistanceText(doc);
if (mUbicacionActual!=null && mDistancia!=null && mDuration!=null) {
progressBar.setVisibility(View.GONE);
btnIr.setEnabled(true);
tvOrigenLatitud.setText("Latitud:"+String.valueOf(mlastLocation.getLatitude()));
tvOrigenLongitud.setText("Longitud"+String.valueOf(mlastLocation.getLongitude()));
tvDestino.setText("Destino:" + getIntent().getStringExtra("info").replace("Info:", ""));
tvDestinoLatitud.setText("Latitud:" + getIntent().getStringExtra("latitud").replace("Latitud:", ""));
tvDestinoLongitud.setText("Longitud:" + getIntent().getStringExtra("longitud").replace("Longitud:", ""));
tvOrigen.setText("Origen:" + mUbicacionActual);
tvDistancia.setText("Distancia:"+mDistancia);
tvTiempo.setText("Tiempo:" + mDuration);
}
}
}
I've tried out .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) but the app crashes. I've tried out also with a handler but it shows the same Behaviour, the first time takes 30 seconds or so, while the subsequents takes only a few.
Edit to show to points where i added log:
if (mCount==0) {
FillVariablesAsyncTask tareaAsincrona = new FillVariablesAsyncTask();
mStart=System.currentTimeMillis();
Log.i("START_BEFORE_EXECUTE", mStart + "");
tareaAsincrona.execute(origen, destino);
long end=System.currentTimeMillis();
Log.i("ELAPSED_EXECUTE", ((end-mStart)/1000) + "");
mCount++;
}
Here :
protected Document doInBackground(LatLng... params) {
long end;
end =System.currentTimeMillis();
Log.i("ELAPSE_PRE_BACKGROUND",((end- mStart)/1000)+"");
md=new GMapV2Direction();
end=System.currentTimeMillis();
Log.i("ELAPSED_POS_GMAPV2DIR",((end- mStart)/1000)+"");
LatLng origen=new LatLng(params[0].latitude,params[0].longitude);
LatLng destino=new LatLng(params[1].latitude,params[1].longitude);
end=System.currentTimeMillis();
Log.i("ELAPSED_PRE_GETDOCUMENT",((end- mStart)/1000)+"");
Document doc = md.getDocument(origen, destino, GMapV2Direction.MODE_WALKING);
end=System.currentTimeMillis();
Log.i("ELAPSED_POS_BACKGROUND",((end- mStart)/1000)+"");
/*mUbicacionActual = md.getStartAddress(doc);
mDuration=md.getDurationText(doc);
mDistancia=md.getDistanceText(doc);*/
return doc;
}
and here:
long end=System.currentTimeMillis();
Log.i("ELAPSED_onPostExecute",((end-mStart)/1000)+"");
mUbicacionActual = md.getStartAddress(doc);
mDuration=md.getDurationText(doc);
mDistancia=md.getDistanceText(doc);
end=System.currentTimeMillis();
Log.i("ELAPSED_POST_FILLVARS",((end-mStart)/1000)+"");
if (mUbicacionActual!=null && mDistancia!=null && mDuration!=null) {
progressBar.setVisibility(View.GONE);
btnIr.setEnabled(true);
tvOrigenLatitud.setText("Latitud:" + String.valueOf(mlastLocation.getLatitude()));
tvOrigenLongitud.setText("Longitud" + String.valueOf(mlastLocation.getLongitude()));
tvDestino.setText("Destino:" + getIntent().getStringExtra("info").replace("Info:", ""));
tvDestinoLatitud.setText("Latitud:" + getIntent().getStringExtra("latitud").replace("Latitud:", ""));
tvDestinoLongitud.setText("Longitud:" + getIntent().getStringExtra("longitud").replace("Longitud:", ""));
tvOrigen.setText("Origen:" + mUbicacionActual);
tvDistancia.setText("Distancia:"+mDistancia);
tvTiempo.setText("Tiempo:" + mDuration);
end=System.currentTimeMillis();
Log.i("ELAPSED_POS_onPostExecute", ((end - mStart) / 1000) + "");
}
this image shows the log output, it shows 3 but in reality is 30 or more.
[IMG]http://i61.tinypic.com/95o1ef.png[/IMG]
The time is taking have absolutely nothing to do with AsyncTask or Thread or Handler or Executor
The issue is that library you're using really takes that long to make calls to a server, or to process the information and I don't believe there's much you can do about it. It's only the 1st time because I'm guessing the library caches the result, but it still have nothing to do with the threading model you use.
The main point of my answer is that you're asking the wrong question. The correct question is:
Why this library is taking that long to process and what can you do to shorten it?
Unfortunately the only possible way to answer it is by analysing the source code of it or talking directly with the library developer.
edit:
to help you measure the executions:
public static class TimeMeasure {
private final DecimalFormat format;
private final double start;
private final String tag;
public TimeMeasure(String tag) {
this.format = new DecimalFormat("0.0");
this.start = System.currentTimeMillis();
this.tag = tag;
log("start);
}
public void log(String message) {
double elapsed = ((double) (System.currentTimeMillis() - start)) / 1000.0;
Log.d(tag, format.format(elapsed) + ": " + message);
}
}
then during doInBackground
protected Document doInBackground(LatLng... params) {
TimeMeasure tm = new TimeMeasure("Carlos");
// execute something:
tm.log("action 1");
// execute next
tm.log("action 2);
// etc...
}
I can't comment yet so here you go: you can use your IDE's debugger after you place breakpoints on every line in the two given blocks of code to see which line takes the most to execute. Then post your findings.
As the above answer mentioned it is actually the library that takes too much time.
I had a similar problem a while ago, I remember that after debugging what causes the problem was distance and duration and also if you were getting some kind of an address, it will contribute to the delay
(For Example) getting street name, city name and country etc..
I didn't really find any better API or library to deal with this but what i did was something to improve the user experience:
1-start another AsyncTask from onPostExecute() that will get duration and distance.
2- while the app is getting duration and distance don't block the user interface with a progress dialog or progress bar, just put loading... as an default value of your TextView then when the app receives the data setText to the actual value.
3-Do all of you data manipulation later after setting important data.
Hope this will help.
Related
I have developed an application to read KML files of point type and then update their elevation using Google Elevation API. As you can see it receives latitude and longitude of the point and appends it with an API key to retrieve the elevation. Because my KML files have multiple points, I've used ThreadPool to read lat and long of points, append it with the key, and send the URL to Google Elevation API.Something like this:
ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(CORE_NUMBERS + 1);
String providerURL = provider.getServiceURL();
String providerKey = provider.getServiceAPIkey();
for (PointFeature p: points) {
String coordinate = p.getLatitude() + "," + p.getLongitude(); // get latitude and longitude of the feature
String url = providerURL + "locations=" + coordinate + providerKey; // creating the url of web service which contains coordinate
HeightTask task = new HeightTask(url, p); // task of each points
executor.execute(task);
}
The heightTask class is where I parse the JSON result from API and get the elevation and set the heithUpdate flag. Here is the snippet:
public class HeightTask implements Runnable {
private String url;
private Feature feature;
public HeightTask(String url, Feature f) {
this.feature = f;
this.url = url;
}
#Override
public void run() {
if (feature instanceof PointFeature) {
float height = GoogleAPIJsonParser.parsePoint(HttpManager.getData(url));
if (height != Float.NaN){
feature.updateHeight(height);
feature.setHeightUpdated(true);
Log.d("elevationPoint",height+"");
}
}
}
}
What I need is a callback to know if the elevation of all points in a layer has been updated. Is there any pattern in threadPool or just loop through all points and check the hieghtUpdate flags?
Modify code as below.
Change your HeightTask to implement Callable interface.
Prepare a collection of Callable tasks and submit using invokeAll()
List<HeightTask > futureList = new ArrayList<HeightTask >();
for (PointFeature p: points) {
String coordinate = p.getLatitude() + "," + p.getLongitude(); // get latitude and longitude of the feature
String url = providerURL + "locations=" + coordinate + providerKey; // creating the url of web service which contains coordinate
HeightTask task = new HeightTask(url, p);
futureList.add(taks);
}
executor.invokeAll(futureList);
invokeAll:
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException
Executes the given tasks, returning a list of Futures holding their status and results when all complete. Future.isDone() is true for each element of the returned list. Note that a completed task could have terminated either normally or by throwing an exception.
I don't know why this count down counter shows a random number at the end?
I mean that it sometimes shows 60:15, sometimes 60:07, so on this way
min=sec=0;
new Thread(new Runnable() {
#Override
public void run() {
while (min < 60 && flagTime) {
try {
Thread.sleep(1);
G.HANDLER.post(new Runnable() {
#Override
public void run() {
String preSec="";
String preMin="";
if (sec < 59) {
sec += 1;
}
if (sec < 10) {
preSec = "0";
}
if (min < 10) {
preMin = "0";
}
score =preMin + min + ":"
+ preSec + sec;
txt[elementNumber + 1].setText(score);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
Please someone tell me why it works so weird?
Timing in ALL OSes is NOT precise, unless you use a framework or tools that is already designed for this task. You can however work with Thread.Sleep with a reasonable uncertainty. But for "reasonable" and "precise" timing, it depends on the problem you are trying to solve.
In threads, sleep(1000) does not mean that the thread will sleep exactly 1 second, so that the thread will sleep less or more every time you run your app. that is why you get random results.
This has many things else to consider like the priority of the thread.
so a better way to count down is to use other ways which is provided by android:
CountDownTimer
Timer
you may check on google and you will find many examples about how to use them.
those are more reliable and more precise.
hope this helps.
The likely reason that you're getting weird results in your TextView is that you're updating it from a thread that is not the main UI thread.
But you're making this harder than it needs to be. An easier approach would be to use a simple CountDownTimer object like this:
final long SECS_IN_1_MIN = 60;
final long MILLIS_IN_1_SEC = 1000;
final long MILLIS_IN_60_MINS = 3600000;
final TextView timer = (TextView) findViewById(R.id.timer);
new CountDownTimer(MILLIS_IN_60_MINS, MILLIS_IN_1_SEC) {
public void onTick(long millisUntilFinished) {
if (flagTime) {
long secs = (MILLIS_IN_60_MINS - millisUntilFinished) / MILLIS_IN_1_SEC;
timer.setText(String.format("%02d:%02d", secs / SECS_IN_1_MIN, secs % SECS_IN_1_MIN));
} else {
timer.setText("cancelled");
cancel();
}
}
public void onFinish() {
timer.setText("time expired");
}
}.start();
Edit: It uses a CountDownTimer to handle the timing, while using its millisUntilFinished value to calculate and display what appears to be an increasing seconds count. I threw in some symbolic names to make the code clearer and a String.format to handle single digit values in a more elegant fashion. Enjoy!
I have the following code in my main activity (Note: GPSTracker in this application works):
double latitude, longitude;
gps = new GPSTracker(MainActivity.this);
if(gps.canGetLocation()){
latitude = gps.getLatitude();
longitude = gps.getLongitude();
Toast.makeText(getApplicationContext(), "Your Location is - \nLat: " + latitude + "\nLong: " + longitude, Toast.LENGTH_LONG).show();
}
else{
gps.showSettingsAlert();
}
I want to create a loop, which would display in some time intervals Toast with my current position. I´ve tried this:
double latitude, longitude;
long currentTime = System.currentTimeMillis();
long myTimestamp = currentTime;
int i = 0;
gps = new GPSTracker(MainActivity.this);
while(i < 5)
{
myTimestamp = System.currentTimeMillis();
if((myTimestamp - currentTime) > 5000)
{
i++;
currentTime = System.currentTimeMillis();
if(gps.canGetLocation()){
latitude = gps.getLatitude();
longitude = gps.getLongitude();
Toast.makeText(getApplicationContext(), "Your Location is - \nLat: " + latitude + "\nLong: " + longitude, Toast.LENGTH_LONG).show();
}else{
gps.showSettingsAlert();
}
}
}
With this code, Toast is shown only one time (the last iteration). Could you help me with this? Thanks in advance.
I want it to be shown every iteration (for example every 5 seconds).
The code above doesn't loop every five seconds, it loops continuously but only increments your counter every five seconds... This is a very inefficient way of creating a time delay because nothing else can happen while the loop runs. (Even if you run this on a separate thread it is still not good tactic.)
Instead use LocationManager's requestLocationUpdates which will use callbacks so your app can do things between updates. A couple quick notes:
Understand that the GPS may not be able to get a fix every five seconds and that this interval is really short so use it sparingly or you'll run the battery down.
Some pre-Jelly Bean devices may not observe the minTime parameter, but you can enforce your time parameter yourself as I describe in Android Location Listener call very often.
All that aside, you use your existing code but I recommend a Handler and Runnable, like this:
handler.postDelayed(new Runnable() {
#Override
public void run() {
// Fetch your location here
// Run the code again in about 5 seconds
handler.postDelayed(this, 5000);
}
}, 5000);
One problem is that this method does a 'busy-wait', which, I suspect, prevents the toast from being displayed. Try doing a sleep() to wait until it's time for the next Toast:
public void sleepForMs(long sleepTimeMs) {
Date now = new Date();
Date wakeAt = new Date(now.getTime() + sleepTimeMs);
while (now.before(wakeAt)) {
try {
long msToSleep = wakeAt.getTime() - now.getTime();
Thread.sleep(msToSleep);
} catch (InterruptedException e) {
}
now = new Date();
}
}
I am trying to run AsyncTask every 1 minute, by using handler,but it doesn't work:(
In AsyncTask I am reading data from Sqlite DB and drawing result in mapview, aim of doing like that is another service updating my Sqlite every 1 min by taking real data from server and i want keep mapview updated too.
Is there any possible way of calling AsyncTask every minute rather than using Handler?
public Runnable getData;
private final Handler _handler = new Handler();
private static int DATA_INTERVAL = 60 * 1000;
getData = new Runnable()
{
public void run()
{
getDataFrame();
}
private void getDataFrame() {
// TODO Auto-generated method stub
_handler.postDelayed(MapViewActivity.this.getData, DATA_INTERVAL);
new DrawFromDataTask();
}
};
DrawFromDataTask is described below:
private class DrawFromDataTask extends AsyncTask<Void, Integer, FriendItemizedOverlay> {
#Override
protected FriendItemizedOverlay doInBackground(Void... params) {
// TODO Auto-generated method stub
mDbAdapter.open();
List<Refresher> nearFriends = mDbAdapter.getAllRecords();
for(Refresher friend : nearFriends)
{
double lat = Double.parseDouble(friend.Latitude);
double lon = Double.parseDouble(friend.Longitude);
OverlayItem item = new OverlayItem(new GeoPoint((int)(lat * 1000000),
(int)(lon* 1000000)),
"" + friend.name,
"" + friend.type);
mFriendOverlay.addOverlay(item);
}
return mFriendOverlay;
}
protected void onProgressUpdate(Integer... progress) {
setProgress(progress[0]);
}
protected void onPostExecute(FriendItemizedOverlay result) {
System.out.println("in AsyncTask execution!");
Location loc = get_location();
final double mLatitude = loc.getLatitude();
final double mLongitude = loc.getLongitude();
// get the last location from the database
GeoPoint lastLocation = new GeoPoint(
(int) (mLatitude * 1E6),
(int) (mLongitude * 1E6));
Drawable marker = getResources().getDrawable(R.drawable.arrow);
int amountOFplayers = result.size()-1;
for (int j=0; j<amountOFplayers; j++) {
result.getItem(amountOFplayers).setMarker(marker);
}
//System.out.println("Number of overlays -- "+amountOFplayers);
mMapView.postInvalidate();
//mMapView.invalidate();
// animate to last location
mMapController.animateTo(lastLocation);
// zoom to the required level
mMapController.setZoom(ZOOM_LEVEL);
}
}
Use the broadcast intent that runs on minute tick and then just execute the Async task. It is more accurate.
http://developer.android.com/reference/android/content/Intent.html#ACTION_TIME_TICK
Make sure you create new instance every time, since the thread object cannot be reused/relaunched unless you create new instance.
http://developer.android.com/guide/components/processes-and-threads.html
Just use a Timer, TimerTask and run in a fixed rate scheduleAtFixedRate()
Instead of using a service to poll the server every one minute, why dont you use Google Cloud Messaging for android(previously C2DM).. In that way you can send some notification to your app that yes data is changed on the server, which then leads you to change some data in your db if needed ,and then start the thread.
But yes I sincerely dont have any idea about your actual requirements
My problem is that I had a program working before, without threading, and it took a long time to process info (16 seconds to get XML data and display it). Now I've gotten the whole threading and async thing down, but for some reason it is making my program SLOWER (in the emulator, device is not available right now), is there anything I've done that could have caused this, the UI thread is fine, but my async thread takes a minute and a half to execute. (when I had it in the UI thread used to take only 16 seconds, but froze the UI thread)
Here is the code for my thread, it sits inside of the main class:
private class TeamSearchTask extends AsyncTask<String,Void,String> {
CharSequence nfo;
String [] matches;
String [] data;
String teamNum;
ProgressDialog loading;
protected void onPreExecute()
{
//Show the 'loading' dialog
loading = new ProgressDialog(SapphireAlliance.this);
loading.setMessage("Loading, please wait...");
loading.show();
}
protected String doInBackground(String... teamNumber)
{
try
{
//Team information ------------------------------------------------------------------------------------
teamNum = teamNumber[0];
//Array of team data
data = APIconnection.getTeams(teamNum, "");
//Display basic team info
nfo = ("\nFormal Team Name:\n" + data[1] +
"\n\nLocation:\n" + data [3] + ", " + data[4] + ", " + data[5] +
"\n\nRookie Year:\n" + data[6] +
"\n\nRobot Name:\n" + data[7] +
"\n\nWebsite:\n" + data[8] + "\n\n\n\n\n\n\n\n\n");
//Make match archive --------------------------------------------------------------------------------------
String [] events = APIconnection.getEventIdsByYear(year1);
ArrayList<String> matches = new ArrayList<String>();
for (int i = 0; i<events.length; i++)
{
String [] add = APIconnection.getMatches2(teamNum, events[i] ,"","");
for(int j = 0; j<add.length; j++)
matches.add(add[j]);
}
String [] out = new String [matches.size()];
matches.toArray(out);
return "";
}
catch(Exception e)
{
return e.toString();
}
}
protected void onPostExecute(String result) {
if(result.equals(""))
{
info.setText(nfo);
matchArchive(matches);
//title
CharSequence ttl = "Team " + teamNum;
titlets.setText(ttl.toString());
loading.dismiss();
}
else
{
alert.setMessage(result);
alert.show();
}
}
}
Anything in there that could be causing this? :|
It may be that your thread priority may not be set very high. I remember the default is rather low. However, for what you're doing, it shouldn't be taking more than a couple seconds. I would think that the real problem lies in APIconnection going to the network and doing something that takes a long time. Particularly, that for loop that does the event fetching would be doing a lot of work if each call opens a new socket, assuming that this is for FIRST matches. :P
I am having the same issue, doing nothing more than a file copy with simple DES decryption... the whole thing ran in a few seconds on the UI thread, but when moved into an ASYNCTASK it is now taking MINUTES to accomplish. Unbelievable.