即使在单独的线程中运行,似乎也无法摆脱NetworkOnMainThreadException

时间:2017-06-25 06:50:59

标签: java android multithreading sockets networkonmainthread

NetworkOnMainThreadException android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1303) ObjectOutputStream.writeObject() ObjectOutputStream ObjectOutputStream 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 > send() > 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) 并尝试在几个不同的地方创建套接字和new_items = [ j for j in JSON['Items'] if j.get('Type') == 'Address' ] JSON = {'Items' : new_items } ,我认为这些不在主线程上,但似乎没有帮助。

我看到了一个改变线程策略here的建议,但我宁愿不这样做,特别是如果事实证明这是一个我不知道的小事。我没有正确创建线程吗?

new_items = [ j.get('heighlight') for j in JSON['Items'] if j.get('Type') == 'Address' ] 

错误,在Address Type

[highlight for highlight in respose['Items'] if highlight['Type'] == 'Address']
编辑:我忘了提,这个类用于客户端和服务器。通过调用connectToServer()调用startServer()和客户端来启动服务器。套接字通信在从客户端发送时有效,但在尝试从服务器发送时会因上述错误而崩溃。

2 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

  

警告:服务在其托管进程的主线程中运行;该   service不会创建自己的线程,也不会单独运行   除非另有说明,否则请遵循如果您的服务是   执行任何CPU密集型工作或阻止操作,如MP3   播放或联网,你应该在里面创建一个新的线程   服务完成这项工作。通过使用单独的线程,您可以   降低应用程序无响应(ANR)错误的风险,以及   应用程序的主线程可以保持专用于用户交互   与您的活动。

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