Why this thread doesn't work?
i have an android app, with a actitity, that shows a map, with a position. This possition is updated on sharedPreferences with a service... ok, that works fine.
I need that my activity implements runnable to be a thread that read's the position of sharedPreferences each 1000ms and then update the item position on the map... but it doesn't works.
As i saw in debug mode, my thread doesn't work fine, ONLY ENTER ONE TIME on RUN() method... i mean that never enters into RUN() method again, only one time, cause this, the positions doesn't be updated on the map
What is wrong? why this thread doesn't work fine?
public class Locate extends MapActivity{
private TextView userText = null;
private TextView permissionText = null;
private TextView lastUpdateText = null;
private Button locateButton = null;
private Button traceButton = null;
private MapView mapView = null;
List<Overlay> mapOverlays = null;
double lat;
double lng;
GeoPoint p;
MapController mc;
Drawable drawable;
Drawable drawable2;
MyItemizedOverlay itemizedoverlay;
MyItemizedOverlay itemizedoverlay2;
//para almacenar la config local de mi app, mostrarme o no en el mapa...
static SharedPreferences settings;
static SharedPreferences.Editor configEditor;
private MyLooper mLooper;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.locate);
userText = (TextView) findViewById(R.id.User);
permissionText= (TextView) findViewById(R.id.Permission);
lastUpdateText= (TextView) findViewById(R.id.LastUpdate);
locateButton = (Button) findViewById(R.id.locate);
traceButton = (Button) findViewById(R.id.trace);
mapView = (MapView) findViewById(R.id.mapview);
mapView.setBuiltInZoomControls(true);
mapOverlays = mapView.getOverlays();
mc = mapView.getController();
settings=PreferenceManager.getDefaultSharedPreferences(this.getApplicationContext());
configEditor = settings.edit();
drawable = this.getResources().getDrawable(R.drawable.minifriend); // Icono de la cara, para posiciones de mis amigos
drawable2 = this.getResources().getDrawable(R.drawable.miniicon2); // Icono del programa, para mi posicion GPS
itemizedoverlay = new MyItemizedOverlay(drawable, this); // Aqui almaceno otras posiciones gps
itemizedoverlay2 = new MyItemizedOverlay(drawable2, this); // Aqui almaceno mi posicion gps
if (this.getIntent().getExtras() != null) /// si he recibido datos del friend en el Bundle
{
updateFriendPosition();
}
if (settings.getBoolean("showMeCheckBox", true))
{
updateMyPosition();
}
locateButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// Perform action on clicks
//itemizedoverlay2.getItem(0).
itemizedoverlay.clear();
updateFriendPosition();
itemizedoverlay2.clear();
updateMyPosition();
}
});
traceButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// Perform action on clicks
}
});
///////////////////////////////////////////////////////////////////////////////////////////
/// CODIGO PARA QUITAR EL FOCO DEL PRIMER TEXTEDIT Y QUE NO SALGA EL TECLADO DE ANDROID ///
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
///////////////////////////////////////////////////////////////////////////////////////////
//Thread thread = new Thread(this);
//thread.start();
mLooper = new MyLooper();
mLooper.start();
}
private void updateFriendPosition()
{
Bundle bundle = this.getIntent().getExtras();//get the intent & bundle passed by X
userText.setText(bundle.getString("user"));
permissionText.setText(bundle.getString("permission"));
lastUpdateText.setText(bundle.getString("lastupdate"));
String coordinates[] = {bundle.getString("lat"), bundle.getString("lon")};
lat = Double.parseDouble(coordinates[0]);
lng = Double.parseDouble(coordinates[1]);
p = new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6));
OverlayItem overlayitem1 = new OverlayItem(p, bundle.getString("user"), "Hi Friend!");
itemizedoverlay.addOverlay(overlayitem1);
mapOverlays.add(itemizedoverlay);// dibujo la estrella con la posicion actual del friend
mc.animateTo(p); ///nos centra el mapa en la posicion donde esta nuestro amigo
mc.setZoom(10); /// ajusta el zoom a 10
}
public void updateMyPosition()
{
String coordinates[] = {settings.getString("mylatitude", null),settings.getString("mylongitude", null)};
lat = Double.parseDouble(coordinates[0]);
lng = Double.parseDouble(coordinates[1]);
p = new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6));
OverlayItem overlayitem1 = new OverlayItem(p, "Me", "My Position");
itemizedoverlay2.addOverlay(overlayitem1);
mapOverlays.add(itemizedoverlay2);//dibujo mi icono para mostrarme a mi
}
@Override
protected boolean isRouteDisplayed() {
// TODO Auto-generated method stub
return false;
}
/////metodos del hilo
/*
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Boolean fallo=false;
itemizedoverlay2.clear();
updateMyPosition();
try{ Thread.sleep(1000);}
catch (Exception e)
{
fallo=true;
}
}
};
public void run()
{
Looper.prepare();
handler.sendEmptyMessage(0);
Looper.loop();
}
*/
private class MyLooper extends Thread {
public void run() {
while (mLooper==Thread.currentThread()) {
// Your code here
Boolean fallo=false;
itemizedoverlay2.clear();
updateMyPosition();
try{ Thread.sleep(5000);}
catch (Exception e)
{
fallo=true;
}
}
}
}
}
EXCEPTION WHEN I TRY TO SOLVE MY PROBLEM THE METHOD'S OF TOMS AND MARVINLABS ANSWERS:
11-18 17:37:43.489: ERROR/AndroidRuntime(729): Uncaught handler: thread main exiting due to uncaught exception
11-18 17:37:43.528: ERROR/AndroidRuntime(729): java.util.ConcurrentModificationException
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at java.util.AbstractList$SimpleListIterator.next(AbstractList.java:66)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at com.google.android.maps.OverlayBundle.draw(OverlayBundle.java:41)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at com.google.android.maps.MapView.onDraw(MapView.java:471)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.view.View.draw(View.java:5838)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.view.ViewGroup.drawChild(ViewGroup.java:1486)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1228)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.view.ViewGroup.drawChild(ViewGroup.java:1484)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1228)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.view.View.draw(View.java:5944)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.widget.FrameLayout.draw(FrameLayout.java:352)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): 开发者_开发百科 at android.view.ViewGroup.drawChild(ViewGroup.java:1486)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1228)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.view.View.draw(View.java:5841)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.widget.FrameLayout.draw(FrameLayout.java:352)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.view.ViewGroup.drawChild(ViewGroup.java:1486)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1228)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.view.ViewGroup.drawChild(ViewGroup.java:1484)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1228)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.view.View.draw(View.java:5841)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.widget.FrameLayout.draw(FrameLayout.java:352)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:1847)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.view.ViewRoot.draw(ViewRoot.java:1217)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.view.ViewRoot.performTraversals(ViewRoot.java:1030)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.view.ViewRoot.handleMessage(ViewRoot.java:1482)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.os.Handler.dispatchMessage(Handler.java:99)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.os.Looper.loop(Looper.java:123)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at android.app.ActivityThread.main(ActivityThread.java:3948)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at java.lang.reflect.Method.invokeNative(Native Method)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at java.lang.reflect.Method.invoke(Method.java:521)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:782)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:540)
11-18 17:37:43.528: ERROR/AndroidRuntime(729): at dalvik.system.NativeStart.main(Native Method)
Why are you touching UI objects (a map overlay) from a background thread? Don't do that, you're bound to get into trouble. If you really must have a background thread, use an AsyncTask and only touch UI objects from onPostExecute().
However, from a quick read of your code I suspect you don't need a thread at all. What's wrong with a simple Handler
on the UI thread which you simply postDelayed(5000)
to?
EDITED TO ADD:
Replace this line
private MyLooper mLooper;
With this:
private Handler mHandler;
private Runnable updateRunnable = new Runnable() {
@Override public void run() {
itemizedoverlay2.clear();
updateMyPosition();
queueRunnable();
}
};
private void queueRunnable() {
mHandler.postDelayed(updateRunnable, 5000);
}
Then remove these lines:
mLooper = new MyLooper();
mLooper.start();
and replace them with:
mHandler = new Handler();
queueRunnable();
Finally remove the MyLooper class you don't need.
Rather than having your activity implement Runnable, you should make a private class to your activity:
public class Locate extends MapActivity {
// ...
private MyLooper mLooper;
public void onCreate(Bundle savedInstanceState) {
//...
mLooper = new MyLooper(this);
mLooper.start();
}
public void onDestroy() {
// Don't forget to stop the thread here and release the reference
// held by the activity (else you will leak memory)
mLooper = null;
}
private class MyLooper extends Thread {
public void run() {
while (mLooper==Thread.currentThread()) {
// Your code here
}
}
}
}
I'd use MarvinLabs approach, but implement the while cycle in a different way:
public class Locate extends MapActivity {
// ...
private MyLooper mLooper;
public void onCreate(Bundle savedInstanceState) {
//...
mLooper = new MyLooper();
mLooper.start();
}
public void onDestroy() {
// Don't forget to stop the thread here and release the reference
// held by the activity (else you will leak memory)
mLooper = null;
}
private class MyLooper extends Thread {
public void run() {
while (true) {
// Your code here
//After executing your code, sleep for 1000ms
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
//Treat the exception
}
}
}
}
}
Also, instead of while(true) you can have a variable that would serve has a flag to inform the while cycle when to stop. That would make things easier when it's time to stop the thread.
That would be something along these lines: Modified to use Handler
public class Locate extends MapActivity {
// ...
private MyLooper mLooper;
public void onCreate(Bundle savedInstanceState) {
//...
mLooper = new MyLooper(new Handler());
mLooper.start();
//When it's time to stop
mLooper.setKeepRunningFalse();
}
public void onDestroy() {
// Don't forget to stop the thread here and release the reference
// held by the activity (else you will leak memory)
mLooper = null;
}
private class MyLooper extends Thread {
//Your flag
private boolean keepRunning;
private Handler locateHandler;
public MyLooper(Handler locateHandler)
{
this.locateHandler = locateHandler;
}
public void run() {
while (keepRunning) {
//When you want to execute some code that accesses Locate objects do this
locateHandler.post(new Runnable() {
@Override
public void run() {
//Your code here
//This code will be executed by the thread that owns the objects, thus not throwing an exception.
}
});
//After executing your code, sleep for 1000ms
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
//Treat the exception
}
}
}
//Method to change the flag to false
public void setKeepRunningFalse()
{
this.keepRunning = false;
}
}
}
It's crashing because you are calling the update My/Friends Positions from both an onClick and the timer. You need to do it just on the timer's handler. Here's the simplest timer/handler
In main activity have
Thread myRefreshThread = null;
in on Create have
this.myRefreshThread = new Thread(new TimerThread());
this.myRefreshThread.start();
Have inner class
class TimerThread implements Runnable {
// @Override
public void run() {
Message m = new Message();
m.what = YourMainClassName.UNIQUE_IDENTIFIER;
// define this as a constant in your manin class/activity
while (!Thread.currentThread().isInterrupted()) {
// Your processing
// You can put data in a Bundle and send the Bundle
// in the message with m.setData(Bundle b)
YourMainClassName.this.myUpdateHandler.sendMessage(m);
try {
Thread.sleep(1000);// sleeps 1 second
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
and the handler
Handler myUpdateHandler = new Handler() {
/** Gets called on every message that is received */
// @Override
public void handleMessage(Message msg) {
switch (msg.what) {
case YourMainClassName.UNIQUE_IDENTIFIER:
// get the data out of the bundle
// update the map overlay here and NOWHERE ELSE
break;
}
}
精彩评论