Android Thread.start()CalledFromWrongThreadException

时间:2014-03-28 13:38:38

标签: java android multithreading

我不确定我的提示是否正确,因为我没有得到预期的输出。我有一个类,我在其中调用一个应该启动一个线程的方法。

public class MainActivity extends Activity {

protected void onCreate(Bundle savedInstanceState) {
beginListenForData()
}

beginListenForData函数用于启动一个线程并检查数据是否有数据供读取。如果是这种情况,它会读取并更新UI变量:

void beginListenForData()
{
    Thread workerThread = new Thread(new Runnable() {
        @Override
        public void run()
        {    
            int bytesAvailable = 3;
            while(!Thread.currentThread().isInterrupted())
            {
                try
                {
                    bytesAvailable = mmInStream.available();
                    if(bytesAvailable > 0)
                    {
                        byte[] packetBytes = new byte[bytesAvailable];
                        mmInStream.read(packetBytes);
                bytesAvailable = mmInStream.available();
                String s = new String(packetBytes);
                text.setText(s);
                    }
                }
                catch (Exception e)
                {
            // TODO Auto-generated catch block
                e.printStackTrace();
                }
            }
        }
    });

       workerThread.start();
   }

}

我没有得到所需的输出。该线程应该读取数据或检查数据是否可用。如果可用,则读取它并将UI变量设置为读取的值。

我正确地执行了吗?我的代码中有什么问题吗?

5 个答案:

答案 0 :(得分:4)

正常Thread should not access the UI thread。我建议使用AsyncTask而不是在Android中使用标准线程或Runnables。 AsyncTask可用于同时远离UI线程,然后对其进行更改。 doInBackground()方法中调用的所有内容都远离主UI线程,而onPreExecute()onPostExecute()方法中调用的所有内容都可以与您的UI进行良好的交互。

当您从UI线程上运行的某个东西调用新线程时,建议使用AsyncTask(如Activity,就像在您的实例中一样)。以下示例;

public class YourAsyncTask extends AsyncTask{

    @Override
    protected Object doInBackground(Object... arg0) {
        // Work to be done in the background.
        return null;
    }

    @Override
    protected void onPostExecute(String result) {
        // Changes to be made to UI
    }

    @Override
    protected void onPreExecute() {
         // Changes to be made to UI
    }

}

然后你可以像这样从你的活动中运行AysncTask;

new YourAsyncTask().execute("");

要处理Activity,您可能需要为AysncTask创建自定义构造函数,并通过构造函数将活动实例传递给它,以存储在实例变量中。我希望这会有所帮助。

更多信息;

答案 1 :(得分:2)

 text.setText(s);

您无法从工作线程中触摸UI。它应该是执行text.setText(s);

的UI线程

答案 2 :(得分:2)

线程无法更新GUI 您可以使用runOnUiThread()方法或使用Handler

编辑:处理程序示例: How to use an Android Handler to update a TextView in the UI Thread?

答案 3 :(得分:0)

所以这就是我将如何做到这一点

创建活动的成员变量:

private BackgroundWorkerThread m_bgThread = null;
protected Handler m_bgHandler = null;

请参阅下面的BackgroundWorkerThread类,然后在onCreate()中初始化主活动中的线程和处理程序:

  // Notice we pass the Handler Constructor this because we are implementing the interface
  // in our activity
  m_bgHandler = new Handler(this)
  m_bgThread = new BackgroudnWorkerThread();
  m_bgThread.start();

确保您拥有活动implements Handler.Callback界面,该界面会将此方法添加到您的活动中:

@Override public boolean handleMessage(Message msg)
 {
     switch(msg.what){

     case 0:
          // Will update the UI on the Main Thread
          updateUI(msg.obj);
      break;

     case 1:
         // rejoin the Thread with the main Thread
        try {
           m_bgThread.join();
        } catch (InterruptedException e) {
           // TODO Auto-generated catch block
          e.printStackTrace();
      }
      break;
   }
    return true;
 }

在每个case语句中,您将消息传递给后台线程。

例如在您的活动中的某个地方调用sendFirstMessage():

 public void sendFirstMessage(){

    // Passes the 0 to the background Handler handleMessage
    m_bgThread.workerHandler.obtainMessage(0).sendToTarget();

 } 

然后在Activity的处理程序handleMessage()覆盖

 case 0:
     updateUI(msg.obj);

 break;

然后

public void updateUI(Object obj){

    text.setText(obj.toString());

    // Call to tell Background Thread to shut down looper
    m_bgThread.workerHandler.obtainMessage(1).sendToTarget();

    // Call to tell the Main Thread no more updates needed
    m_bgHandler.obtainMessage(1).sendToTarget();

}

然后将您的后台线程类创建为您的活动的子类:

private class BackgroundWorkerThread extends Thread implements Handler.Callback{
  private Looper workerLooper;
  protected Handler workerHandler;

 @Override public void run()
 {
     // Set up the Handler and Looper
     Looper.prepare();
     workerLooper = Looper.myLooper();
     workerHandler = new Handler(workerLooper, this);
     Looper.loop();
 }

 @Override public boolean handleMessage(Message msg)
 {
     switch(msg.what){

     case 0:
         // now this function is being called to run on the background Thread  
      beginListenForData();
      break;

     case 1:
      workerLooper.quit();
      break;
   }
    return true;
 }

}


  // This method is now being execute in the background
 public void beginListenForData()
 {

           int bytesAvailable = 3;
          while(!Thread.currentThread().isInterrupted())
          {
            try{
                bytesAvailable = mmInStream.available();
                if(bytesAvailable > 0)
                {
                byte[] packetBytes = new byte[bytesAvailable];
                mmInStream.read(packetBytes);
                bytesAvailable = mmInStream.available();
                String s = new String(packetBytes);
                // Instead of trying to set the text view here, send a message back to 
                // the Activity to update the UI on the Main thread
                // text.setText(s);
                // Passes 0, and the String Object to the Activity Handler
                 m_bgHandler.obtainMessage(0, s).sendToTarget();
                }
        }catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
          }
       }
  }

这有点困难,但这就是它的完成方式,手动创建一个线程,使用looper在后台检查消息,然后将消息从后台线程传递给活动以更新UI。

祝你好运!任何问题随时都可以问。

或者您可以从http://android-developers.blogspot.com/2009/05/painless-threading.html

采取最简单的路线可能的意识形态
 public void beginListenForData()
  {
   String s = null;

   new Thread(new Runnable(){

      public void run(){

           int bytesAvailable = 3;
          while(!Thread.currentThread().isInterrupted())
          {
            try{
                bytesAvailable = mmInStream.available();
                if(bytesAvailable > 0)
                {
                  byte[] packetBytes = new byte[bytesAvailable];
                  mmInStream.read(packetBytes);
                  bytesAvailable = mmInStream.available();
                  s = new String(packetBytes);

                   // Takes Care of updating the UI
                   text.post(new Runnable(){
                       public void run(){
                           text.setText(s);
                       }
                    });
                }
             }catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
             }
       }
     }).start();
  }

答案 4 :(得分:-1)

您先致电super.onCreate(savedInstanceState);吗?

然后你必须致电setContentView(R.layout.yourlayout);

为什么bytesAvailable设置为3而不是0?

为什么bytesAvailable = mmInStream.available();被调用两次?只有一个就足够了!

然后mmInStreamtext(!important!text = (TextView) findViewById(R.id.thetextviewid);)在某处初始化了吗?

如果所有这些都完成了,一切都应该没问题。

编辑:最后您 致电text.postInvalidate();