Android:当应用被杀死时,保持服务运行 [英] Android: keep Service running when app is killed

查看:165
本文介绍了Android:当应用被杀死时,保持服务运行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

即使应用程序被杀死,我也希望保持IntentService在后台运行. 杀死"的意思是长时间按主页按钮-> 查看所有正在运行的应用-> 将我的应用刷到一边-> 应用被杀死长按后退按钮-> 应用被杀死

I want to keep a IntentService running in background even when the app is killed. And by "killed" I mean press home-button for a long time -> see all running apps -> swipe my app aside -> app killed OR press back-button for a long time -> app killed

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

My code goes as follows. In my MainActivity:

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

在我的MyService中:

In my 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();
    }
}

}

我看到当应用程序打开时,该服务正在运行.当我通过主屏幕按钮最小化应用程序时,它仍在运行.当我通过后退按钮关闭应用程序时,它仍在运行.但是,如果我如上所述杀死它,它将停止.我该如何解决?

I see that the service is running when the app is open. It's still running when I minimize the app via home-button. It's still running when I close the app via back-button. But it will stop if I kill it as mentioned above. How do I solve this?

推荐答案

所有答案似乎都是正确,所以我将继续给出完整答案在这里.

All the answers seem correct so I'll go ahead and give a complete answer here.

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

Firstly, the easiest way to do what you are trying to do is launch a Broadcast in Android when the app is killed manually, and define a custom BroadcastReceiver to trigger a service restart following that.

现在让我们进入代码.

YourService.java

Create your Service in YourService.java

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

Note the onCreate() method, where we are starting a foreground service differently for Build versions greater than Android Oreo. This because of the strict notification policies introduced recently where we have to define our own notification channel to display them correctly.

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

The this.sendBroadcast(broadcastIntent); in the onDestroy() method is the statement which asynchronously sends a broadcast with the action name "restartservice". We'll be using this later as a trigger to restart our service.

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

Here we have defined a simple Timer task, which prints a counter value every 1 second in the Log while incrementing itself every time it prints.

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


Create a Broadcast Receiver to respond to your custom defined broadcasts in Restarter.java

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

The broadcast with the action name "restartservice" which you just defined in YourService.java is now supposed to trigger a method which will restart your service. This is done using BroadcastReceiver in Android.

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

We override the built-in onRecieve() method in BroadcastReceiver to add the statement which will restart the service. The startService() will not work as intended in and above Android Oreo 8.1, as strict background policies will soon terminate the service after restart once the app is killed. Therefore we use the startForegroundService() for higher versions and show a continuous notification to keep the service running.

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以在应用启动时调用该服务.


Define your MainActivity.java to call the service on app start.

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

Here we define a separate isMyServiceRunning() method to check the current status of the background service. If the service is not running, we start it by using startService().

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

Since the app is already running in foreground, we need not launch the service as a foreground service to prevent itself from being terminated.

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

Note that in onDestroy() we are dedicatedly calling stopService(), so that our overridden method gets invoked. If this was not done, then the service would have ended automatically after app is killed without invoking our modified onDestroy() method in 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);
        Intent broadcastIntent = new Intent();
        broadcastIntent.setAction("restartservice");
        broadcastIntent.setClass(this, Restarter.class);
        this.sendBroadcast(broadcastIntent);
        super.onDestroy();
    }
}


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


Finally register them in your AndroidManifest.xml

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

All of the above three classes need to be separately registered in AndroidManifest.xml.

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

Note that we define an intent-filter with the action name as "restartservice" where the Restarter.java is registered as a receiver. This ensures that our custom BroadcastReciever is called whenever the system encounters a broadcast with the given action name.

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

现在,如果应用程序已从任务管理器中终止,这应该再次重新启动您的服务.只要用户未从应用程序设置中通过 Force Stop 应用程序,该服务就会在后台继续运行.

This should now restart your service again if the app was killed from the task-manager. This service will keep on running in background as long as the user doesn't Force Stop the app from Application Settings.

更新:对 Dr.jacky 表示感谢.上面提到的方法仅在调用服务的onDestroy()时才有效,在某些情况下可能不是,这是我不知道的.谢谢.

UPDATE: Kudos to Dr.jacky for pointing it out. The above mentioned way will only work if the onDestroy() of the service is called, which might not be the case certain times, which I was unaware of. Thanks.

这篇关于Android:当应用被杀死时,保持服务运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆