在 Thread 上与用户交互导致我的应用程序崩溃

Interaction with user on Thread cause my app crash

如果 TextView.setText 在线程中,我的应用程序会崩溃:
注意:以下 class 在 MainActivity 内部。

private class StreamThread extends Thread {
    public StreamThread() {

    }

    public void run() {
        byte[] buffer = new byte[1024];
        int bytes;
        while (true) {
            try {
                bytes = mmInStream.read(buffer);
                String message = new String(buffer, 0, bytes);
                //THIS IS IMPORTANT, READ THIS PLEASE
                //I tested many times my app to find the problem, and I found, my app crashes when TextView.setText() is executed

                //Here starts the problem
                ((TextView) findViewById(R.id.textView)).setText(message);
            } catch (IOException e) {
                break;
            }
    }

}

线程被设计为通过分离允许另一个代码同时执行来执行代码。 不幸的是线程与 UI 不兼容,但我有一个解决方案。

runOnUiThread(new Runnable() {
    @Override
    public void run () {
        //Some stuff that needs to interact with the user (UI).
    }
}

您必须在 ui 线程中更新视觉组件。为了您的目的,您应该使用在 ui 线程中运行的 AsyncTask、Service 或 Runnable。

例如,您在以下代码中使用 AsyncTask

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textview = (TextView) findViewById(R.id.textView);
        new StreamAsyncTask(textview).execute();
    }

    private class StreamAsyncTask extends AsyncTask<Void, String, Void> {

        private TextView textview;

        public StreamAsyncTask(TextView textview) {
            this.textview = textview;
        }

        @Override
        protected Void doInBackground(Void... voids) {
            byte[] buffer = new byte[1024];
            int bytes;
            while (true) {
                try {
                    bytes = mmInStream.read(buffer);
                    String message = new String(buffer, 0, bytes);
                    publishProgress(message);
                } catch (IOException e) {
                    break;
                }
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(String... values) {
            super.onProgressUpdate(values);
            textview.setText(values[0]);
        }
    }
}

或者你可以使用Activity的方法runOnUiThread:

runOnUiThread(new Runnable() {
    @Override
    public void run() {
        ((TextView) findViewById(R.id.textView)).setText(message);
    }
});

最后一种方式更容易理解,但第一种方式更灵活。 了解 AsyncTasks:http://developer.android.com/reference/android/os/AsyncTask.html

这应该可以解决问题:

private class StreamThread extends Thread {
    public StreamThread() {}

    public void run() {
        byte[] buffer = new byte[1024];
        int bytes;
        while (true) {
            try {
                bytes = mmInStream.read(buffer);
                String message = new String(buffer, 0, bytes);

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        ((TextView) findViewById(R.id.textView)).setText(message);
                    }
                });
            } catch (IOException e) {
                break;
            }
        }
    }
}

太好了,怎么了? Android 的 UI 是单线程的。 这意味着您不能从 ui 线程之外的另一个线程更改 ui。 您可以使用 runOnUiThread-Method or using a Handler.

post 更改 ui 线程