应用程序关闭时,Android服务停止


76

我正在从主要的Android活动中启动服务,如下所示:

final Context context = base.getApplicationContext();
final Intent intent = new Intent(context, MyService.class);
startService(intent);

当我通过从最近的应用程序列表中划出活动页面来关闭活动页面时,该服务将停止运行并在一段时间后重新启动。由于我的应用程序要求,我无法将持久性服务与通知一起使用。如何使服务不重新启动或关闭,而仅在应用退出时继续运行?



@kiran我面临的问题是活动关闭时服务将重新启动。我正在寻找一种使服务始终保持运行状态的方法(无需在活动完成后重新启动)。
巴姆(Bam)2015年

1
我觉得我们无法实现。如果资源不足,您的服务将被终止。我们所希望的最好的是重启。而在4.4+版本上,滑动杀死甚至不会重新启动服务。请阅读此主题
Kiran 2015年

认真考虑一下,您是否真的真的需要一直在运行服务:这对功耗和内存消耗都是不利的。
poolie

Answers:


48

我处在相同的情况下,到目前为止,我了解到关闭应用程序时该服务也会关闭,因为它们位于一个线程中,因此该服务应位于另一个线程上以便不关闭它,请调查一下并考虑使用警报管理器使该服务保持活动状态,这里以http://www.vogella.com/articles/AndroidServices/article.html为例,这种方式不会在通知中显示您的服务。

最后,在完成所有研究之后,我逐渐意识到,长期运行服务的最佳选择是startForeground(),因为它是为此而来的,并且系统实际上可以很好地处理您的服务。


10
@Bam我有这个确切的问题。但是,我的服务是前台服务,当我的活动关闭时,它仍然被杀死。我将意图初始化参数从上下文getApplicationContext()更改为getBaseContext(),它解决了该问题。
JayB 2015年

1
我可以在未决的Intent中使用getBaseContext()吗?
Kairi San

1
notificationManager.notify(intent.getIntExtra("notification_Id", 0), builder.build());我应该使用而不是使用startForeground(intent.getIntExtra("notification_Id, 0), builder.build());?我正在使用NotificationCompat.Builder
Kairi San

startForeground()需要api 26有解决方案吗?
Midhilaj

13

使您在维护中像这样服务

 <service
            android:name=".sys.service.youservice"
            android:exported="true"
        android:process=":ServiceProcess" />

然后您的服务将在名为ServiceProcess的其他进程上运行


如果您想使服务永不中断:

  1. onStartCommand()返回START_STICKY

  2. onDestroy()-> startself

  3. 创建Deamon服务

  4. jin->创建Native Deamon进程,您可以在github上找到一些开源项目

  5. startForeground(),有一种无需通知即可启动startForeground的方法,google


12
可以肯定的是,从onDestroy开始本身将被认为与android系统作斗争,这是一种不好的做法。
Flare Cat

12

可能对您有帮助。我可能会弄错,但是在我看来,这与返回START_STICKY您的onStartCommand()方法有关。您可以通过返回START_NOT_STICKY来避免再次调用该服务。


4
实际上,当我退出或关闭应用程序时服务停止运行(然后服务重新启动)。我不希望服务在退出/关闭应用程序时暂停/停止;我希望它继续运行。我相信该解决方案与在单独的进程上运行服务有关,但是我不确定。
巴姆(Bam)

工作中的artex,谢谢
Manikandan K

7

有时服务非常复杂。

当您从活动(或您的流程)启动服务时,该服务本质上是在同一流程上。

引用开发者注释

关于Service类的最困惑实际上是围绕它不是什么:

服务不是独立的过程。Service对象本身并不意味着它在自己的进程中运行;除非另有说明,否则它将与它所属的应用程序在同一进程中运行。

服务不是线程。它本身并不是在主线程之外工作的一种方式(以避免Application Not Responding错误)。

因此,这意味着,如果用户将应用程序从最近的任务中滑出,它将删除您的进程(包括您的所有活动等)。现在,让我们采取三种情况。

首先,该服务没有前台通知。

在这种情况下,您的过程将与服务一起被杀死。

其次,服务具有前台通知

在这种情况下,服务不会被终止,进程也不会被终止

第三种情况如果该服务没有前台通知,则在关闭应用程序后它仍可以继续运行。我们可以通过使服务在不同的流程中运行来做到这一点。(但是,我听说有人说它可能不起作用。请您自己尝试一下

您可以通过在清单中包含以下属性来在单独的过程中创建服务。

android:process =“:yourService”

要么

android:process =“ yourService”流程名称必须以小写字母开头。

引用开发人员注释

如果分配给该属性的名称以冒号(':')开头,则会在需要时创建该应用程序专用的新进程,并且该服务将在该进程中运行。如果进程名称以小写字母开头,则该服务将在具有该名称的全局进程中运行,前提是它具有这样做的权限。这允许不同应用程序中的组件共享进程,从而减少资源使用。

这是我收集的信息,如果有人是专家,如果我错了,请纠正我:)



4

主要问题是在应用关闭时无法启动服务,Android OS(在Some OS中)将终止该服务以进行资源优化,如果您无法重新启动该服务,请致电警报管理器以启动接收器,这是完整的代码,此代码将使您的服务保持有效。

清单是,

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

这个Alaram接收器会在打开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");
    }
}

2

尝试此操作,它将使服务在后台运行。

BackServices.class

public class BackServices extends Service{

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

    @Override
   public int onStartCommand(Intent intent, int flags, int startId) {
      // Let it continue running until it is stopped.
      Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
      return START_STICKY;
   }
   @Override
   public void onDestroy() {
      super.onDestroy();
      Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();
   }
}

在您的MainActivity中, onCreate删除以下代码行

startService(new Intent(getBaseContext(), BackServices.class));

现在,该服务将继续在后台运行。


7
问题仍然存在:当我从“最近”菜单中终止该应用程序时,该服务将重新启动(再次调用onStartCommand)。唯一帮助我的是使其成为前台服务。
Bam 2014年

1

对服务和活动使用相同的过程以及在服务中使用START_STICKY或START_REDELIVER_INTENT的唯一方法是重新启动应用程序时重新启动服务的唯一方法,例如,当用户关闭应用程序时,这种情况也会发生,而且系统决定出于优化原因将其关闭。您无法获得将不会永久中断的服务。这是设计使然,智能手机不能长时间连续运行。这是因为电池寿命是最重要的。您需要设计服务,以便随时处理被停止的情况。


1

您必须将此代码添加到Service类中,以便处理被杀死的情况

 @Override
    public void onTaskRemoved(Intent rootIntent) {
        Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
        restartServiceIntent.setPackage(getPackageName());

        PendingIntent restartServicePendingIntent = PendingIntent.getService(getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
        AlarmManager alarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
        alarmService.set(
                AlarmManager.ELAPSED_REALTIME,
                SystemClock.elapsedRealtime() + 1000,
                restartServicePendingIntent);

        super.onTaskRemoved(rootIntent);
    }

0

为什么不使用IntentService?

IntentService会打开一个与主线程分开的新线程并在那里工作,这样关闭应用程序不会对其产生影响

请注意,IntentService运行onHandleIntent(),完成该服务后,请查看它是否满足您的需求。 http://developer.android.com/reference/android/app/IntentService.html


2
实际上,我的要求是拥有始终运行的后台服务。将服务作为前台服务启动对我来说解决了它。
2015年

0

最好的解决方案是使用android中的同步适配器启动服务。创建一个同步适配器并在onPerformSync方法内调用启动服务。要创建同步帐户,请参考此链接https://developer.android.com/training/sync-adapters/index.html

为什么选择SyncAdapter?回答:因为您以前曾使用您的App上下文启动服务。因此,只要您的应用程序进程被杀死(当您从任务管理器中将其删除或由于缺乏资源而将其杀死时),您的服务也将被删除。SyncAdapter将无法在应用程序线程中工作..因此,如果您在其中调用..服务将不再被删除..除非您编写了将其删除的代码。


0
<service android:name=".Service2"
            android:process="@string/app_name"
            android:exported="true"
            android:isolatedProcess="true"
            />

在清单中声明这一点。为您的流程命名一个自定义名称,并使该流程隔离并导出。


0

运行意向服务将更加容易。在应用程序中创建线程的服务,但该线程仍在应用程序中。


-5

只需在您的第一个可见活动中覆盖onDestroy方法,例如在启动后拥有主页,而从启动重定向到主页时,您已经完成了启动。所以放在主页上销毁。并以该方法停止服务。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.