2014-03-24 44 views
0

在我的代碼中,只要我的ToggleButton被選中,我必須發送一條消息。爲了防止UI線程凍結,我把這個動作放在一個單獨的線程中。此線程爲什麼會凍結用戶界面

我的問題是,它仍然凍結,但我不知道爲什麼

這是相關代碼:

private ToggleButton.OnClickListener lightMirrorOnClickListener = new ToggleButton.OnClickListener() { 

    @Override 
    public void onClick(View v) { 
     if (lightMirrorBtn.isChecked()) { 
      lightThread = new LightThread(); 
      lightThread.start(); 
     } else if(!lightMirrorBtn.isChecked()) { 
      lightThread.interrupt(); 
     } 
    } 

}; 

class LightThread extends Thread { 

    Handler lightHandler = new Handler(); 

    Runnable light = new Runnable() { 
     public void run() { 

      while (lightMirrorBtn.isChecked()) { 
       lightTxMsg.frameFormat = ConstantList.STANDARD_FRAME; 
       lightTxMsg.frameType = ConstantList.DATA_FRAME; 
       lightTxMsg.dataLength = (byte) 8; 
       lightTxMsg.messageID = 0x3C1; 
       int[] messageArray = AMBI_LIGHT; 
       for (int i = 0; i < lightTxMsg.dataLength; i++) { 
        lightTxMsg.data[i] = messageArray[i]; 
       } 

       returnCode = demoController.transmitMessage(lightTxMsg, 
         ConstantList.BINARY_FORMAT);  
      } 
     } 
    }; 

    public void run() { 
     while (!isInterrupted()) { 
      try { 
       Thread.sleep(60); 
       lightHandler.post(light); 

      } catch (InterruptedException e) { 
       break; 
      } 
     } 

    } 

} 

編輯: 這是該問題的解決方案:

private ToggleButton.OnCheckedChangeListener lightMirrorOnClickListener = new ToggleButton.OnCheckedChangeListener() { 

    @Override 
    public void onCheckedChanged(CompoundButton buttonView, 
      boolean isChecked) { 
     if (isChecked == true) { 
      new Thread(new Runnable() { 
       public void run() { 
        lightTxMsg.frameFormat = ConstantList.STANDARD_FRAME; 
        lightTxMsg.frameType = ConstantList.DATA_FRAME; 
        lightTxMsg.dataLength = (byte) 8; 
        lightTxMsg.messageID = 0x3C1; 
        int[] messageArray = AMBI_LIGHT_ON; 
        for (int i = 0; i < lightTxMsg.dataLength; i++) { 
         lightTxMsg.data[i] = messageArray[i]; 
        } 

        returnCode = demoController.transmitMessage(lightTxMsg, 
          ConstantList.BINARY_FORMAT); 
       } 
      }).start(); 

     } else if (!isChecked) { 
      new Thread(new Runnable() { 
       public void run() { 
        lightTxMsg.frameFormat = ConstantList.STANDARD_FRAME; 
        lightTxMsg.frameType = ConstantList.DATA_FRAME; 
        lightTxMsg.dataLength = (byte) 8; 
        lightTxMsg.messageID = 0x3C1; 
        int[] messageArray = AMBI_LIGHT_OFF; 
        for (int i = 0; i < lightTxMsg.dataLength; i++) { 
         lightTxMsg.data[i] = messageArray[i]; 
        } 

        returnCode = demoController.transmitMessage(lightTxMsg, 
          ConstantList.BINARY_FORMAT); 
       } 
      }).start(); 
     } 
    } 

}; 
+0

我建議你查一下有關使用Java多線程的一些教程,你在那裏有幾次失誤。例如,擴展Thread並在其內部創建一個Runnable是多餘的,一個線程已經是一個Runnable。 – m0skit0

+0

你可能會說出其中的一些,所以它更容易做研究? – Fraggles

+0

對不起,這不是一個學習的地方,但要提出具體問題。你甚至不應該嘗試寫多線程代碼,而不必先閱讀它。我可以指向[Oracle的官方教程](http://docs.oracle.com/javase/tutorial/essential/concurrency/),它們是一個很好的開始。 – m0skit0

回答

3
Handler lightHandler = new Handler(); 

當你CREA你的處理程序你的線程尚未開始。它剛剛創建。因此,根據Handlerdefault constructor documentation,此處理程序與「當前線程的Looper」關聯......這是當前主要(UI)線程。所以你在主線程上發佈消息。

你不需要Handler來發布你的runnable。您可以:

  1. 創建一個線程,並指定它在run()方法操作 或
  2. 使用Thread(Runnable)構造函數傳遞一個Runnable到你的線程將在你的線程執行

這裏是關於Threads的基本文章:

  1. Processes and threads
  2. Keeping your app responsive
  3. Specifying the Code to Run on a Thread
+0

好信息,thx!但我想知道,爲什麼這只是這個線程上的問題。在所有其他線程中,我在同一位置創建了一個新的Handler對象 – Fraggles

+0

我不知道您的其他線程在做什麼。這一個有一個循環,while while lightMirrorBtn.isChecked()。它也睡了60毫秒。在其他線程中是否有這些長時間運行的動作?你確定它們(和它們的處理程序)是否也在UI線程中創建(例如,在onClick中)? – stan0

+0

我不認爲這是問題。一個'Handler'只能在UIThread(主線程)或者調用了'Looper.prepare()'的線程中實例化,這個AFAIK你不能手動調用。不,'Handler#post'(和家人)不會在UIThread中運行。 – m0skit0