Android:当应用程序被杀时,保持服务运行

时间:2015-05-29 09:20:05

标签: android service background intentservice

即使应用程序被杀,我也希望在后台运行IntentService。并且“被杀”是指长按主页按钮 - > 查看所有正在运行的应用 - > 将我的应用扫到一边 - > app killed 长按后退按钮 - > app killed

我的代码如下。在我的MainActivity中:

Intent intent = new Intent(this, MyService.class);
this.startService(intent);

在我的MyService中:

public class MyService extends IntentService {

@Override
protected void onHandleIntent(Intent intent) {
    System.out.println("MyService started");
    run();
}

private void run() {
    while (true){
        System.out.println("MyService still running");
        doSomething();
        waitSomeTime();
    }
}

}

当应用程序打开时,我看到该服务正在运行。当我通过主页按钮最小化应用时,它仍在运行。当我通过后退按钮关闭应用时,它仍然在运行。但如果我如上所述杀死它,它就会停止。我该如何解决这个问题?

8 个答案:

答案 0 :(得分:27)

如果您的应用启动了您的服务,那么实际上您的服务正在主进程上运行。因此,当应用程序被杀死时,服务也将被停止。所以你可以做的是,从服务的onTaskRemoved方法发送广播,如下所示:

 Intent intent = new Intent("com.android.ServiceStopped");
 sendBroadcast(intent);

并有一个广播接收器,它将再次启动服务。我试过了。服务从所有类型的杀戮中重新开始。

答案 1 :(得分:18)

所有答案似乎都是正确,所以我将在此处给出完整答案。

首先,最简单的方法是尝试手动杀死 app 并在Android中启动 Broadcast ,然后定义自定义 BroadcastReceiver 之后触发服务重启。

现在让我们进入代码。


YourService.java

中创建服务

请注意onCreate()方法,在此方法中,对于大于 Android Oreo 的Build版本,我们将以不同的方式启动前景服务。这是因为最近引入了严格的通知政策,我们必须定义自己的通知渠道才能正确显示它们。

this.sendBroadcast(broadcastIntent);方法中的onDestroy()是一条语句,它以动作名称 "restartservice" 异步发送广播。稍后我们将以此为触发来重新启动我们的服务。

我们在这里定义了一个简单的Timer任务,该任务每 1秒 Log 中打印一个计数器值,而每次打印时都会递增。 / p>

public class YourService extends Service {
public int counter=0;

    @Override
    public void onCreate() {
        super.onCreate();
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
            startMyOwnForeground();
        else
            startForeground(1, new Notification());
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private void startMyOwnForeground()
    {
        String NOTIFICATION_CHANNEL_ID = "example.permanence";
        String channelName = "Background Service";
        NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE);
        chan.setLightColor(Color.BLUE);
        chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);

        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        assert manager != null;
        manager.createNotificationChannel(chan);

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
        Notification notification = notificationBuilder.setOngoing(true)
                .setContentTitle("App is running in background")
                .setPriority(NotificationManager.IMPORTANCE_MIN)
                .setCategory(Notification.CATEGORY_SERVICE)
                .build();
        startForeground(2, notification);
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        startTimer();
        return START_STICKY;
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        stoptimertask();

        Intent broadcastIntent = new Intent();
        broadcastIntent.setAction("restartservice");
        broadcastIntent.setClass(this, Restarter.class);
        this.sendBroadcast(broadcastIntent);
    }



    private Timer timer;
    private TimerTask timerTask;
    public void startTimer() {
        timer = new Timer();
        timerTask = new TimerTask() {
            public void run() {
                Log.i("Count", "=========  "+ (counter++));
            }
        };
        timer.schedule(timerTask, 1000, 1000); //
    }

    public void stoptimertask() {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }

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

创建广播接收器以响应Restarter.java

中自定义的广播

您刚刚在"restartservice"中定义的动作名称 YourService.java 的广播现在应该触发一种方法,该方法将重新启动服务 。这是在Android中使用 BroadcastReceiver 完成的。

我们覆盖了 onRecieve() 中的内置BroadcastReceiver方法,以添加将重新启动服务的语句。 startService()在Android Oreo 8.1及更高版本中将无法正常工作,因为一旦应用被终止,严格的后台策略将在重启后立即终止服务。因此,我们将startForegroundService()用于更高版本,并显示连续的通知以保持服务运行。

public class Restarter extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i("Broadcast Listened", "Service tried to stop");
        Toast.makeText(context, "Service restarted", Toast.LENGTH_SHORT).show();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(new Intent(context, YourService.class));
        } else {
            context.startService(new Intent(context, YourService.class));
        }
    }
}

定义您的MainActivity.java,以便在应用启动时调用该服务。

在这里,我们定义了一个单独的isMyServiceRunning()方法来检查后台服务的当前状态。如果该服务未运行 ,我们将使用startService()启动该服务。

由于该应用程序已经在前台运行,因此我们无需将其作为前台服务启动,以防止自身被终止。

请注意,在onDestroy()中,我们专门调用stopService(),以便调用我们的 覆盖的方法 。如果未执行此操作,则该服务将在终止应用程序后自动 终止,而无需调用 onDestroy() YourService.java方法>

public class MainActivity extends AppCompatActivity {
    Intent mServiceIntent;
    private YourService mYourService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mYourService = new YourService();
        mServiceIntent = new Intent(this, mYourService.getClass());
        if (!isMyServiceRunning(mYourService.getClass())) {
            startService(mServiceIntent);
        }
    }

    private boolean isMyServiceRunning(Class<?> serviceClass) {
        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (serviceClass.getName().equals(service.service.getClassName())) {
                Log.i ("Service status", "Running");
                return true;
            }
        }
        Log.i ("Service status", "Not running");
        return false;
    }


    @Override
    protected void onDestroy() {
        stopService(mServiceIntent);
        super.onDestroy();
    }
}

最后将它们注册到您的AndroidManifest.xml

以上三个类别中的所有类别都需要分别在 AndroidManifest.xml 中注册。

请注意,我们定义的intent-filter操作名称"restartservice",其中 Restarter.java 被注册为{{ 1}}。 这样可以确保在系统遇到具有给定动作名称的广播时,都会调用我们的自定义 receiver

BroadcastReciever

现在,如果应用程序已从任务管理器中终止,这应该再次重新启动服务。只要用户未从应用程序设置中通过 <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <receiver android:name="Restarter" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="restartservice" /> </intent-filter> </receiver> <activity android:name="MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name="YourService" android:enabled="true" > </service> </application> 应用程序,该服务将继续在后台运行。

更新:对Dr.jacky表示赞赏。上面提到的方法仅在调用服务的Force Stop时才有效,在某些时候可能不是,这是我不知道的。谢谢。

答案 2 :(得分:5)

在onstart命令中放置START_STICKY ...除非执行太多任务并且内核想要为它杀死它,否则此服务不会被杀死...

@Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.i("LocalService", "Received start id " + startId + ": " + intent);
            // We want this service to continue running until it is explicitly
            // stopped, so return sticky.
            return START_STICKY;
        }

答案 3 :(得分:4)

原因是您正在尝试使用IntentService。以下是API Docs

中的一行
  

IntentService执行以下操作:

     

在处理完所有启动请求后停止服务,所以你   永远不必调用stopSelf()。

因此,如果您希望您的服务无限期运行,我建议您扩展Service类。但是,这并不能保证您的服务无限期运行。如果内存处于低优先级状态,你的服务仍然有可能被内核杀死。所以你有两个选择:
1)通过调用startForeground()方法使其在前台运行 2)如果服务被杀,请重新启动服务。  以下是文档中的一部分示例,其中讨论了在服务被杀后重新启动服务

 public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

      // For each start request, send a message to start a job and deliver the 
      // start ID so we know which request we're stopping when we finish the job 
      Message msg = mServiceHandler.obtainMessage();
      msg.arg1 = startId;
      mServiceHandler.sendMessage(msg);

      // If we get killed, after returning from here, restart 
      return START_STICKY;
  }  

答案 4 :(得分:4)

在您的服务中,添加以下代码。

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

答案 5 :(得分:2)

您可以在清单中使用android:stopWithTask="false",这意味着即使用户通过从任务列表中删除应用来杀死应用,您的服务也不会停止。

 <service android:name=".service.StickyService"
                  android:stopWithTask="false"/>

答案 6 :(得分:0)

在您的服务类中使用 onTaskRemoved

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

        PendingIntent restartServicePendingIntent = PendingIntent.getService(
                getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
        AlarmManager alarmService = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        alarmService.set(AlarmManager.ELAPSED_REALTIME, elapsedRealtime() + 500,
                restartServicePendingIntent);
        Log.d("taskremoved", "task removed ");
        super.onTaskRemoved(rootIntent);
    }

答案 7 :(得分:0)

你试试下面的代码:

public class HeartbeartService extends Service {

private static final int SERVICE_NOTIFICATION_ID = 54321;
private static final String CHANNEL_ID = "Notification service";
private final LocalBinder mBinder = new LocalBinder();

Handler handler = new Handler();
private Runnable runnableCode = new Runnable() {
    @Override
    public void run() {
        Context context = getApplicationContext();

        Intent myIntent = new Intent(context, HeartbeatEventService.class);
        context.startService(myIntent);
        HeadlessJsTaskService.acquireWakeLockNow(context);
        handler.postDelayed(this, 2000);
        
    }
};

public class LocalBinder extends Binder {
    public HeartbeartService getService() {
        return HeartbeartService.this;
    }
}

private void createNotificationChannel() {
    // Create the NotificationChannel, but only on API 26+ because
    // the NotificationChannel class is new and not in the support library
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        int importance = NotificationManager.IMPORTANCE_MIN;
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "Notification service", importance);
        channel.setDescription("CHANEL DESCRIPTION");
        NotificationManager notificationManager = getSystemService(NotificationManager.class);
        notificationManager.createNotificationChannel(channel);
    }
}





@Override
public void onCreate() {
    super.onCreate();
   
}

@Override
public void onDestroy() {
    super.onDestroy();
    this.handler.removeCallbacks(this.runnableCode);
   
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    this.handler.post(this.runnableCode);
     createNotificationChannel();
     Intent notificationIntent = new Intent(this, MainActivity.class);
     PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent,
             PendingIntent.FLAG_CANCEL_CURRENT);
     Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
             .setContentTitle("Notification service")
             .setContentText("Running...")
             .setBadgeIconType(0)
             .setAutoCancel(false)
             .setOngoing(true)
             .build();

    startForeground(1, notification);
  
    return START_STICKY;
}

}