Android服务:Kitkat上的START_STICKY无效

18

我正在使用应用程序中的服务来监听用户按下电源按钮的次数。该实现在所有设备上都正常工作。但是当我在Android Kitkat上测试该应用时,我注意到了一些问题。

只要我将应用程序从最近使用的应用程序中向右滑动,应用程序就不再监听电源按钮。

下面是我正在使用的代码:

public class Receiver extends Service {

    Notification notification;
    private static final int NOTIFICATION_ID = 0;
    NotificationManager manager;
    PendingIntent toOpen;
    Intent intent;
    private BroadcastReceiver POWER_BUTTON = new Powerbuttonrecceiver();

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        registerReceiver(POWER_BUTTON, filter);
        startNotify();
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        unregisterReceiver(POWER_BUTTON);
        dismissNotification();
        super.onDestroy();
    }

    public void startNotify(){
        manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
        int icon = R.drawable.ic_launcher_drop;
        CharSequence tickerText = "Service activated";
        CharSequence tickerContent = "Service is now on. You can press your power button and the app will listen to it. Tap to turn this feature off";
        intent = new Intent(Receiver.this, Options.class);
        toOpen = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);

        notification = new NotificationCompat.Builder(getApplicationContext())
        .setContentTitle(tickerText)
        .setContentText(tickerContent)
        .setSmallIcon(icon)
        .setOngoing(true)
        .setContentIntent(toOpen)
        .build();
        manager.notify(NOTIFICATION_ID, notification);
    }

    public void dismissNotification(){
        manager.cancel(NOTIFICATION_ID);
    }

}

如您所见,我正在使用通知来表示服务已激活。令我困惑的是,在从最近使用的应用程序中向左滑动后,通知仍然存在,不活动的是BroadcastReceiver?还是这是虚假的。

在应用程序的onDestroy中,我没有调用任何函数来注册或注销以及停止服务。再次强调,这个问题只在Android KitKat中出现。如果您们知道发生了什么,请帮忙解决 :)

更新:我还注意到,在Kitkat上使用Play Music时,从最近使用的应用程序中向左滑动后音乐会停止。这是Kitkat上的一个错误吗?但是在SoundCloud上的声音/媒体播放器服务甚至在从最近使用的应用程序中向左滑动后也可以正常工作。

更新: 已记录为问题63618的安卓问题跟踪器上。 阅读问题评论以获取更多详细信息。


我没有特别调用任何函数来销毁服务。就像我之前说的那样,除了Kitkat之外,这个应用在早期的Android API上运行良好。@MadhurAhuja - Rakeeb Rajbhandari
为什么不在清单中注册接收器? - Ivan
1
我刚刚用我们新开发的应用程序进行了测试,是的,这确实正在发生,所以对于这个问题我无法回答,但给你一个赞! - Eugene
1
我已经更新了问题的问题编号。另外,你尝试过在单独的进程中运行服务吗? - S.D.
1
在这里查看答案(带有可行的解决方案!):https://dev59.com/n2Ij5IYBdhLWcg3wGRiM#21157035 - Muzikant
显示剩余9条评论
5个回答

7

看起来这是一个存在于Android 4.4的bug,以下方法可以解决:

@Override
public void onTaskRemoved(Intent rootIntent) {
    // TODO Auto-generated method stub
    Intent restartService = new Intent(getApplicationContext(),
            this.getClass());
    restartService.setPackage(getPackageName());
    PendingIntent restartServicePI = PendingIntent.getService(
            getApplicationContext(), 1, restartService,
            PendingIntent.FLAG_ONE_SHOT);
    AlarmManager alarmService = (AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
    alarmService.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() +1000, restartServicePI);

}

所以这是在Service类本身中被重写的方法。我使用AlarmManager的原因是为了避免Kitkat杀死我的服务。

1
SystemClock.elapsedRealtime() +1000 可能不够,如果失败请尝试更长的延迟,例如2000毫秒。 - AVEbrahimi
2
似乎onTaskRemoved只适用于在滑动后重新启动应用程序,但有时系统会在后台停止应用程序,然后不会调用"onTaskRemoved"! - AVEbrahimi
哇,你真的救了我! - powder366
如果您希望将服务作为单独的进程启动,请启动该服务。此外,您的服务还应具有启动粘性标志。 - Rakeeb Rajbhandari
@Rakeeb 闹钟管理器在我的设备上无论如何都无法触发服务。(Android 4.4.4)。 - Sohaib
显示剩余2条评论

1
我有一个完美运行的解决方案。
@Override
public void onDestroy() {
     startService(new Intent(yourcontext, YourService.class));
}

0

其他应用程序是如何解决这个问题的? - Rakeeb Rajbhandari
这取决于开发者的个人喜好。我认为你的想法也不错,但如果你在几秒钟后尝试恢复它,你会获得更好的性能。 - CR Sardar

0
尝试我的解决方案:this 这个技巧将会在每次用户通过任务管理器或者从设置中强制关闭服务时重启您的服务,我希望这也能对您有所帮助。

0
如果您无法重新启动服务,则可以通过调用一个警报管理器来启动接收器,例如这样:
清单如下,
         <service
            android:name=".BackgroundService"
            android:description="@string/app_name"
            android:enabled="true"
            android:label="Notification" />
        <receiver android:name="AlarmReceiver">
            <intent-filter>
                <action android:name="REFRESH_THIS" />
            </intent-filter>
        </receiver>

在主活动中以这种方式启动闹钟管理器:

String alarm = Context.ALARM_SERVICE;
        AlarmManager am = (AlarmManager) getSystemService(alarm);

        Intent intent = new Intent("REFRESH_THIS");
        PendingIntent pi = PendingIntent.getBroadcast(this, 123456789, intent, 0);

        int type = AlarmManager.RTC_WAKEUP;
        long interval = 1000 * 50;

        am.setInexactRepeating(type, System.currentTimeMillis(), interval, pi);

这将调用接收器,而接收器是:

public class AlarmReceiver extends BroadcastReceiver {
    Context context;

    @Override
    public void onReceive(Context context, Intent intent) {
        this.context = context;

        System.out.println("Alarma Reciver Called");

        if (isMyServiceRunning(this.context, BackgroundService.class)) {
            System.out.println("alredy running no need to start again");
        } else {
            Intent background = new Intent(context, BackgroundService.class);
            context.startService(background);
        }
    }

    public static boolean isMyServiceRunning(Context context, Class<?> serviceClass) {
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);

        if (services != null) {
            for (int i = 0; i < services.size(); i++) {
                if ((serviceClass.getName()).equals(services.get(i).service.getClassName()) && services.get(i).pid != 0) {
                    return true;
                }
            }
        }
        return false;
    }
}

这个警报接收器在 Android 应用程序打开和关闭时各调用一次。因此,该服务的工作方式如下:

public class BackgroundService extends Service {
    private String LOG_TAG = null;

    @Override
    public void onCreate() {
        super.onCreate();
        LOG_TAG = "app_name";
        Log.i(LOG_TAG, "service created");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(LOG_TAG, "In onStartCommand");
        //ur actual code
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        // Wont be called as service is not bound
        Log.i(LOG_TAG, "In onBind");
        return null;
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onTaskRemoved(Intent rootIntent) {
        super.onTaskRemoved(rootIntent);
        Log.i(LOG_TAG, "In onTaskRemoved");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(LOG_TAG, "In onDestroyed");
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接