A snippet with updating TextView line-to-line (via setText()
old line to new one) from non-UI thread:
TextView mLog;
final static String ONE_LINER;
// Condition 1: update from loopless thread
new Thread(new Runnable() {
@Override
public void run() {
mLog.append(ONE_LINER);
}
}).start();
// Condition 2: update from thread with Looper
HandlerThread handlerThread = new HandlerThread();
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
mLog.append(ONE_LINER);
}
};
handler.sendEmptyMessage();
Code execution causes CalledFromWrongThreadException with different stacktraces (please, check "Stacktrace #1" and "Stacktrace #2" below). Under both variants (updating View from plain loopless Thread or from HandlerThread which has Looper inside) exception throwing isn't precise -- event doesn't occur exact after each UI update via worker thread.
Does any have an thoughts why the system behaviour with following call routinues (in the same input conditions) isn't persistent?
Stacktrace #1 (frequence: high; most of the times)
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7900)
at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:1207)
at android.view.ViewGroup.invalidateChild(ViewGroup.java:5424)
at android.view.View.invalidateInternal(View.java:13884)
at android.view.View.invalidate(View.java:13848)
at android.view.View.invalidate(View.java:13832)
at android.widget.TextView.updateAfterEdit(TextView.java:8918)
at android.widget.TextView.handleTextChanged(TextView.java:8943)
at android.widget.TextView$ChangeWatcher.onTextChanged(TextView.java:11623)
at android.text.SpannableStringBuilder.sendTextChanged(SpannableStringBuilder.java:1037)
at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:563)
at android.text.SpannableStringBuilder.append(SpannableStringBuilder.java:286)
at android.text.SpannableStringBuilder.append(SpannableStringBuilder.java:34)
at android.widget.TextView.append(TextView.java:4454)
at android.widget.TextView.append(TextView.java:4441)
Stacktrace #2 (frequence: rare; catched once)
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7900)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1170)
at android.view.View.requestLayout(View.java:20043)
at android.view.View.requestLayout(View.java:20043)
at android.view.View.requestLayout(View.java:20043)
at android.view.View.requestLayout(View.java:20043)
at android.view.View.requestLayout(View.java:20043)
at android.view.View.requestLayout(View.java:20043)
at android.view.View.requestLayout(View.java:20043)
at android.widget.TextView.checkForRelayout(TextView.java:8024)
at android.widget.TextView.setText(TextView.java:4959)
at android.widget.TextView.setText(TextView.java:4786)
at android.widget.TextView.append(TextView.java:4451)
at android.widget.TextView.append(TextView.java:4441)
Note:
- No error “Only the original thread that created a view hierarchy can touch its views” when the view is updated without delay explanation doesn't fully cover the issue (only "Stacktrace #2").
- OS: Android 6.0.