为什么UI线程会被阻塞?

3
在下面的代码中,我试图在按下按钮时运行一个线程。在按钮监听器中,我创建一个新线程并运行它...但在运行时,当按下按钮时,按钮本身会冻结,应用程序不响应,我收到ANR对话框。此外,当套接字成功连接时,甚至TexView也会如此。
mtvStatus.setText("RFC-SOCKET CONNECTED");

不显示任何内容。

请告诉我为什么会发生这种情况。

按钮监听器

this.mbtnConnect.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            BluetoothSocket rfcSocket = mSPPCtrl.rfcConnect();
            if (rfcSocket.isConnected()) {
                mtvStatus.setText("RFC-SOCKET CONNECTED");

                Thread rx = new Thread(new RxRun(rfcSocket));
                rx.run();

            } else {
                mtvStatus.setText("RFC-SOCKET NOT CONNECTED");
            }

        }
    });

可运行的类

private class RxRun implements Runnable {

    private BluetoothSocket mRFCSocket = null;
    private OutputStream mRFCOS = null;
    private InputStream mRFCIS = null;

    public RxRun(BluetoothSocket rfcSocket) {
        this.mRFCSocket = rfcSocket;

        try {
            this.mRFCOS = rfcSocket.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            this.mRFCIS = rfcSocket.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        try {
            this.mRFCOS.write(DIRON.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }

        while (this.mRFCSocket.isConnected()) {
            try {
                int readBytes = this.mRFCIS.read(new byte[5120]);
                Log.d(TAG, CSubTag.bullet("RxRun", "readBytes:" + readBytes));
                //mtvRx.setText(""+readBytes);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

6
我认为你不应该调用rx.run(),而应该调用rx.start()。 - Mateus Brandao
6
你应该使用 rx.start(); 而不是 rx.run(); - Titus
5
不要认为你应该在用户界面线程上执行 .rfConnect()。 - JJF
1
或者为什么你不应该直接使用 Thread,而是应该使用 ExecutorService.run() 方法是从 Runnable 继承而来的,并不会自动在后台运行。另外,你打算如何收集结果呢? - fge
@fge 我只是在做一个测试。回答你的问题“你打算如何收集结果?”我想在while循环内部显示结果,但它什么也没有显示,并且在这行代码之后的任何日志语句:int readBytes = this.mRFCIS.read(new byte[5120]); 都没有被调用...有什么建议为什么它没有被调用? - LetsamrIt
2个回答

1
当按下按钮时,按钮本身会冻结,应用程序无响应,并且我会收到“ANR”对话框。此外,即使套接字成功连接,TexView也不显示任何内容。
这是预期的,因为您实际上还没有启动rx线程。以下是正在发生的情况:
  1. mSPPCtrl被连接,
  2. mtvStatus的文本设置为"RFC-SOCKET CONNECTED",但您无法在视觉上看到它,因为
  3. RxRun实例的run()方法被手动调用,其中循环while (this.mRFCSocket.isConnected())可能持续与套接字连接一样长的时间。
上述所有内容都在UI线程上调用,这就是ANR的原因。
您不应手动调用run()。使用以下方式启动rx线程:
rx.start();

我强烈建议将所有的rfcSocket逻辑移至线程内,并在连接成功/失败时通知UI线程。

编辑

这是我评论中提到的一个选项。

在按钮点击时启动rx线程。

this.mbtnConnect.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        new Thread(new RxRun(rfcSocket)).start();
    }
});

将您的逻辑放在run()方法内部:
public void run() {
        BluetoothSocket rfcSocket = mSPPCtrl.rfcConnect();
        if (rfcSocket.isConnected()) {
            mtvStatus.post(new Runnable() {
                @Override
                public void run() {
                    mtvStatus.setText("RFC-SOCKET CONNECTED");
                }
            });
        } else {
            mtvStatus.post(new Runnable() {
                @Override
                public void run() {
                    mtvStatus.setText("RFC-SOCKET NOT CONNECTED");
                }
            });
            return;
        }
    // the rest of your logic
    }

以下是一些可能有用的链接:

  1. 线程的Android文档
  2. 关于在UI线程中运行代码的Android基础问题的SO问答
  3. 另一个关于从线程更新UI的SO帖子

谢谢您的回答。您能否告诉我更多关于notify()方法的信息,为什么应该使用它?它是否像“return”一样? - LetsamrIt
1
@LetsamrIt 不,我不是指 Objectnotify() 方法,我把它用作动词。有几种选项可以从后台线程“通知”UI线程。例如通过 Handler、如果你向后台线程传递了上下文,则使用 runOnUiThread() 方法、实现 Observer 模式或直接调用 YourActivity 方法(其中视图必须仅在UI线程上触摸)。只需使用其中一种技术即可。如果你真的需要帮助,我明天会编辑答案(我快要睡着了... :)) - Onik

1
这里的错误非常简单-不要在Thread实例上使用run(),因为它实际上会在当前线程上运行其Runnable!使用start()就可以正常工作 :-)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接