【问题标题】:Why this thread doesn't work?为什么这个线程不起作用?
【发布时间】:2010-11-18 16:24:03
【问题描述】:

我有一个带有活动的 android 应用程序,它显示了一个带有位置的地图。这个位置在 sharedPreferences 上使用服务进行了更新...好的,效果很好。

我需要我的活动实现 runnable 成为一个线程,每 1000 毫秒读取一次 sharedPreferences 的位置,然后更新地图上的项目位置......但它不起作用。

正如我在调试模式下看到的,我的线程不能正常工作,只在 RUN() 方法上输入一次...我的意思是永远不会再进入 RUN() 方法,只有一次,导致这个,地图上的位置不会更新

怎么了?为什么这个线程不能正常工作?

公共类定位扩展 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;         
            }
        }
    }


}

}

当我尝试解决我的问题时,TOMS 和 MARVINLABS 答案的方法除外:

    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)

【问题讨论】:

    标签: android multithreading


    【解决方案1】:

    为什么要从后台线程触摸 UI 对象(地图叠加层)?不要那样做,你一定会惹上麻烦的。如果您确实必须有一个后台线程,请使用 AsyncTask 并仅从 onPostExecute() 触摸 UI 对象。

    但是,通过快速阅读您的代码,我怀疑您根本不需要线程。 UI 线程上的简单 Handler 有什么问题,而您只是 postDelayed(5000) 到?

    编辑添加:

    替换这一行

    private MyLooper mLooper;
    

    有了这个:

    private Handler mHandler;
    private Runnable updateRunnable = new Runnable() {
        @Override public void run() {
            itemizedoverlay2.clear();
            updateMyPosition();
            queueRunnable();
        }
    };
    private void queueRunnable() {
        mHandler.postDelayed(updateRunnable, 5000);
    }
    

    然后删除这些行:

    mLooper = new MyLooper();
    mLooper.start();
    

    并将它们替换为:

    mHandler = new Handler();
    queueRunnable();
    

    最后删除不需要的 MyLooper 类。

    【讨论】:

    • 您能给我一个代码示例,说明如何在没有线程的情况下执行此操作吗?我仍然可以按下按钮并与此 ui 进行交互?
    • 而且我正在检索信息以从 SharedPreferences 更新我的地图,但我正在此 ui 上更新地图,而不是在其他 ui 上
    • 而且这个用户界面还有其他按钮和其他功能
    • 是的。我必须抽出 10 分钟,但当我回来时,我会用代码更新我的答案。
    • 它工作!!!!!!不会让我崩溃!!!!!!谢了哥们!!!!!! WOOOW,但现在我有另一个问题,只有当我移动或缩放地图时,项目的新位置才会在地图上重新绘制:S 我该如何解决?
    【解决方案2】:

    您应该为您的 Activity 创建一个私有类,而不是让您的 Activity 实现 Runnable:

    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
                }
            }
        }
    }
    

    【讨论】:

    • 我在这里得到一个错误:mLooper = new MyLooper(this);错误:构造函数 Locate.MyLooper(Locate) 未定义
    • 另外,如果找到 implementews Runnable,我必须执行 run... 你确定你的代码没问题吗?
    • 我修复了您的代码,删除了第一行的可运行实现,并将其从新的 Mylooper 行中删除...并且不起作用...相同的发生,线程仅执行 Run 方法一次
    • 抱歉,您的活动不再实现 Runnable(代码已更正)。如果你想传递一个 Context 或一个 Activity(代码更正),你还需要为 MyLooper 定义一个构造函数。
    • 我需要哪种处理程序?安卓系统?或 java.util ?
    【解决方案3】:

    我会使用 MarvinLabs 方法,但以不同的方式实现 while 循环:

    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
                    }
                }
            }
        }
    
    }
    

    此外,您可以使用一个变量来代替 while(true),该变量具有一个标志来通知 while 循环何时停止。当需要停止线程时,这会让事情变得更容易。

    大概是这样的: 修改为使用 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;
                }
        }
    
    
    }
    

    【讨论】:

    • 嗨,伙计,这种方式,就像 MarvinLabs 的方式一样,给了我一个异常并让我的应用程序崩溃,你在第一个问题上有 logcat,请阅读它
    • 您是否尝试过将整个 onCreate 和 run 方法放在 try catch 块中?就是看看异常是否被捕获,是什么异常。
    • 第一个问题是例外,请阅读。当我移动地图或放大地图时会发生这种情况,这只发生在这种线程上......之前不会发生在我身上
    • 正如我在 google 中所读到的:当这种修改是不允许的时,检测到对象的并发修改的方法可能会抛出此异常。
    • 我睡了 5000 毫秒,它似乎工作了 10 或 15 秒,但随后又崩溃了....
    【解决方案4】:

    它崩溃了,因为您正在从 onClick 和计时器调用更新我/朋友的位置。您只需要在计时器的处理程序上执行此操作。这是最简单的计时器/处理程序

    主要活动有

    Thread myRefreshThread = null;
    

    在创建有

    this.myRefreshThread = new Thread(new TimerThread());
    this.myRefreshThread.start();
    

    有内部类

      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();
                }
    
             }
          }
    
       }
    

    和处理程序

     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;
             }
          }
    

    【讨论】:

    • P.S.可以通过首选项将数据从服务传递到应用程序,但这不是最好的方法。如果它是远程服务,您可以通过aidl 和远程接口发送它。如果它是本地服务,您可能可以以更简单的方式进行操作,但我只使用过远程服务,所以不确定如何
    • 我从不使用onclick,因为我从不按下按钮,所以它不会成为崩溃的原因......
    猜你喜欢
    • 1970-01-01
    • 2019-04-09
    • 2013-01-26
    • 2016-04-20
    • 2011-11-14
    • 1970-01-01
    • 2015-05-21
    • 2022-01-17
    相关资源
    最近更新 更多