Search code examples
androidandroid-service-binding

Cannot get my Android bound service to work with my network listener code


The Android application I am working on, communicates with a "server" running on the same device as my app.

Since I will need to be constantly listening to this server, I decided to make it a bound service. I looked at the official Android documentation here and a good example here.

Both of those examples worked just fine when I ran them on my development tablet. However, when I tried to incorporate the examples into my own app, it doesn't seem to work.

Here is my MainActivity.java:

public class MainActivity extends AppCompatActivity {

    Listener myListener;
    boolean isBound = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent(this, Listener.class);
        startService(intent);
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);

        myListener.listenToServer();

        Toast.makeText(this, Integer.toString(myListener.getPort()), Toast.LENGTH_LONG).show();
    }

    @Override
    protected void onStop() {
        super.onStop();
        if(isBound){
            unbindService(serviceConnection);
            isBound = false;
        }
    }

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Listener.LocalBinder localBinder = (Listener.LocalBinder) service;
            myListener = localBinder.getService();
            isBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound = false;
        }
    };
}

Here is my service to bind, Listener.java:

public class Listener extends Service {

    private boolean keepWorking = true;
    private int localPort;
    private final IBinder iBinder = new LocalBinder();

    public class LocalBinder extends Binder {
        Listener getService(){
            return Listener.this;
        }
    }

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

    public void listenToServer(){
        try {
            byte[] buffer = new byte[16 * 1024];
            DatagramPacket recvPacket = new DatagramPacket(buffer, buffer.length);
            DatagramSocket server = new DatagramSocket(0);
            server.setSoTimeout(1000);
            localPort = server.getLocalPort();

            while (keepWorking) {
                try {
                    server.receive(recvPacket);
                    byte[] data = new byte[recvPacket.getLength()];
                    System.arraycopy(recvPacket.getData(), 0, data, 0, data.length);
                } catch ( IOException ioe) {
                    // something here
                }
            }
        } catch (SocketException se) {
            // something here
        }
    }

    public int getPort() {
        return this.localPort;
    }

    public void stopWorking() {
        this.keepWorking = false;
    }
}

Whenever I try to run my app, I get the following error:

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'Listener.listenToServer()' on a null object reference

at MainActivity.onStart(MainActivity.java:19)

Which is the line myListener.listenToServer();.

I know I can get this to work using a thread (because I've already done it), but that requires a less elegant approach. I like the bound service route as I can easily use the data coming from the server in my app's UI.

So, what is going on? What am I doing wrong?

Thanks


Solution

  • The problem is that onServiceConnected() callback won't be called synchronously. You are merely asking system to bind a service for you, which won't happen immediately/synchronously. As soon as binding will happen you'd be notified with onServiceConnected() callback. Thus after bindService(...) line your myListener hasn't yet been initialized.

    Perform myListener.listenToServer() in onServiceConnected() callback:

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Listener.LocalBinder localBinder = (Listener.LocalBinder) service;
        myListener = localBinder.getService();
        myListener.listenToServer();
        isBound = true;
    }