1

I am trying to get a better handle on notifications with the simple app shown below (at least the relevant parts). I would like to show a notification icon when the app is not destroyed, and remove the notification icon when it is destroyed; pretty simple right?

My problem is that when the fragment's onDestroyView event is fired the notification icon does disappear, but then reappears and I can't figure out why.

To try to figure things out a bit better I create a stop notification button that calls the mServiceBinder's stop() method, which does remove the notification icon for good even though it is calling the same method called in the onDestroyView event.

Any ideas to what the problem is?

MainActivityFragment

package org.chrisolsen.notificationtest;

import ...

/**
 * A placeholder fragment containing a simple view.
 */
public class MainActivityFragment extends Fragment {

    private ServiceConnection mServiceConn;
    private TestService.TestBinder mServiceBinder;

    public MainActivityFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_main, container, false);
    }

    @Override
    public void onDestroyView() {
        Log.d("Fragment", "onDestroyView");

        // *try* to prevent services from restarting
        if (mServiceConn != null) {
            getActivity().unbindService(mServiceConn);
        }

        if (mServiceBinder != null) {
            mServiceBinder.stop();
        }

        mServiceConn = null;
        mServiceBinder = null;
        super.onDestroyView();
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        Log.d("Fragment", "onViewCreated");
        Button showButton = (Button) view.findViewById(R.id.button);
        Button hideButton = (Button) view.findViewById(R.id.btnHide);

        showButton.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("Fragment", "onCLick");

                mServiceConn = new ServiceConnection() {

                    @Override
                    public void onServiceConnected(ComponentName name, IBinder service) {
                        Log.d("serviceConn", "onConnected");
                        mServiceBinder = (TestService.TestBinder) service;
                    }

                    @Override
                    public void onServiceDisconnected(ComponentName name) {
                        Log.d("serviceConn", "onDisconnected");
                    }
                };

                Intent service = new Intent(getActivity(), TestService.class);
                getActivity().startService(service);
                getActivity().bindService(service, mServiceConn, Context.BIND_ABOVE_CLIENT);
            }
        });

        hideButton.setOnClickListener(new Button.OnClickListener() {

            @Override
            public void onClick(View v) {
                mServiceBinder.stop();
            }
        });
    }
}

TestService

package org.chrisolsen.notificationtest;

import ...

public class TestService extends Service {

    public class TestBinder extends Binder {
        public void stop() {
            TestService.this.stopForeground(true);
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("TestSErvice", "onDestroy");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new TestBinder();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("TestSErvice", "onHandleIntent");
        Context c = getApplicationContext();

        Intent activityIntent = new Intent(c, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(
                c, 0, activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());
        builder.setContentIntent(pendingIntent)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentText("The Content Text")
                .setOngoing(false)
                .setAutoCancel(true)
                .setPriority(Notification.PRIORITY_LOW)
                .setContentTitle("The content title");

        startForeground(1, builder.build());

        return super.onStartCommand(intent, flags, startId);
    }

}
chris
  • 4,332
  • 5
  • 41
  • 61

2 Answers2

0

The notification reappears because your service onStartCommand called after your activity closed. It is a bad place to create and show notification. onStartCommand method maybe called multiple times. Place the notification creation to the onCreate for example.

What do you mean when you wrote "app is not destroyed"? If you thought while your service is running, put the creation to the onCreate and remove it in onDestroy.

jkyree
  • 284
  • 2
  • 5
  • Moving the code within the `onStartCommand` results in the exact same issue. "app is not destroyed" means the onDestroy event is not fired. I don't understand what you mean in your last sentence, but there is nothing being created in the onDestroyView... – chris Aug 21 '15 at 00:47
  • onStartCommand is only called multiple times only if you call startService multiple times. http://stackoverflow.com/questions/14182014/android-oncreate-or-onstartcommand-for-starting-service – chris Aug 21 '15 at 00:52
  • 1
    try to use stopping service by `stopSelf()` – jkyree Aug 21 '15 at 01:26
0

Returning START_STICKY instead of super.onStartCommand(intent, flags, startId), is what I should've been doing.

chris
  • 4,332
  • 5
  • 41
  • 61