【问题标题】:Can't seem to get rid of NetworkOnMainThreadException even while running in a separate thread [duplicate]即使在单独的线程中运行,似乎也无法摆脱 NetworkOnMainThreadException [重复]
【发布时间】:2017-11-28 08:43:19
【问题描述】:

我不断在android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1303) 上获得NetworkOnMainThreadException ObjectOutputStream.writeObject() 调用,即使它是在与主线程不同的线程上完成的。我从一个套接字创建了ObjectOutputStream,并尝试在几个不同的地方创建套接字和ObjectOutputStream,我认为这些地方不在主线程上,但这似乎没有帮助。

我看到了更改线程策略 here 的建议,但我宁愿不这样做,特别是如果它证明是我刚刚遗漏的一些小东西。我没有正确创建线程吗?

public class WifiService extends Service {
    private ArrayList<ClientThread> clientThreads=new ArrayList<>();
    private ServerThread serverThread;
    private int localPort;

    public WifiService() {
    }

    public void send(String info){
        for (ClientThread c: clientThreads) {
            c.send(info);
        }
    }

    public void startServer(String hostName){
        if(serverThread==null) {
            serverThread = new ServerThread(hostName);
            new Thread(serverThread).start();
        }
    }

    public void connectToServer(InetAddress address, int port){
        ClientThread clientThread=new ClientThread(address,port);
        clientThread.start();
        clientThreads.add(clientThread);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mybinder;
    }

    public final IBinder mybinder = new LocalBinder();
    public class LocalBinder extends Binder {
        public WifiService getService(){
            return WifiService.this;
        }
    }

    @Override
    public int onStartCommand(Intent intent,int flags, int startId){ //called when service first started. Starts service in background forever unless stopself() called.
        super.onStartCommand(intent, flags, startId);
        return START_STICKY;
    }

    private class ServerThread extends Thread{
        private ServerSocket serverSocket;
        private String hostName;

        public ServerThread(String hostName){
            this.hostName=hostName;
        }

        public void tearDown(){
            stopSelf();
        }

        @Override
        public void run(){
            try {
                serverSocket=new ServerSocket(0);
                localPort=serverSocket.getLocalPort();
                startBroadcasting(hostName);
            } catch (IOException e) {
                e.printStackTrace();
            }

            while (!Thread.currentThread().isInterrupted()){
                Socket clientSocket = null;
                try {
                    clientSocket = serverSocket.accept();
                    ClientThread clientThread=new ClientThread(clientSocket);
                    clientThread.start();
                    clientThreads.add(clientThread);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private class ClientThread extends Thread{
        protected Socket socket=null;
        private ObjectInputStream input;
        private ObjectOutputStream output;
        private InetAddress address;
        private int port;

        public ClientThread(Socket socket){
            this.socket=socket;
            this.address=socket.getInetAddress();
            this.port=socket.getLocalPort();
        }

        public ClientThread(InetAddress address, int port){
            this.address=address;
            this.port=port;
        }

        public void tearDown(){
            stopSelf();
        }

        @Override
        public void run(){
            Log.d("ClientThread", "port: "+port);
            if(socket==null){
                try {
                    socket=new Socket(address, port);
                } catch (IOException e) {
                    e.printStackTrace();
                    return;
                }
            }

            try {
                output=new ObjectOutputStream(new BufferedOutputStream(socket.getOutputStream()));
                input=new ObjectInputStream(socket.getInputStream());
                while (!Thread.currentThread().isInterrupted()) {
                    try {
                        CommunicationTemplate received_CT=(CommunicationTemplate)input.readObject();
                        MessageMainActivity(received_CT);
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
                input.close();
                output.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public void send(String info){
            CommunicationTemplate ct=new CommunicationTemplate(1,"fromPlayer","toPlayer", 100L, info);
            try {
                output.writeObject(ct);
                output.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void MessageMainActivity(CommunicationTemplate communicationTemplate){ //sends bundle to MainActivity to interact with UI
        Bundle messageBundle = new Bundle();
        messageBundle.putSerializable("msg",communicationTemplate);
        Intent intent=new Intent();
        intent.setAction("message");
        intent.putExtra("message",messageBundle);
        sendBroadcast(intent);
    }

    private void commMainActivity(HostInfo hostInfo){ //sends bundle to MainActivity to interact with UI
        Bundle messageBundle = new Bundle();
        messageBundle.putParcelable("host",hostInfo);
        Intent intent=new Intent();
        intent.setAction("host");
        intent.putExtra("host",messageBundle);
        Log.d("commMainActivity", "sending hostInfo to MainActivity...");
        sendBroadcast(intent);
    }
}

还有错误,ClientThread &gt; send() &gt; output.writeObject()

06-25 01:22:29.864 15175-15175/com.example.admin.bluetoothcomms E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.admin.bluetoothcomms, PID: 15175
android.os.NetworkOnMainThreadException
    at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1303)
    at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:111)
    at java.net.SocketOutputStream.write(SocketOutputStream.java:157)
    at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
    at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
    at java.io.ObjectOutputStream$BlockDataOutputStream.flush(ObjectOutputStream.java:1889)
    at java.io.ObjectOutputStream.flush(ObjectOutputStream.java:731)
    at com.example.admin.bluetoothcomms.WifiService$ClientThread.send(WifiService.java:151)
    at com.example.admin.bluetoothcomms.WifiService.send(WifiService.java:33)
    at com.example.admin.bluetoothcomms.MainActivity.send(MainActivity.java:178)
    at com.example.admin.bluetoothcomms.MainActivity.onClick(MainActivity.java:126)
    at android.view.View.performClick(View.java:6207)
    at android.widget.TextView.performClick(TextView.java:11094)
    at android.view.View$PerformClick.run(View.java:23639)
    at android.os.Handler.handleCallback(Handler.java:751)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6688)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358)   

编辑:我忘了提,这个类用于客户端和服务器。服务器通过调用 startServer() 启动,客户端通过调用 connectToServer() 启动。 Socket 通信在从客户端发送时有效,但在尝试从服务器发送时因上述错误而崩溃。

【问题讨论】:

  • @ReazMurshed 这是一个最不恰当的要求。这里提出的问题本身就是完整的,并且出于正当理由,OP 可能无法,可能无法提供他的整个代码,
  • 我理解并删除了评论。

标签: java android multithreading sockets networkonmainthread


【解决方案1】:

问题是,服务默认运行在主线程上。因此,当您使用该服务的 Binder 调用 send() 时,它仍然是从主线程调用的。您应该从 ClientThread 中调用此方法。为了实现这一点,请尝试使用 ExecutorService 框架 - 它具有用于待处理任务的并发队列(因为 wifi 在发送时可能比您想要的工作速度慢)。 如果线程和作业的主机名相同,您可以在 ExecutorService 中为每个连接创建线程并接受任务。

【讨论】:

  • 我正在检查 ExecutorService,但如果是从主线程调用 send() 的问题,为什么当客户端发送到服务器时相同的代码会起作用?
  • 因为 ServerThread 从其 .run() 方法的循环中调用它 - 即从后台(服务器)线程
  • 您可以尝试使用 IntentService - 所有消息都会保存在 Intents 中并由 Android 排队。不幸的是,只有一个线程在运行,因此对于多个客户端线程,ExecutorService 是一个更好的选择。
【解决方案2】:

注意:服务在其宿主进程的主线程中运行;这 服务不创建自己的线程,也不在单独的线程中运行 除非您另有说明,否则处理。如果您的服务将 执行任何 CPU 密集型工作或阻塞操作,例如 MP3 播放或联网,您应该在 服务来完成这项工作。通过使用单独的线程,您可以 降低应用程序无响应 (ANR) 错误的风险,并且 应用程序的主线程可以保持专用于用户交互 与您的活动。

您可以从 google android 开发者页面阅读有关服务的完整信息 https://developer.android.com/guide/components/services.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-31
    • 1970-01-01
    • 2012-08-28
    • 1970-01-01
    • 2018-01-14
    • 2011-12-16
    • 1970-01-01
    • 2021-06-09
    相关资源
    最近更新 更多