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
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;
}