2016-06-01 68 views
1

我想更好地理解我的android應用中的線程行爲。由於某種原因,當我在其中一個工作線程中使用while(true)時,該線程的run方法中的代碼在while(true)循環之前順序存在,因此永遠不會執行。爲了清楚起見,我不確定代碼(吐司消息)是否實際上未執行,或者線程同步是由Android操作系統處理的方式導致我的Toast消息不顯示。這種行爲似乎是某種阻塞,但我不明白爲什麼會發生這種情況。Android/Java線程同步:while(true){}導致阻塞

我的應用程序使用3個線程:UI線程(Android應用程序中的默認/主線程),在運行時從設備的USB端口無限讀取數據的線程以及通過來自USB的消息處理此數據的線程讀線程。這個問題似乎發生在我的USBController類中。當我註釋掉我的無限while循環時,循環開始之前的所有Toast消息都顯示得很好。 當我不評論我的時候(真),沒有任何東西消息顯示!我很困惑,我認爲我誤解了Android操作系統處理線程的根本原因。即使while循環導致阻塞,我不認爲它,因爲它駐留在工作線程中,爲什麼在while循環之前發生的Toast消息不會被觸發?這是同步問題嗎?我濫用Android的Handler-Looper系統嗎?

下面的代碼。注意:我已經包含了主要活動的相關部分和整個USBController類。我的這個類的實現在很大程度上依賴於USB到串行庫,這裏找到mik3y/usb-serial-for-android。我不認爲這是必要的,但我已經包含了包含我的第三個線程SensorDataBuffer的類,該線程接收來自線程UsbController的消息。

UsbController.java

public class UsbController extends Thread{ 
    ... 
    @Override 
    public void run() { 
     android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_DEFAULT); //sets thread to default queing priority 
     Looper.prepare(); 
     Toast.makeText(mContext.getApplicationContext(), "Hello from UsbController's run method!", Toast.LENGTH_SHORT).show(); 

     // **********************USB otg******************************* 
     //Obtain permission to use Android device's USB intent 
     PendingIntent mPermissionIntent; 
     mPermissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0); 

     // Find all available drivers from attached devices. 
     ProbeTable customTable = new ProbeTable(); 
     customTable.addProduct(0x03EB, 0x2044, CdcAcmSerialDriver.class);     
     UsbManager manager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE); 
     UsbSerialProber prober = new UsbSerialProber(customTable); 
     List<UsbSerialDriver> availableDrivers = prober.findAllDrivers(manager); 

     if (availableDrivers.isEmpty()) { 
      Toast.makeText(mContext.getApplicationContext(), "No available USB drivers found",Toast.LENGTH_SHORT).show(); // Toast message for debugging 
     } 
     else {             // open connection to first avail. driver 
      UsbSerialDriver driver = availableDrivers.get(0); 
      Toast.makeText(mContext.getApplicationContext(), "Driver found",Toast.LENGTH_SHORT).show(); // Toast message for debugging 
      UsbDeviceConnection connection = manager.openDevice(driver.getDevice()); 
      Toast.makeText(mContext.getApplicationContext(), "Device Driver Opened",Toast.LENGTH_SHORT).show(); // Toast message for debugging 
      if (connection == null) {   // You probably need to call UsbManager.requestPermission(driver.getDevice(), ..) 
       Toast.makeText(mContext.getApplicationContext(),"Connection to device not allowed, need permissions",Toast.LENGTH_LONG).show(); 
       manager.requestPermission(driver.getDevice(),mPermissionIntent); //conn test 
       if (manager.hasPermission(driver.getDevice())==true){ 
        Toast.makeText(mContext.getApplicationContext(),"Permissions granted",Toast.LENGTH_SHORT).show(); 
       } 
      } 
      else {      // Read some data! Most have just one port (port 0). 
       List<UsbSerialPort> myPortList = driver.getPorts(); 
       UsbSerialPort port = myPortList.get(0); 
       Toast.makeText(mContext.getApplicationContext(),"USB OTG Connection Established",Toast.LENGTH_SHORT).show(); 
       try { 
        port.open(connection); 
        port.setParameters(9600, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE); // sets baud rate,databits, stopbits, & parity 
        port.setDTR(true);     //necessary to make Arduino Micro begin running it's program 
        Toast.makeText(mContext.getApplicationContext(),"port opened, parameters set, DTR set",Toast.LENGTH_SHORT).show(); 
        byte buffer[] = new byte[16];  
        String incompPacket = ""; 
        Toast.makeText(mContext.getApplicationContext(), "hi again!"), Toast.LENGTH_LONG).show(); 
        while (true){     //continuous loop to read data 
         numBytesRead = port.read(buffer, 100);   
         arduinoData = new String(buffer, "US-ASCII"); 
         String raw = arduinoData.substring(0, numBytesRead); 
         if (numBytesRead > 0) { 
          ... 
         } 
        } 
       } catch (IOException e) { 
        Toast.makeText(mContext, e.getMessage(), Toast.LENGTH_SHORT).show(); 
       } 
      } 
     } 
     Looper.loop(); 
    } 
} 

MainActivity.java

... 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 

     //Multi-threading 
     //Create thread to handle incoming data from USB Controller thread 
     SensorDataBuffer pressureDataBuffer = new SensorDataBuffer(MainActivity.this); 
     Thread bufferThread = new Thread(pressureDataBuffer); 
     bufferThread.start(); 

     //Create USB Serial Worker thread which will continuously receive data 
     UsbController serialDataLink = new UsbController(PlayFrets.this); 
     Thread sensorMonitorThread = new Thread(serialDataLink); 
     sensorMonitorThread.start(); 
     //Toast.makeText(this, "USB Controller thread started", Toast.LENGTH_SHORT).show(); 

     //Build GUI 
     super.onCreate(savedInstanceState); 
     requestWindowFeature(Window.FEATURE_NO_TITLE);   //Removes action bar from display 
     getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); //Removes status bar from display 

     //Create AsyncTask to load the note files. A splash screen will be displayed while task is executing 
     new AsyncTask_NoteFileLoader(this).execute(); 
     } 
... 

SensorDataBuffer.java

public class SensorDataBuffer extends Thread{ 

    //Handler subclass which accepts messages one by one in 
    //the main activitiy's FIFO message que called a "Looper" 
    //The worker thread, sensorMonitor, runs UsbController in parallel 
    //with the UI thread and continuously formats and sends pressure sensor 
    //values read from the microcontroller to the Handler which updates the 
    //corresponding pressure state logic variables in the UI thread. 
    public void run(){ 
     android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO); //TODO:priority was previously more favorable, test this to ensure UI doesn't lag 
     Looper.prepare(); //create MessageQue to receive messages from USB Controller thread 
     UsbController.setHandler(bufferHandler); 

     bufferHandler = new Handler(Looper.myLooper()) { 
       //do stuff 
     }; 
     Looper.loop(); 
    } 
} 
+2

我認爲你不能從非UI線程創建一個Toast。嘗試使用runOnUiThread() – Skynet

+0

我會嘗試這一點,但這似乎與我看到的行爲不一致,因爲當我註釋掉while(true)循環時,我會看到來自此線程類的所有Toast消息。 – Cody

+0

確認這不是問題。 – Cody

回答

0

如何使用HandlerThreads,Handlers和Runnables來代替?使您的代碼更清潔,更易於維護。

在你的onCreate()只需要創建一個他們夫婦:

HandlerThread usbThread = new HandlerThread("USBController"); 
usbThread.start(); 
usbHandler = new Handler(usbThread.getLooper()); 

HandlerThread sensorThread = new HandlerThread("SensorDataBuffer"); 
sensorThread.start(); 
sensorHandler = new Handler(sensorThread.getLooper()); 

然後你創建你的Runnable並將其發佈到處理程序

usbHandler.post(new Runnable(){ 
    run(){ 
     //.... 
     numBytesRead = port.read(buffer, 100); 
      if (numBytesRead > 0) { 
       sensorHandler.post(new Runnable(){run(){//doSomething}}); 
      } 
     //.... 
     if(isStillRunning) 
      usbHandler.post(this); 
    } 
}); 

可以讓運行的職位本身和它將永遠運行。從內部,您可以將runnable發佈到其他處理程序(如主線程處理程序)以顯示您的Toasts。

+0

哦,你可以有你自己的人裏面做所有你需要在其構造來啓動和使用run()方法爲您的無限循環 – xxtesaxx

+0

這難道不是使代碼不乾淨的必要的東西難以維護,因爲我現在在我的onCreate()方法中有大量的邏輯混亂?我認爲我的代碼是非常可讀的,每個線程都在它自己的類和.java文件中 – Cody