Why can't I directly call method in the binder class? - java

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?

Related

Calling a function in Android Started Service

I am developing an app to monitor changes in proximity sensor value. In app there should be two separate buttons to start a service and then to start monitoring proximity sensor.
This is my service class
public class MyService extends Service{
Sensor proxSensor;
SensorManager sm;
public static MyService instance;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
instance = this;
return Service.START_STICKY;
}
public void startScan(){
sm=(SensorManager)getSystemService(SENSOR_SERVICE);
proxSensor=sm.getDefaultSensor(Sensor.TYPE_PROXIMITY);
SensorEventListener eventListener = new SensorEventListener() {
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
Log.e("Sensor","Value "+sensorEvent.values[0]);
}
#Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
};
sm.registerListener(eventListener, proxSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
I am starting service from my main activity
public void viewNotification(View view){
startService(new Intent(this,MyService.class));
}
public void viewNotification2(View view){
MyService.instance.startScan();
}
The Log output is printed correctly while the app is running but when I close the activity and remove it from previous apps list the output is not given. But if I call startScan() within onStartCommand it goes on running even after I close the app.
Why doesn't it keep on giving the output?
Is there any other method instead of using static MyService to achieve this?
First of all - use service binding or aidl approaches for working with your Service. (see: https://developer.android.com/guide/components/bound-services.html)
For example:
Suppose, we have service named MyService.
In this class you need write next
private final IBinder mBinder = new LocalServiceBinder();
#Nullable
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public class LocalServiceBinder extends Binder {
public MyService getBinder() {
return MyService.this;
}
}
Next in your Activity:
private MyService mService;
boolean isBounded;
private ServiceConnection mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected");
MyService.LocalServiceBinder binder = (MyService.LocalServiceBinder) service;
mService = binder.getBinder();
isBounded = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected");
isBounded = false;
}
};
And
#Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, MyService.class), mServiceConnection, BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
if (isBounded) {
unbindService(mServiceConnection);
isBounded = false;
}
}
Now you can call your service methods like:
private void activityMethod(){
if (isBounded){
mService.someMethod();
}
}
Second, for working in foreground, call startForeground (int id, Notification notification) method.

Access outer class instance in bound service

In android bound service, the following snippets access to service instance using Foo.this but in reality if we try to do it in java it can't be possible. So how it works?
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; }
// Other public methods below
public void foo() {} // etc...
}
// The activity class
public class MyActivity extends Activity {
LocalService mService;
boolean mBound = false;
....
.....
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
LocalBinder binder = (LocalService.LocalBinder) service;
/* See, using this we can get the instance of LocalService in android, but using the same approach in java we get the error. */
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
As you can see in MyActivity class, we can get the instance of LocalService class using getService() method (and after than we can access its methods). But if we try to do that in java we get the error.
In short, how binder.getService() call works in android.

is it possible to communicate with a Service by calling it's methods?

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

Android: Service won't bind

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.

communicate to service from activity

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.

Categories