2017-07-03 8 views
3

Ich habe gerade angefangen mit Android Concurrency/Looper/Henders zu spielen, und ich habe gerade mit seltsamen Anomalie konfrontiert. Der folgende Code hindert mich nicht daran, Text in TextView aus einem anderen Thread zu setzen.Setzen von Text auf TextView von Hintergrund Thread Anomalie

TextView tv; 
Handler backgroundHandler; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    tv = (TextView) findViewById(R.id.sample_text); 
    Runnable run = new Runnable() { 
     @Override 
     public void run() { 
      Looper.prepare(); 
      backgroundHandler = new Handler() { 
       @Override 
       public void handleMessage(Message msg) { 
        String text = (String) msg.obj; 
        tv.setText(Thread.currentThread().getName() + " " + text); 
       } 
      }; 
      Looper.loop(); 
     } 
    }; 

    Thread thread = new Thread(run); 
    thread.setName("Background thread"); 
    thread.start(); 
    try { 
     Thread.sleep(100); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 
    Message message = backgroundHandler.obtainMessage(); 
    message.obj = "message from UI"; 
    backgroundHandler.sendMessage(message); 
} 

Und raten Sie mal, was passieren

enter image description here

Aber, wenn ich schlafe Faden Hintergrund für eine Weile

backgroundHandler = new Handler() { 
      @Override 
      public void handleMessage(Message msg) { 
       try { 
        Thread.sleep(1000); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
       String text = (String) msg.obj; 
       tv.setText(Thread.currentThread().getName() + " " + text); 
      } 
     }; 

es wirft Ausnahme, wie ich

07-03 18:54:40.506 5996-6025/com.stasbar.tests E/AndroidRuntime: FATAL EXCEPTION: Background thread 
                  Process: com.stasbar.tests, PID: 5996 
                  android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 
                   at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7275) 
erwartet 0

Kann mir jemand erklären, was passiert ist? Warum konnte ich Text aus verschiedenen Threads setzen?

Antwort

3

Vom ersten versuchen, Ihr Code falsch ist, weil Sie Handler auf anderen Thread erstellt. Dies führt dazu, dass Looper/Handler auf verschiedenen Threads ausgeführt wird. Basierend auf Ihrem Kommentar, ich denke, Sie kennen dieses Problem und Sie möchten verstehen, warum Ausnahme nicht beim ersten Versuch, sondern zweitens werfen.

Sie sollten dies beachten: Der Zugriff auf ein Benutzeroberflächenelement mit einem anderen Thread als UI Thread verursacht undefiniertes Verhalten. Das bedeutet:

  • Irgendwann sehen Sie es funktioniert.
  • irgendwann nicht. Sie werden die Ausnahme treffen, wie Sie gesehen haben.

Das bedeutet, dass das Zugreifen auf ein UI-Element in einem anderen Thread nicht immer 100% reproduzierbar ist.

Warum sollten Sie auf alle UI-Elemente nur auf UI-Thread zugreifen? Weil das UI-Element (interner Status ändern, auf Bildschirm zeichnen ...) ein komplexer Prozess ist und zwischen verbundenen Parteien synchronisiert werden muss. Zum Beispiel rufen Sie TextView#setText(String) auf 2 Fragmente, die beide auf dem Bildschirm sichtbar sind. Android führt dies nicht gleichzeitig durch, sondern verschiebt alle Jobs in eine UI-Nachrichtenwarteschlange und führt dies sequenziell durch. Dies gilt nicht nur für Ihre Anwendungssicht, sondern auch für die gesamte Android-Systemperspektive. Aktualisierung von der Statusleiste, die vom System aufgerufen wird, Aktualisierung von Ihrer App, die von Ihrer Anwendung aufgerufen wird, drücken Sie immer Aktionen auf gleiche UI Message Queue vor der Verarbeitung.

Wenn Sie auf Benutzeroberflächenelemente in einem anderen Thread zugreifen und diese ändern, haben Sie diesen Prozess abgebrochen. Das bedeutet, dass möglicherweise zwei Threads auf ein Element mit demselben Status und derselben Zeit zugreifen und es ändern können. Als Ergebnis werden Sie irgendwann Race-Bedingung treffen. Das, wenn ein Fehler auftritt.

Erklären für Ihre Situation ist schwierig, weil nicht genug Daten für die Analyse.Aber es gibt ein paar Gründe:

  • Bei Ihrem ersten Versuch wurde TextView noch nicht auf dem Bildschirm angezeigt. Ein anderer Thread konnte Änderungen an TextView vornehmen. Aber bei deinem zweiten Versuch schläfst du 1 Sekunde. Zu diesem Zeitpunkt wurden alle Ansichten erfolgreich gerendert und auf dem Bildschirm angezeigt. Sie können versuchen Thread.sleep(0) und hoffentlich stürzt Ihr Code nicht ab.
  • Dies wird in einigen Situationen passieren, aber schwer zu erraten, warum. Dass Ihr Thread und Ihr ui-Thread zufälligerweise auf dasselbe Lock-Objekt zugreifen, hat die Ausnahme ausgelöst.

Sie können mehr über Thread Ausgabe lesen Sie hier Android Thread

Explizite Referenzen

Viele Aufgaben auf Nicht-Haupt-Threads haben das Endziel UI-Objekte zu aktualisieren. Wenn jedoch einer dieser Threads auf ein -Objekt in der Ansichtshierarchie zugreift, kann dies zu Instabilität der Anwendung führen: Wenn ein Worker-Thread gleichzeitig die Eigenschaften dieses Objekts ändert , dass ein anderer Thread auf das Objekt verweist, sind die Ergebnisse nicht definiert.

Ich hoffe, dies hilft Ihnen.

0

Nur der UI-Thread kann Änderungen an UI-Elementen vornehmen. Mit anderen Worten, Sie können keine Benutzerschnittstellenänderungen aus Hintergrundthreads vornehmen.

Anstatt also tv.setText(Thread.currentThread().getName() + " " + text);, verwenden Sie den folgenden Code in Ihrem backgroundHandler: -

runOnUiThread(new Runnable() { 
    public void run() { 
     tv.setText(Thread.currentThread().getName() + " " + text); 
    } 
}); 
+0

Ich weiß das, bitte lesen Sie noch einmal meine Post. Ich frage mich, warum es funktioniert, als ich TextView von verschiedenen Thread zugegriffen habe. –

Verwandte Themen