Android Service starting 2 times because of BindService and StartService - android

I've been working on a music player app. I'm using a service to run the MediaPlayer. From a fragment I start the service using startService(Intent) and then I bound it to my activity. At least that's what I intend to do. The thing is that my app after getting terminated attempts to launch the service again and since the app is already terminated, the service throws an exception.
E/ActivityThread: Activity com.veloxigami.myapplication.MainActivity has leaked ServiceConnection com.veloxigami.myapplication.MainFragment$1#d8b488c that was originally bound here
android.app.ServiceConnectionLeaked: Activity com.veloxigami.myapplication.MainActivity has leaked ServiceConnection com.veloxigami.myapplication.MainFragment$1#d8b488c that was originally bound here.
My onStartCommand() is getting called 2 times. Although I've been able to stop the crashing message by returning START_NOT_STICKY in onStartCommand() as it was suggested in this link. I would like to understand what is the actual problem here.
My project is available on my GitHub if anyone would like to check the code. Music-Player-App.
I'm using a fragment in my MainActivity to work with the service. Below codes are where I work in between MainFragment and MediaPlayerService.
MainFragment
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MediaPlayerService.LocalBinder binder = (MediaPlayerService.LocalBinder) service;
playerService = binder.getService();
serviceBound = true;
Toast.makeText(getActivity(), "Media Player Active", Toast.LENGTH_SHORT).show();
}
#Override
public void onServiceDisconnected(ComponentName name) {
serviceBound = false;
}
};
public void playAudio(int audioIndex) {
currentFile = audioIndex;
if (!serviceBound) {
// storage = new DataStorage(getActivity());
/* storage.storeAudio(playlist);
storage.storeAudioIndex(audioIndex);*/
serviceBound = true;
Log.v("TAG", "Creating new instance");
Intent playerIntent = new Intent(getActivity(), MediaPlayerService.class);
getActivity().startService(playerIntent);
getActivity().bindService(playerIntent, serviceConnection, Context.BIND_AUTO_CREATE);
} else {
//storage = new DataStorage(getActivity());
/*storage.storeAudio(playlist);
storage.storeAudioIndex(audioIndex);*/
Intent broadcastIntent = new Intent(Broadcast_PLAY_NEW_AUDIO);
Log.v("TAG", "Broadcasting");
getActivity().sendBroadcast(broadcastIntent);
}
Intent playingBroadcast = new Intent(Broadcast_PLAY_BTN_CHANGE);
getActivity().sendBroadcast(playingBroadcast);
Intent nextPlayingBroadcastMain = new Intent(Broadcast_SONG_TEXT_CHANGE);
getActivity().sendBroadcast(nextPlayingBroadcastMain);
}
MediaPlayerService
private void initMediaPlayer(){
mediaPlayer = new MediaPlayer();
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setOnInfoListener(this);
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setOnSeekCompleteListener(this);
mediaPlayer.reset();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try{
mediaPlayer.setDataSource(currentMedia.getData());
currentFileIndex = MainFragment.currentFile;
MainActivity.durationText.setText(currentMedia.getDuration());
Toast.makeText(getApplicationContext(),"Playlist Size: "+MainFragment.playlist.size() +"\nSong No.: "+(currentFileIndex+1) ,Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
stopSelf();
}
mediaPlayer.prepareAsync();
}
#Override
public void onCreate() {
super.onCreate();
callStateListener();
registerAudioOutputChange();
register_playNewAudio();
registerStopMediaBroadcast();
registerUpdatePlaylistReceiver();
registerPlayButtonBroadcast();
registerPrevButtonBroadcast();
registerNextButtonBroadcast();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try{
playList = new ArrayList<>();
playList = MainFragment.playlist;
currentMedia = MainFragment.playlist.get(MainFragment.currentFile);
}catch (NullPointerException e){
e.printStackTrace();
stopSelf();
}
if(requestAudioFocus() == false)
stopSelf();
if (currentMedia.getData() != null && currentMedia.getData() !="") {
initMediaPlayer();
}
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer!=null){
stopMedia();
mediaPlayer.release();
}
removeAudioFocus();
if(phoneStateListener != null){
telephonyManager.listen(phoneStateListener,PhoneStateListener.LISTEN_NONE);
}
unregisterReceiver(audioOutputChange);
unregisterReceiver(playNewAudio);
unregisterReceiver(stopMediaBroadcast);
unregisterReceiver(updatePlaylistReceiver);
unregisterReceiver(playButtonBroadcast);
unregisterReceiver(prevButtonBroadcast);
unregisterReceiver(nextButtonBroadcast);
//new DataStorage(getApplicationContext()).clearCachedAudioPlaylist();
}

You don't have an unbindService call anywhere in your code. So whenever the Activity gets destroyed, the system detects that it is still bound to a ServiceConnection and has been leaked. This is still the case when calling bindService inside of a Fragment. Since fragments don't inherit from Activity or Context, they don't have a context reference themselves thus they must use their parent Activities context. Remember to always call unbindService when the owning component is being destroyed, whether it's a Fragment, Activity, or even another Service. It's not unheard for a service to bind to another.
If you don't want your bound service to be destroyed when all clients unbind, you need to add special logic to determine if the Service should transition to a started service temporarily so it won't be killed by the OS, and stop the service when a client rebinds to it.

Related

IInAppBillingService Object is null

I'm trying to use Android In App Billing by following google documents. But I tried to use bindService method for using InAppBillingService Object (mService). It returns true, but mService is still null. here is my code
public class PaymentActivity extends AppCompatActivity {
IInAppBillingService mService;
ServiceConnection mServiceConn;
ArrayList<String> skuList;
Bundle querySkus;
Bundle skuDetails;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_payment);
Log.d("payment", "isBillingAvailable? " + isBillingAvailable(this));
String chargeString = getIntent().getStringExtra("charge");
Log.d("intentTest", "charge is: " + chargeString);
mServiceConn = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
Log.d("Payment", "service disconnected!");
mService = null;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
Log.d("Payment", "service connected!");
}
};
}
#Override
protected void onStart() {
super.onStart();
// Bind to IInAppBillingService
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
this.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
try{
while(mService == null){
Thread.sleep(1000);
Log.d("payment", "sleep 1 second");
}
}catch (InterruptedException e)
{
e.printStackTrace();
}
skuList = new ArrayList<String> ();
skuList.add("premiumUpgrade");
skuList.add("gas");
querySkus = new Bundle();
querySkus.putStringArrayList("ITEM_ID_LIST", skuList);
}
#Override
public void onDestroy() {
super.onDestroy();
if (mService != null) {
unbindService(mServiceConn);
}
}
public static boolean isBillingAvailable(Context context) {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
List<ResolveInfo> list = packageManager.queryIntentServices(intent, 0);
return list.size() > 0;
}
}
I know it is vert bad code because bindService is called in onCreate. But I tried it on Asyn and there was no change. I tried to wait until connection is completed by sleeping mainTread. This try makes my App in infinited Loop.
my error message is
java.lang.RuntimeException: Unable to start activity ComponentInfo{kr.co.bigsapp.www/kr.co.bigsapp.www.activities.PaymentActivity}: java.lang.NullPointerException: Attempt to invoke interface method 'android.os.Bundle com.android.vending.billing.IInAppBillingService.getSkuDetails(int, java.lang.String, java.lang.String, android.os.Bundle)' on a null object reference
Please help me TT
Your approach (looping until mService is non-null) is flawed. In fact, that's what's preventing the service from being created.
The creation of a local, "in-process" Service does not happen synchronously with the bindService()/startService() call. You have to return control to the framework; in this case, that means returning from onStart(). This is because the state of the Service is advanced by the message queue (Looper) that is running on the main thread, and while you're stuck in onStart(), that Looper isn't "looping".
If you return, you'll find that Service.onCreate() and ServiceConnection.onServiceConnected() are called within a few milliseconds.

Android not able to stop service

In my app i am using a Service that periodically checks if there is a new personal message for the logged in user.
The service is started if the user enables the notification feature. Now if the user disables the notification feature i would like to stop the service.
I try to stop the service with the following lines of code.
Intent service = new Intent(getApplicationContext(), MessageService.class);
stopService(service);
The problem is that the service doesn't stop. It goes on working.
Here you can see my message service.
public class MessageService extends Service {
private int intervall;
public MessageService(){
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent,flags,startId);
Bundle intentData = intent.getExtras();
if(intentData != null) {
this.intervall = intentData.getInt("intervall");
}
final Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// async task for calling api otherwise we get an exeception here
new ServiceMessagesTask().execute(MessageService.this);
}
};
new Thread(new Runnable(){
public void run() {
while(true)
{
try {
Thread.sleep(intervall); // repeat after given intervall
handler.sendEmptyMessage(0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
I have an activity where the user can edit his preferences. There it is also possible to activate the notification feature.
The notification service is started or stoped in the savePreferences() method:
public void savePreferences(View button) {
EditText login = (EditText)findViewById(R.id.txtbLogin);
EditText password = (EditText)findViewById(R.id.txtbPassword);
CheckBox enableNotification = (CheckBox) findViewById(R.id.cbNotifications);
Spinner spinner = (Spinner) findViewById(R.id.notificationInterval);
if(!login.getText().equals("") && !password.getText().equals("")){
Map<String, Object> preferences = new HashMap<String, Object>();
preferences.put("document_type", CouchbaseHelper.CB_VIEW_USER_PREFERENCES);
preferences.put("login", login.getText().toString());
preferences.put("password", password.getText().toString());
if(enableNotification.isChecked()){
preferences.put("enableNotification", true);
} else {
preferences.put("enableNotification", false);
}
preferences.put("notificationInterval", this.notificationInterval);
CouchbaseHelper couchbaseHelper = new CouchbaseHelper(getApplicationContext());
String documentId = couchbaseHelper.createDocUserPreferences(preferences);
couchbaseHelper.closeDb();
// start notification service if enabled
if(enableNotification.isChecked()){
Intent service = new Intent(getApplicationContext(), MessageService.class);
service.putExtra("intervall", Integer.valueOf(this.notificationInterval)*60*1000);
startService(service);
} else {
// TODO: this is not working!!! service doesnt stop
// try to stop running service
if(isMyServiceRunning()){
Intent service = new Intent(getApplicationContext(), MessageService.class);
stopService(service);
}
}
}
finish();
Intent main = new Intent(Preferences.this, Main.class);
startActivity(main);
}
I'm afraid you really don't get what a service is, service is just a component that do not require UI and is not linked to an activity life cycle, hence it runs in background, BUT background doesn't necessarily means in a separate thread, actually the service runs in the main thread, now that's one thing, killing a service doesn't mean you are killing all the working threads you create within, and in your code you are creating a Thread that is looping forever, that thread although created in the service is not linked in any way to the service life cycle.
So, if you want to stop the thread, get a reference to the thread you are creating in the startCommand method and in the onDestroy method just stop it, instead of having a while(true) validation, go for a flag and just change it to false in the onDestroy so it will stop the thread you created when started the service.
Regards!

Google Play Service Activity recognition start and stops itself constantly

I need to call the Google activity recognition service through a service (not activity) and run it in the background, of course when the user starts the app, which has an activity (But the service does not called directly from activity).
Therefore I have created a service class (ActivitySensor) and another class (ActivityRecognitionScan).
When I install the app on my Galaxy Nexus S device, the service starts calling onCreate and onDestroy automatically. Even without doing anything in the GUI
It is very strange behaviour. Does anybody has the same experience or solution for it?
I mean I get something as follows in the debug console:
Activity-Logging --- onCreate
Activity-Logging --- onDestroy
Activity-Logging --- onCreate
Activity-Logging --- onDestroy
Activity-Logging --- onCreate
Activity-Logging --- onDestroy
...
Here are my two classes:
public class ActivitySensor extends IntentService {
private ActivityRecognitionScan myascan;
private Intent inIntent;
private static long ACTIVITY_LOG_INTERVAL = 30000L;
private static JsonEncodeDecode jsonencoder = new JsonEncodeDecode();
public ActivitySensor() {
super("ActivitySensor");
}
#Override
public void onCreate(){
super.onCreate();
Log.d("Activity-Logging", "--- onCreate");
try {
myascan = new ActivityRecognitionScan(getApplicationContext());
myascan.startActivityRecognitionScan();
} catch (Exception e) {
Log.e("[Activity-Logging]","----------Error:"+e.getLocalizedMessage());
e.printStackTrace();
}
}
#Override
public void readSensor() {
// Log.e("Activity-Logging", "ActivityRecognitionResult.hasResult: "+String.valueOf(ActivityRecognitionResult.hasResult(inIntent)));
if (ActivityRecognitionResult.hasResult(inIntent)) {
ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(inIntent);
DetectedActivity activity = result.getMostProbableActivity();
final int type = activity.getType();
String strType = new String();
switch(type){
case DetectedActivity.IN_VEHICLE:
strType = "invehicle";
break;
case DetectedActivity.ON_BICYCLE:
strType ="onbicycle";
break;
case DetectedActivity.ON_FOOT:
strType = "onfoot";
break;
case DetectedActivity.STILL:
strType = "still";
break;
case DetectedActivity.TILTING:
strType ="tilting";
break;
case DetectedActivity.UNKNOWN:
strType ="unknown";
break;
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
Editor edt = prefs.edit();
String previousActv = prefs.getString("PREVIOUS_ACTIVIY","");
long previousDate = prefs.getLong("PREVIOUS_DATE", 0);
if (previousActv.length()==0){ // nothing was in the string and it is the first time just initialize
previousActv = strType;
previousDate = new Date().getTime();
// Log.e("-----FIRST TIME: type:", previousActv+" date:"+String.valueOf(previousDate));
edt.putString("PREVIOUS_ACTIVIY", strType);
edt.putLong("PREVIOUS_DATE", previousDate);
edt.commit();
}else {
if (!strType.equalsIgnoreCase(previousActv)){
Date readablePrevDate = new Date(previousDate);
Date nowDate = new Date();
String jsonstr = jsonencoder.EncodeActivity("Activity", readablePrevDate, nowDate, strType, activity.getConfidence());
// Log.e("[Activity-Logging] ----->",jsonstr);
edt.putString("PREVIOUS_ACTIVIY", strType);
edt.putLong("PREVIOUS_DATE", nowDate.getTime());
edt.commit();
DataAcquisitor.dataBuff.add(jsonstr);
}
}
}
}
#Override
protected void onHandleIntent(Intent intent) {
Log.d("Activity-Logging", "--- onHandleIntent"+ "---"+intent.getAction());
intent.putExtra("LOG_INTERVAL",ACTIVITY_LOG_INTERVAL );
intent.putExtra("STOP",false);
inIntent = intent;
readSensor();
}
#Override
public void onDestroy(){
Log.d("Activity-Logging", "--- onDestroy");
myascan.stopActivityRecognitionScan();
myascan=null;
//super.onDestroy();
}
}
This is the class that calls the Google Activity Recognition Service:
ActivityRecognitionScan implements GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener {
private Context ctx;
private static final String TAG = "ActivityRecognition";
private static ActivityRecognitionClient actrecClient;
private static PendingIntent callbackIntent;
private long ACTIVITY_LOG_INTERVAL=30000;
public ActivityRecognitionScan(Context context) {
ctx=context;
}
public void startActivityRecognitionScan(){
int resp = GooglePlayServicesUtil.isGooglePlayServicesAvailable(ctx);
if(resp == ConnectionResult.SUCCESS){
actrecClient = new ActivityRecognitionClient(ctx, this, this);
if (!actrecClient.isConnected()){
actrecClient.connect();
} else{
Log.e("ActivityRecognitionScan"," ---Activity recognition client is already connected");
}
}else{
Log.e("[Activity-Logging]", "Google Play Service hasn't installed");
}
}
public void stopActivityRecognitionScan(){
try{
if (actrecClient.isConnected() || actrecClient.isConnecting() ){
actrecClient.removeActivityUpdates(callbackIntent);
actrecClient.disconnect();
}
} catch (Exception e){
e.printStackTrace();
}
}
#Override
public void onConnectionFailed(ConnectionResult result) {
Log.e("[ActivityRecognitionScan]", "Connection Failed");
}
#Override
public void onConnected(Bundle connectionHint) {
try{
Intent intent = new Intent(ctx, ActivitySensor.class);
Bundle bundle = intent.getExtras();
callbackIntent = PendingIntent.getService(ctx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
long interval = 5000;
if ( null!= bundle && bundle.containsKey("LOG_INTERVAL") ){
interval = bundle.getLong("LOG_INTERVAL");
}
actrecClient.requestActivityUpdates(interval, callbackIntent);
actrecClient.disconnect();
}catch(Exception ex){
Log.e("[Activity-Logging]","Error in requesting Activity update "+ex.getMessage());
ex.printStackTrace();
}
}
#Override
public void onDisconnected() {
callbackIntent.cancel();
actrecClient = null;
Log.e("[ActivityRecognitionScan]","---onDisconnected");
}
}
IntentService automatically stops itself on completion of onHandleIntent as per the source code (see ServiceHandler.handleMessage()) as per the description of an IntentService:
Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.
Use a Service if you want it to run continuously in the background.
You have 2 issues with your code that is causing the problem you are experiencing.
When activity is detected, the pending intent that is called calls (and creates, since it is an IntentService) ActivitySensor. The onCreate will connect another ActivityRecognitionClient, which is unnecessary. This causes another activity to be detected which causes your logging loop.
You should separate the creation of the ActivityRecognitionClient from the handling of the detected activity. You don't need to keep recreating it as subsequent detections will use the same PendingIntent. This will prevent the logging loop.

service instance still null after binding

I have a service, and I am trying to bind an activity to it. The problem is...after running bindService(..), the service instance that Im setting inside the serviceconnection is still null, and I dont know why.
private ConnectionService conn;
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
conn = ((ConnectionService.ConnectionBinder)service).getService();
Toast.makeText(main_tab_page.this, "Connected", Toast.LENGTH_SHORT)
.show();
}
#Override
public void onServiceDisconnected(ComponentName name) {
conn = null;
}
};
#Override
protected void onStart()
{
super.onStart();
//check start connection service
if(conn == null)
{
Intent serviceIntent = new Intent(this, ConnectionService.class);
bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
}
//connect to server
server.conn = conn;
//THIS STATEMENT FAILS: NULL REFERENCE, conn is Null here, and I have no idea why
conn.ConnectToServer(server);
server.StartReader();
}
Yes: The service is defined in the manifest.
Yes: I can start the service from the MAIN Activity (this code resides in an activity that is started BY the main activity, which is where i need to bind to the service) I have checked to make sure the service actually does start....it does
According to every example i've managed to locate for bound services, this should be working. Can anyone tell me why its not?
Edit: Add service code definition
public class ConnectionService extends Service{
private BlockingQueue<String> MessageQueue;
public final IBinder myBind = new ConnectionBinder();
public class ConnectionBinder extends Binder {
ConnectionService getService() {
return ConnectionService.this;
}
}
private Socket socket;
private BufferedWriter writer;
private BufferedReader reader;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(MessageQueue == null)
MessageQueue = new LinkedBlockingQueue<String>();
return Service.START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent arg0) {
return myBind;
}
//some other code that has everything to do with what the service does, and nothing to do with how it should be started/run
}
Please check the service is declared in Manifest.

Is the Android service still alive even after the onDestroy() be called?

For studying the Android service, I wrote a test program that have three button "bind service", "unbind service" and "send echo" on the screen. When clicked, they use bindService(), unbindService() and a Messenger to communicate with the service.
Here is the service codes:
public class MessengerService extends Service {
private final Messenger mMessenger = new Messenger(new TempHandler());
private class TempHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(), "Hi, there.", Toast.LENGTH_SHORT).show();
break;
case MSG_SAY_GOODBYE:
Toast.makeText(getApplicationContext(), "See you next time.", Toast.LENGTH_SHORT).show();
break;
case MSG_ECHO:
Toast.makeText(getApplicationContext(), "Received " + msg.arg1 + " from client.", Toast.LENGTH_SHORT).show();
Messenger replyMessenger = msg.replyTo;
Message replyMsg = Message.obtain(null, MSG_ECHO, msg.arg1, 0);
try {
replyMessenger.send(replyMsg);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
default:
super.handleMessage(msg);
}
}
}
#Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "Service bound", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
#Override
public void onDestroy() {
Log.d("", "Service.onDestroy()...");
super.onDestroy();
}
}
And here is the activity code:
public class MessengerActivity extends Activity {
private Messenger mMessengerService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity2);
Button bind = (Button) findViewById(R.id.button5);
bind.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
doBindService();
}
});
Button unbind = (Button) findViewById(R.id.button6);
unbind.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
doUnbindService();
}
});
Button echo = (Button) findViewById(R.id.button7);
echo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
doSendEcho();
}
});
}
private void doBindService() {
Intent intent = new Intent(getApplicationContext(), MessengerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
private void doUnbindService() {
Message msg = Message.obtain(null, MessengerService.MSG_SAY_GOODBYE);
try {
mMessengerService.send(msg);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
unbindService(mConnection);
}
private void doSendEcho() {
if (mMessengerService != null) {
Message msg = Message.obtain(null, MessengerService.MSG_ECHO, 12345, 0);
msg.replyTo = mMessenger;
try {
mMessengerService.send(msg);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private final Messenger mMessenger = new Messenger(new TempHandler());
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Toast.makeText(getApplicationContext(), "Service is connected.", Toast.LENGTH_SHORT).show();
mMessengerService = new Messenger(service);
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO);
try {
mMessengerService.send(msg);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void onServiceDisconnected(ComponentName name) {
mMessengerService = null;
Toast.makeText(getApplicationContext(), "Service is disconnected.", Toast.LENGTH_SHORT).show();
}
};
private class TempHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MessengerService.MSG_ECHO:
Toast.makeText(getApplicationContext(), "Get the echo message (" + msg.arg1 + ")", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
}
When I click "bind service" and "send echo" button. I can see the service is connected and the message communication is good. And then click "unbind service", I saw the service onDestroy() be called, so I expect the service is stopped and should not respond to the coming message again. But actually is, the service seems still alive and I could get the echo message again when click the "send echo" button. So I'm wondering is there anything I made incorrect? Or maybe I'm not fully understand about the service?
Hope someone can help, thanks.
A service is "bound" when an application component binds to it by calling bindService(). A bound service offers a client-server interface that allows components to interact with the service, send requests, get results, and even do so across processes with interprocess communication (IPC). A bound service runs only as long as another application component is bound to it.
http://developer.android.com/guide/components/services.html
A service will shut down after all bindService() calls have had their corresponding unbindService() calls. If there are no bound clients, then the service will also need stopService() if and only if somebody called startService() on the service.
Drawing from the below link.
How to check if a service is running on Android?.
private void doSendEcho() {
if(isMyServiceRunning()) // if service is running
{
if (mMessengerService != null) {
Message msg = Message.obtain(null, MessengerService.MSG_ECHO, 12345, 0);
msg.replyTo = mMessenger;
try {
mMessengerService.send(msg);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
private boolean isMyServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (MessengerService.class.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
#Override
protected void onStop() {
super.onStop();
// Unbind from the service
unbindService(mConnection);
Log.i("Stopped!",""+isMyServiceRunning());
Log.i("stopped", "Service Stopped");
}
Example:
I tested the below it works fine.
public class MessengerService extends Service {
public static final int MSG_SAY_HELLO =1;
public static final int MSG_SAY_GOODBYE =2;
ArrayList<Messenger> mClients = new ArrayList<Messenger>();
private final Messenger mMessenger = new Messenger(new TempHandler());
private class TempHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
mClients.add(msg.replyTo);
Toast.makeText(getApplicationContext(), "Hi, there.", Toast.LENGTH_SHORT).show();
break;
case MSG_SAY_GOODBYE:
mClients.add(msg.replyTo);
break;
default:
super.handleMessage(msg);
}
}
}
#Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "Service bound", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
#Override
public void onDestroy() {
Log.i("MessengerService", "Service Destroyed...");
super.onDestroy();
}
}
MainAactivity.java
public class MainActivity extends Activity {
boolean mIsBound=false;
Messenger mService = null;
private boolean isMyServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (MessengerService.class.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button bind = (Button) findViewById(R.id.button1);
bind.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
doBindService();
}
});
Button unbind = (Button) findViewById(R.id.button2);
unbind.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
doUnbindService();
}
});
}
class TempHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MessengerService.MSG_SAY_GOODBYE:
Toast.makeText(MainActivity.this,"Received from service: " + msg.arg1,1000).show();
break;
default:
super.handleMessage(msg);
}
}
}
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new TempHandler());
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = new Messenger(service);
// mCallbackText.setText("Attached.");
// We want to monitor the service for as long as we are
// connected to it.
try {
Message msg = Message.obtain(null,
MessengerService.MSG_SAY_HELLO);
msg.replyTo = mMessenger;
mService.send(msg);
// Give it some value as an example.
// msg = Message.obtain(null,
// MessengerService.MSG_E, this.hashCode(), 0);
// mService.send(msg);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
// As part of the sample, tell the user what happened.
Toast.makeText(MainActivity.this, "remote_service_connected",
Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
// mCallbackText.setText("Disconnected.");
// As part of the sample, tell the" user what happened.
Toast.makeText(MainActivity.this, "remote_service_disconnected",
Toast.LENGTH_SHORT).show();
}
};
void doBindService() {
// Establish a connection with the service. We use an explicit
// class name because there is no reason to be able to let other
// applications replace our component.
bindService(new Intent(MainActivity.this,
MessengerService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound=true;
Toast.makeText(MainActivity.this, "Binding",1000).show();
}
void doUnbindService() {
if (mIsBound) {
// If we have received the service, and hence registered with
// it, then now is the time to unregister.
if (mService != null) {
try {
Message msg = Message.obtain(null,
MessengerService.MSG_SAY_GOODBYE);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
// There is nothing special we need to do if the service
// has crashed.
}
}
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
Toast.makeText(MainActivity.this, "UnBinding"+isMyServiceRunning(),1000).show();
}
}
}
I personally find the terminology/nomenclature to be dissatisfying/misleading.
"onDestroy" and "stopService" might be better understood if they were called "FlagForAndroidOSDestruction" and "FlagForAndroidStopService".
If one downloads/compiles/runs any of the following examples, one can see that even when the OnHandleIntent is finished or stopService has been called, the process and even the service can still hang around! To see this simply launch the example(s) below, and then on your phone/tablet goto
Settings->Apps->Running->Show Running Services
and
Settings->Apps->Running->Show Cached Processes
When you see these, try launching a ton of other apps on the phone and THEN you'll see Android destroying said service & process.
http://developer.android.com/guide/components/services.html#ExtendingIntentService
http://android-er.blogspot.com/2013/03/stop-intentservice.html
How to check all the running services in android?
Yes, this is a conclusion drawn out of the official docs:
A service can be both started and have connections bound to it. In such a case, the system will keep the service running as long as either it is started or there are one or more connections to it with the Context.BIND_AUTO_CREATE flag. Once neither of these situations hold, the service's onDestroy() method is called and the service is effectively terminated. All cleanup (stopping threads, unregistering receivers) should be complete upon returning from onDestroy().
From http://developer.android.com/guide/components/services.html :
These two paths are not entirely separate. That is, you can bind to a service that was already started with startService(). For example, a background music service could be started by calling startService() with an Intent that identifies the music to play. Later, possibly when the user wants to exercise some control over the player or get information about the current song, an activity can bind to the service by calling bindService(). In cases like this, stopService() or stopSelf() does not actually stop the service until all clients unbind.
So you have to call unBindService() and after stopService()
This link (Do I need to call both unbindService and stopService for Android services?) says that you need to call stopService before unbindService.
Try that.

Categories

Resources