0

I am writing a chat program for android os 2.3 and above. I have been following some examples and got it working perfectly with all my extra bells and whistles on OS 2.3.x.

My problem comes with OS > 2.3.x ie honeycomb and specifically ICS (I have an ICS device for testing).

I am unable to send (packet). It crashes with an error every time. The answer indicated on here seems to be to run the thread inside a service.

Unfortunately that has always been the case - I want to open extra windows and keep a service grabbing data before passing it back to the main activity, regardless of the fact that the main window may open another window - when you go back the entire chat history , including everything you missed.

Can anyone point me to what I need to do to get this to NOT release an error everytime the code hits the send (packet) in OS 4.0 (ICS)?

Thanks in advance. Full Source of the service is here.

package com.rpg.phg.themesh;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.DhcpInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;

/**
 * This class does all the work for sending and receiving broadcast packets. 
 * It has a thread that listens for incoming packets.
 */
public class TheMesh_ChatService extends Service 
{
// Debugging
private static final String TAG = "TheMesh_ChatService";
private static final boolean D = true;
private static String myIP = null;

// Member fields
private final Handler mHandler;
private ComService mConnectedService;

Context mContext ;
/**
 * Constructor. Prepares a new Broadcast service.
 * @param context  The UI Activity Context
 * @param handler  A Handler to send messages back to the UI Activity
 */
public TheMesh_ChatService(Context context, Handler handler, String ourIP) {
    //mAdapter = BluetoothAdapter.getDefaultAdapter();
    mContext = context;
    mHandler = handler;
    myIP = ourIP;
}


/**
 * Start the chat service. Specifically start ComThread to begin 
 * listening incoming broadcast packets. 
 */
public synchronized void start() {
    if (D) Log.d(TAG, "Started Service");

    mConnectedService = new ComService();
    mConnectedService.start();
}


/**
 * Stop thread
 */
public synchronized void stop() {
    if (D) Log.d(TAG, "stop");
    if (mConnectedService != null) {mConnectedService.cancel(); mConnectedService = null;}
}


public void write(byte[] out, String IP) {
    mConnectedService.write(out, IP);
}


/**
 * This thread handles all incoming and outgoing transmissions.
 * 
 * This actually needs to be turned onto the TheMesh_ChatService
 */    
private class ComService extends Thread {
    // Should we create a Thread here? 
    // It *should* stay running until the service ends...
    private static final int BCAST_PORT = 2568;
    DatagramSocket mSocket ;

    InetAddress myBcastIP, myLocalIP, myRemoteIP = null ;

    public ComService() {

        try { 
               myBcastIP    = getBroadcastAddress();
               if(D)Log.d(TAG,"my bcast ip : "+myBcastIP);

               //myLocalIP  = getLocalAddress();
               myLocalIP =  InetAddress.getByName(myIP);
               if(D)Log.d(TAG,"my local ip : "+myLocalIP);

               mSocket      = new DatagramSocket(BCAST_PORT); 
               mSocket.setBroadcast(true); 

             } catch (IOException e) { 
                 Log.e(TAG, "Could not make socket", e); 
             } 
    }


    public void run() {

        try {

            byte[] buf = new byte[1024]; 

            //Listen on socket to receive messages 
            while (true) { 
                DatagramPacket packet = new DatagramPacket(buf, buf.length); 
                mSocket.receive(packet); 

                InetAddress remoteIP = packet.getAddress();
                if(remoteIP.equals(myLocalIP))
                    continue;

                String s = new String(packet.getData(), 0, packet.getLength()); 
                if(D)Log.d(TAG, "Received response " + s); 

                // Send the obtained bytes to the UI Activity
                mHandler.obtainMessage(TheMesh_PAN.MESSAGE_READ,-1,-1, s)
                .sendToTarget();
            } 
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
      * Write broadcast packet.
      */
    public void write(byte[] buffer, String IP) {

        try {
            String data = new String (buffer);
            if (IP.equalsIgnoreCase("ALL"))
            {
                DatagramPacket packet = new DatagramPacket(data.getBytes(), data.length(), 
                    myBcastIP, BCAST_PORT);
                mSocket.send(packet); // logcat shows crash here!
            } else
            {
                myRemoteIP = InetAddress.getByName(IP); // Will this generate Exceptions if lookup fails?
                DatagramPacket packet = new DatagramPacket(data.getBytes(), data.length(), 
                        myRemoteIP, BCAST_PORT);
                mSocket.send(packet); // and logcat shows crash here.
            }
            // Share the sent message back to the UI Activity
            mHandler.obtainMessage(TheMesh_PAN.MESSAGE_WRITE, -1, -1, data)
                    .sendToTarget();
        } catch (Exception e) {
            Log.e(TAG, "Exception during write", e);
        }
    }


    /** 
     * Calculate the broadcast IP we need to send the packet along. 
     */ 
    private InetAddress getBroadcastAddress() throws IOException {
      WifiManager mWifi = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);

      WifiInfo info = mWifi.getConnectionInfo();
      if(D)Log.d(TAG,"\n\nWiFi Status: " + info.toString());

      // DhcpInfo  is a simple object for retrieving the results of a DHCP request
      DhcpInfo dhcp = mWifi.getDhcpInfo(); 
      if (dhcp == null) { 
        Log.d(TAG, "Could not get dhcp info"); 
        return null; 
      } 

      int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask; 
      byte[] quads = new byte[4]; 
      for (int k = 0; k < 4; k++) 
        quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);

      return InetAddress.getByAddress(quads); // The high order byte is quads[0].
    }  

    public void cancel() {
        try {
            mSocket.close();
        } catch (Exception e) {
            Log.e(TAG, "close() of connect socket failed", e);
        }
    }
}

@Override
public IBinder onBind(Intent arg0) {
    // TODO Auto-generated method stub
    return null;
}
}

This all works as is. Most of the program is taken from "broadcastchat4" (sorry I forget which website I got it from). Ive added to it, especially the calling program.

As soon as the send is called from inside the write function (which is called whilst the run() is running as it monitors incoming traffic whilst the write handles outgoing traffic), I get the following in logcat:

09-22 19:32:24.959: E/TheMesh_ChatService(17320): Exception during write
09-22 19:32:24.959: E/TheMesh_ChatService(17320): android.os.NetworkOnMainThreadException
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1099)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at libcore.io.BlockGuardOs.sendto(BlockGuardOs.java:175)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at libcore.io.IoBridge.sendto(IoBridge.java:463)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at java.net.PlainDatagramSocketImpl.send(PlainDatagramSocketImpl.java:182)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at java.net.DatagramSocket.send(DatagramSocket.java:287)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at com.rpg.phg.themesh.TheMesh_ChatService$ComService.write(TheMesh_ChatService.java:146)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at com.rpg.phg.themesh.TheMesh_ChatService.write(TheMesh_ChatService.java:69)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at com.rpg.phg.themesh.TheMesh_PAN.sendMessage(TheMesh_PAN.java:513)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at com.rpg.phg.themesh.TheMesh_PAN.access$11(TheMesh_PAN.java:494)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at com.rpg.phg.themesh.TheMesh_PAN$3.onKey(TheMesh_PAN.java:264)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at android.view.View.dispatchKeyEvent(View.java:5495)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1246)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1246)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1246)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1246)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1246)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1246)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1879)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1361)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at android.app.Activity.dispatchKeyEvent(Activity.java:2324)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1806)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at android.view.ViewRootImpl.deliverKeyEventPostIme(ViewRootImpl.java:3327)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2597)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at android.os.Handler.dispatchMessage(Handler.java:99)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at android.os.Looper.loop(Looper.java:137)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at android.app.ActivityThread.main(ActivityThread.java:4424)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at java.lang.reflect.Method.invokeNative(Native Method)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at java.lang.reflect.Method.invoke(Method.java:511)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
09-22 19:32:24.959: E/TheMesh_ChatService(17320):   at dalvik.system.NativeStart.main(Native Method)

Any ideas as to how to make this ICS (actually I suspect it wont work on honeycomb or higher since the network changes seem to have been incorporated in v3+) work?

TZHX
  • 5,291
  • 15
  • 47
  • 56
user802479
  • 23
  • 3
  • "It crashes with an error every time" -- examine LogCat and look at the stack trace associated with your error. Consider pasting that stack trace into your question and indicating what line in your source is the one raising the exception. – CommonsWare Sep 21 '12 at 11:53

2 Answers2

1

Not 100% certain since I have no stacktrace but:

The constructor of ComService is executed on the main thread (new ComService()) and does networking already since AFAIK most InetAddress methods try resolving names and may do dns lookups which already qualifies as "networking". The IOException you have to catch is a strong indicator for that too.

Try moving the code from the constructor to the run() method and it might already work.

Btw: you should actually never do networking or long running tasks in a constructor. Just simple assignments.

zapl
  • 63,179
  • 10
  • 123
  • 154
-1

I've already answered a similar question here but anyway, add the following code to your oncreate() and you'll be fine

        if (android.os.Build.VERSION.SDK_INT > 9)
         {
            StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
            StrictMode.setThreadPolicy(policy);
          }
Community
  • 1
  • 1
Manoj Kumar
  • 1,510
  • 3
  • 20
  • 40
  • its just disabling the restriction for using Networking on main thread; you could use it in a seperate thread otherwise, but tats somewhat complicated :( – Manoj Kumar Sep 21 '12 at 12:04
  • @CommonsWare: What if you're running in olympics and you are about to reach finish in 10metres, do you need aspirin or or to lay-off? Sometimes you need to patch up until you can fix it, If you still dont agree, report google to remove Strictmode Library from supporting files – Manoj Kumar Sep 21 '12 at 12:17
  • 1
    Multi-threading is sometimes painful, hard to understand at first and it definitely complicates the code but you get used to it. If you just hide the exception here you run into ANR problems quite easily since execution of your whole app can now get stuck if the network connection is bad. The only solution is to move all network access away from the main thread. – zapl Sep 21 '12 at 12:18
  • @zapl I accept:) but this we have to keep in mind from the start of the app, if we run into it in the end, its hard to change the whole code for this issue, so mi8 patch it up temporarily and fix it up in the next version – Manoj Kumar Sep 21 '12 at 12:27
  • do you have any examples of how multithreading works, or even better, networking? All the examples Ive seen do one way comms. Any examples of two way comms where the incoming and outgoing functions need to monitor the same port without conflicting? – user802479 Sep 23 '12 at 02:23
  • Thank you MoJo. That worked, but I am not giving that a positive answer as Technically I would have to demote it as bad programming. I want to write it correctly and efficiently. Also, I didnt say I was above rewriting the entire program (Im not - Im trying to learn the correct, effective Android programming), so, SOrry but Im not awarding that... – user802479 Sep 23 '12 at 09:27
  • @user802479 : no issues mate; always happy to help; i'm happy tat its useful to you:) – Manoj Kumar Sep 24 '12 at 04:56