I keep getting the NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1303)
on a ObjectOutputStream.writeObject()
call even though it is done on a separate thread from the main. I create the ObjectOutputStream
from a socket and have tried creating the socket and ObjectOutputStream
in several different places that I thought were not on the main thread but that didn't seem to help.
I saw a suggestion to change the Thread Policy here, but I would rather not, especially if it turns out to be something small that I am just missing. Am I not creating the threads correctly?
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);
}
}
And the error, at 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)
EDIT: I forgot to mention, this class is used for both Client and Server. The server is started by calling startServer() and client by calling connectToServer(). The Socket Communication works when sent from a client, but crashes with the aforementioned error when attempting to send from the server.
The problem is, that Service run on Main thread by default. So, when you use Binder of that service to call send(), itis still called from main thread. You should call this method from within ClientThread. In order to achieve that try using ExecutorService framework - it has concurrent queue for pending tasks (because wifi may work slower than you want while sending). You can create threads in ExecutorService for every connection and accept task if host names of thread and job are the same.