I'm trying to communicate with a service. I found this Android Guide.
I did like in the first example but i have an error:
"java.lang.RuntimeException: Unable to bind to service
com.example.internetcall.MyService#41763970 with Intent {
cmp=com.example.internetcall/.MyService }:
java.lang.NullPointerException: println needs a message".
This is the Activity:
public class StartService extends Activity{
String telephoneNumber;
TextView statusTextView, numberTextView;
MyService myService;
Boolean myBound;
ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName arg0, IBinder arg1) {
Log.i("bnf","qui");
LocalBinder binder = (LocalBinder) arg1;
myService = binder.getService();
myBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
myBound = false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.start_service_layout);
statusTextView = (TextView) this.findViewById(R.id.statusTextView);
numberTextView = (TextView) this.findViewById(R.id.numberTextView);
Intent i = this.getIntent();
telephoneNumber = i.getStringExtra("number");
numberTextView.setText(telephoneNumber);
if(isMyServiceRunning()){
statusTextView.setText("Online");
statusTextView.setTextColor(Color.GREEN);
}else{
statusTextView.setText("Offline");
statusTextView.setTextColor(Color.RED);
}
Intent intent = new Intent(this, MyService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
//SupportClass.myService.setNumber(telephoneNumber);
}
private boolean isMyServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if ("com.example.internetcall.MyService".equals(service.service.getClassName().toString())) {
return true;
}
}
return false;
}
}
and this is MyService:
public class MyService extends Service{
MyWebSocket mws;
private final IBinder mBinder = new LocalBinder();
Boolean onCall;
String telephoneNumber;
String myTelephoneNumber = null;
#Override
public IBinder onBind(Intent arg0) {
Log.i("bnf",arg0.getStringExtra("number"));
return mBinder;
}
public class LocalBinder extends Binder {
MyService getService() {
Log.i("bnf","localbinder");
// Return this instance of LocalService so clients can call public methods
return MyService.this;
}
}
Does someone know a solution for this problem?
if i did some grammar error i sorry but i don't know english very well.
Your exception occurs at Log.i("bnf",arg0.getStringExtra("number")); arg0.getStringExtra("number") is null. You did not pass the string "number" in your intent to bind the service.
Related
I'm developing an app that is going to require 2 services:
HostService - A foreground service that manages music playback using the Spotify app-remote SDK. It is necessary to run this service in the foreground as I need to detect playState changes and manage playback constantly. However, this service ONLY needs to run if a condition is met, we'll call that condition isHost. This service will be bound to a SessionActivity that displays the music playing and manages playback.
ApiService - A service that continuously polls a separate web API for updates. This must also run in the foreground if the isHost condition is met as the updates may influence the music playback. However, if the user is not a host, this can be a background process that only needs to poll while the user is actively using a particular Activity. I'm not sure if this service should also be bound to the activity or not.
My question is, how should I go about setting up the ApiService differently depending on this condition? I've started to implement these services below but I'm not entirely sure what I have done makes sense. Additionally, am I doing the right thing by using an event handler the way I am? I don't fully understand Intents and I'm not sure if I should be using broadcast messages and such instead.
SessionActivity.java
class SessionActivity extends AppCompatActivity implements HostEventHandler, ApiEventHandler {
private boolean isHost;
private String data;
private String UID;
private ApiService api;
private HostService host;
private boolean hostBound;
private boolean apiBound;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_session);
isHost = getIntent().getBooleanExtra("isHost", false);
data = getIntent().getStringExtra("data");
UID = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
}
private ServiceConnection hostConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
Log.d(Constants.TAG, "Bound to HostService");
HostService.LocalBinder binder = (HostService.LocalBinder) service;
host = (HostService) binder.getService()
host.registerEventHandler(this);
hostBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
Log.e(Constants.TAG, "onServiceDisconnected");
hostService = null;
}
};
private ServiceConnection apiConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
Log.d(Constants.TAG, "Bound to ApiService");
HostService.LocalBinder binder = (ApiService.LocalBinder) service;
api = (ApiService) binder.getService()
api.registerEventHandler(this);
apiBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
Log.e(Constants.TAG, "onServiceDisconnected");
apiService = null;
}
};
protected void onStart() {
super.onStart();
Log.d(Constants.TAG, "Binding to services...");
Intent intent = new Intent(this, ApiService.class);
intent.putExtra("data", data);
intent.putExtra("user", Constants.UID);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
if (isHost) {
Intent intent = new Intent(this, HostService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
{
}
#Override
protected void onStop() {
super.onStop();
unbindService(apiConnection);
apiBound = false;
if (isHost) {
unbindService(hostService);
hostBound = false;
}
}
// HostEventHandler and ApiEventHandler methods...
}
HostService.java
public class HostService extends Service {
private final IBinder binder = new LocalBinder();
private HostEventHandler handler;
public class LocalBinder extends Binder {
HostService getService() {
return HostService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
Log.d(Constants.TAG, "onBind");
return binder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
createNotificationChannel();
Intent notificationIntent = new Intent(this, SessionActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText("Foreground service is running...")
.setSmallIcon(R.drawable.logo)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
connect(); // This connects to spotify
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
disconnect();
}
public registerEventHandler(HostEventHandler handler) {
this.handler = handler;
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
}
ApiService.java
public class ApiService extends Service {
private final IBinder binder = new LocalBinder();
private ApiEventHandler handler;
private final Timer timer;
private boolean connected = false;
private String data;
private String UID;
private ApiState state;
public class LocalBinder extends Binder {
HostService getService() {
return ApiService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
Log.d(Constants.TAG, "onBind");
return binder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!connected) {
data = intent.getStringExtra("data");
UID = intent.getStringExtra("user");
connect();
}
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
disconnect();
}
public void registerEventHandler(ApiEventHandler handler) {
this.handler = handler;
}
public ApiState getState() {
return state;
}
private void connect() {
if (connected) return;
class Updater extends TimerTask {
#Override
public void run() {
state = fetchUpdatedState(); // This will implement the web request
handler.onStateUpdate(state);
Log.d(Constants.TAG, "Polling API...");
timer.schedule(new Updater(), Constants.REFRESH_DELAY);
}
}
timer.schedule(new Updater(), Constants.REFRESH_DELAY);
connected = true;
}
}
I was reading the documentation for bound services on android and I stumble upon the following code on the documentation:
public class LocalService extends Service {
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
// Random number generator
private final Random mGenerator = new Random();
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/** method for clients */
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
and
public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
#Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
/** Called when a button is clicked (the button in the layout file attaches to
* this method with the android:onClick attribute) */
public void onButtonClick(View v) {
if (mBound) {
// Call a method from the LocalService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
int num = mService.getRandomNumber();
Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
What strikes me as odd was that why is the method getRandomNumber() not be included in the LocalBinder class. This way you can just use the binder to call method to be perform(getRandomNumber()) rather than having to call the LocalBinder to return the service with getService() and then using the service to call the getRandomNumber(). This extra step of returning the service to call getRandomNumber() doesn't make sense to me so can anyone explain to me why this is the case?
So I'm building my MusicPlayer and so I create a service to manage everything. I call ContextWrapper.bindService() and this should call onServiceConnected and onBind right? But this isn't the case for me.
Please tell me if I got anything wrong there.
Here is my code:
This is my method to bind a Activity to the service (MusicUtils.java) . In my Activity it looks like this: MusicUtils.bindToService(this, this);
public static final ServiceToken bindToService(final Context context,
final ServiceConnection callback) {
Activity realActivity = ((Activity)context).getParent();
if (realActivity == null) {
realActivity = (Activity)context;
}
final ContextWrapper contextWrapper = new ContextWrapper(realActivity);
contextWrapper.startService(new Intent(contextWrapper, MediaPlayerService.class));
final ServiceBinder binder = new ServiceBinder(callback);
Intent intent = new Intent().setClass(contextWrapper, MediaPlayerService.class);
if (contextWrapper.bindService(intent, binder, Context.BIND_AUTO_CREATE)) {
mConnectionMap.put(contextWrapper, binder);
Log.e("MusicUtils","Bound");
return new ServiceToken(contextWrapper);
}
return null;
}
This is my ServiceBinder:
public static final class ServiceBinder implements ServiceConnection {
private final ServiceConnection mCallback;
public ServiceBinder(final ServiceConnection callback) {
mCallback = callback;
}
#Override
public void onServiceConnected(final ComponentName className, final IBinder service) {
mService = IMusicSlideService.Stub.asInterface(service);
if (mCallback != null) {
mCallback.onServiceConnected(className, service);
}
}
#Override
public void onServiceDisconnected(final ComponentName className) {
if (mCallback != null) {
mCallback.onServiceDisconnected(className);
}
mService = null;
}
}
And this is my onBind(MediaPlayerService.java) :
#Override
public IBinder onBind(Intent intent) {
Log.e("Service", "onBind");
return mBinder;
}
If you need something please tell me!
Thanks!!!
I use code below and all works fine.
Intent intent = new Intent(mContext, Connector.class);
mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
May be your problem is in using ContextWrapper?
Or try to change new Intent().setClass(contextWrapper, MediaPlayerService.class); to new Intent(contextWrapper, MediaPlayerService.class);
as mentioned here and as all developers do, there are several ways for an Activity to communicate with a Service. the most popular ways are using Intent data and binding. is it possible to communicate with a service simply by calling it's methods ? if it is possible, is it a good way?
Yes, you just have to return service instance from the binder. Take a look at this article for an example of how to do this.
Extracted from the article linked above (look for the line: int num = mService.getRandomNumber();).
LocalService.java
public class LocalService extends Service {
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public int getRandomNumber() {
return 5;
}
}
BindingActivity.java
public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;
#Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
// Call the method from service
int num = mService.getRandomNumber();
}
};
}
Im trying to create bound service. As a test I created service which plays music:
public class MusicService extends Service {
private final IBinder myBinder = new LocalBinder();
MediaPlayer player;
#Override
public IBinder onBind(Intent arg0) {
return myBinder;
}
#Override
public void onCreate() {
super.onCreate();
player = MediaPlayer.create(this,R.raw.teardrop);
player.setLooping(true); // Set looping
player.setVolume(100,100);
player.start();
}
#Override
public void onDestroy() {
player.stop();
player.release();
}
public class LocalBinder extends Binder {
public MusicService getService() {
return MusicService.this;
}
}
}
And when I bind it from Activity nothing happens:
public class MainActivity extends TabSwipeActivity {
boolean isBound = false;
MusicService myService;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Some code
Intent intent = new Intent(this, MusicService.class);
bindService(intent, myConnection, Context.BIND_AUTO_CREATE);
if(isBound){
Toast.makeText(this, "success", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(this, "bind failed", Toast.LENGTH_SHORT).show();
}
}
private ServiceConnection myConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,IBinder service) {
LocalBinder binder = (LocalBinder) service;
myService = binder.getService();
isBound = true;
}
public void onServiceDisconnected(ComponentName arg0) {
isBound = false;
}
};
}
Service is registred in manifest:
<service android:name=".MusicService" />
Bind failed appears and nothing happens
EDIT: bindService() returns false
EDIT2: when I add complete name in manifest eg. com.mypackage.mypackage2.MusicService
bind Service() returned true. But onServiceConnected() is never called.
Next question is: When I create service which implements LocationListener, what should I use to send message to activity everytime when onLocationChanged()?
I already know the solution. I extends TabActivity made by actionBarSherlock instead of Activity. This is known issue:
getApplicationContext().bindService();
fix that.