2016-06-01 3 views
1

Ich versuche, das Verhalten von Threads in meiner Android-App besser zu verstehen. Aus irgendeinem Grund, wenn ich while (true) in einem meiner Worker-Threads verwende, wird Code in der run-Methode dieses Threads, der VOR der while-Schleife (true) existiert, niemals ausgeführt. Um klar zu sein, ich bin mir nicht sicher, ob der Code (Toast-Nachrichten) tatsächlich nicht ausgeführt wird oder ob die Art und Weise, wie die Thread-Synchronisation vom Android-Betriebssystem gehandhabt wird, dazu führt, dass meine Toast-Nachrichten nicht angezeigt werden. Dieses Verhalten scheint eine Art Blockierung zu sein, aber ich kann nicht herausfinden, warum dies passiert.Android/Java Thread-Synchronisierung: while (true) {} verursacht Blockierung

Meine App verwendet 3 Threads: den UI-Thread (Standard/Hauptthread in einer Android-App), einen Thread zum unendlichen Lesen von Daten vom USB-Port des Geräts zur Laufzeit und einen Thread zum Verarbeiten dieser Daten über Nachrichten vom USB -Lesesthread. Das Problem scheint in meiner USBController-Klasse aufzutreten. Wenn ich meine unendliche while-Schleife auskommentiere, werden alle Toast-Nachrichten vor dem Start der Schleife gut angezeigt. Wenn ich meine Weile nicht auskommen lasse (wahr), KEINE TOASTMELDUNGEN EVER DISPLAY! Ich bin ziemlich verwirrt, ich glaube, ich missverstände etwas Grundlegendes über die Thread-Handhabung durch das Android OS. Selbst wenn eine while-Schleife eine Blockierung verursachen würde, was ich nicht glaube, da sie sich in einem Worker-Thread befindet, warum würden die Toast-Nachrichten, die vor der while-Schleife auftreten, nicht ausgelöst werden, ? Ist das ein Synchronisationsproblem? Verwende ich das Handler-Looper-System von Android?

Code unten. Hinweis: Ich habe den relevanten Teil der Hauptaktivität und die gesamte USBController-Klasse hinzugefügt. Meine Implementierung dieser Klasse hängt stark von der USB-zu-Seriell-Bibliothek ab, die hier gefunden wird mik3y/usb-serial-for-android. Ich denke nicht, dass es notwendig ist, aber ich habe die Klasse, die meinen dritten Thread enthält, SensorDataBuffer, der Nachrichten vom Thread UsbController empfängt, aufgenommen.

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

Ich denke, Sie können keinen Toast von einem Nicht-Ui-Thread erstellen. Versuchen Sie es mit runOnUiThread() – Skynet

+0

Ich werde versuchen, aber das scheint nicht mit dem Verhalten, das ich sehe, weil, wenn ich die while (true) Schleife kommentieren ich sehe alle Toast-Nachrichten aus diesem Thread-Klasse. – Cody

+0

Bestätigt, dass dies nicht das Problem ist. – Cody

Antwort

0

Wie wäre es stattdessen mit HandlerThreads, Handlern und Runnables? Macht Ihren Code viel sauberer und einfacher zu pflegen.

In Ihrem onCreate() erstellen Sie einfach ein paar von ihnen:

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

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

Dann Sie Ihre Runnables erstellen und veröffentlichen sie auf die Handlers

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

Sie können die runnable Post selbst lassen und es wird für immer laufen. Von innen können Sie Runnables an andere Handler (wie den Main Thread Handler) posten, um Ihre Toasts anzuzeigen.

+0

Oh und anstelle von anonymen Runnables, können Sie Ihre eigenen haben, die alle notwendigen Dinge tun, die Sie in ihrem Konstruktor initiieren müssen und verwenden Sie die run() -Methode als Ihre Endlosschleife – xxtesaxx

+0

Macht dies nicht den Code weniger sauber und schwieriger zu pflegen, da ich jetzt ein riesiges Durcheinander an Logik in meiner onCreate() Methode habe? Ich dachte, dass mein Code vorher ziemlich lesbar war, indem ich jeden Thread in seiner eigenen Klasse und in der .java-Datei hatte – Cody

Verwandte Themen