即使 运行 在单独的线程中,似乎也无法摆脱 NetworkOnMainThreadException
Can't seem to get rid of NetworkOnMainThreadException even while running in a separate thread
我在 ObjectOutputStream.writeObject()
调用中一直在 android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1303)
处收到 NetworkOnMainThreadException
,即使它是在与主线程不同的线程上完成的。我从套接字创建 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 > 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)
编辑:我忘了说,这个 class 用于客户端和服务器。服务器通过调用 startServer() 启动,客户端通过调用 connectToServer() 启动。套接字通信在从客户端发送时有效,但在尝试从服务器发送时因上述错误而崩溃。
问题是,默认情况下主线程上的服务 运行。因此,当您使用该服务的 Binder 调用 send() 时,它仍然是从主线程调用的。您应该从 ClientThread 中调用此方法。为了实现这一点,请尝试使用 ExecutorService 框架 - 它具有用于待处理任务的并发队列(因为发送时 wifi 的工作速度可能比您想要的慢)。
如果线程和作业的主机名相同,您可以在 ExecutorService 中为每个连接创建线程并接受任务。
Caution: A service runs in the main thread of its hosting process; the
service does not create its own thread and does not run in a separate
process unless you specify otherwise. If your service is going to
perform any CPU-intensive work or blocking operations, such as MP3
playback or networking, you should create a new thread within the
service to complete that work. By using a separate thread, you can
reduce the risk of Application Not Responding (ANR) errors, and the
application's main thread can remain dedicated to user interaction
with your activities.
您可以从 google android 开发者页面阅读有关服务的完整信息
https://developer.android.com/guide/components/services.html
我在 ObjectOutputStream.writeObject()
调用中一直在 android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1303)
处收到 NetworkOnMainThreadException
,即使它是在与主线程不同的线程上完成的。我从套接字创建 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 > 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)
编辑:我忘了说,这个 class 用于客户端和服务器。服务器通过调用 startServer() 启动,客户端通过调用 connectToServer() 启动。套接字通信在从客户端发送时有效,但在尝试从服务器发送时因上述错误而崩溃。
问题是,默认情况下主线程上的服务 运行。因此,当您使用该服务的 Binder 调用 send() 时,它仍然是从主线程调用的。您应该从 ClientThread 中调用此方法。为了实现这一点,请尝试使用 ExecutorService 框架 - 它具有用于待处理任务的并发队列(因为发送时 wifi 的工作速度可能比您想要的慢)。 如果线程和作业的主机名相同,您可以在 ExecutorService 中为每个连接创建线程并接受任务。
Caution: A service runs in the main thread of its hosting process; the service does not create its own thread and does not run in a separate process unless you specify otherwise. If your service is going to perform any CPU-intensive work or blocking operations, such as MP3 playback or networking, you should create a new thread within the service to complete that work. By using a separate thread, you can reduce the risk of Application Not Responding (ANR) errors, and the application's main thread can remain dedicated to user interaction with your activities.
您可以从 google android 开发者页面阅读有关服务的完整信息 https://developer.android.com/guide/components/services.html