Android:应用程序被终止时保持服务运行

1cklez4t  于 2022-12-02  发布在  Android
关注(0)|答案(9)|浏览(271)

我想让一个IntentService在后台运行,即使应用程序被杀死。“杀死”我的意思是 * 长时间按home键 * -〉* 查看所有运行的应用程序 * -〉* 将我的应用程序扫到一边 * -〉* 应用程序被杀死 * 或 * 长时间按back键 * -〉* 应用程序被杀死 *
我的代码如下所示。在我的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();
    }
}

}

我看到服务在应用程序 * 打开 * 时运行。当我通过home按钮 * 最小化应用程序 * 时它仍然在运行。当我通过back按钮 * 关闭应用程序 * 时它仍然在运行。但是如果我如上所述杀死它,它将停止。我如何解决这个问题?

dnph8jn4

dnph8jn41#

  • 所有答案似乎都正确,因此我将继续并在此处给予完整答案。*

首先,最简单的方法是在手动终止 * 应用 * 时,在Android中启动广播,并定义自定义**BroadcastReceiver**以触发服务重启。
现在,让我们跳到代码。

YourService.java中创建服务

请注意onCreate()方法,在该方法中,我们为高于 Android Oreo 的构建版本启动前台服务的方式有所不同。这是因为最近引入了严格的通知策略,我们必须定义自己的 * 通知通道 * 才能正确显示它们。
onDestroy()方法中的this.sendBroadcast(broadcastIntent);是异步发送广播的语句,操作名为**"restartservice"。稍后我们将使用它作为触发器来重新启动服务。
这里我们定义了一个简单的Timer任务,它每
1秒Log**中打印一个计数器值,同时在每次打印时递增自身。

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中自定义的广播

您刚刚在YourService.java中定义的操作名为**"restartservice"的广播现在应该会触发一个方法,该方法将重新启动您的服务**。这是在Android中使用**BroadcastReceiver完成的。
我们覆盖了
BroadcastReceiver中的内置onRecieve()方法,以添加将重新启动服务的语句。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(),以便调用我们的*覆盖方法***。如果没有这样做,则服务将在应用被终止后 * 自动 * 结束,而不会调用我们在YourService.java**中修改的onDestroy()方法

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中注册它们

以上三个类都需要在**AndroidManifest.xml中单独注册。
请注意,我们将具有 action nameintent-filter定义为"restartservice",其中
Restarter.java注册为receiver。这确保了每当系统遇到具有给定 action name 的广播时,都会调用我们的自定义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>

如果应用程序已从任务管理器中终止,现在应该会重新启动您的服务。只要用户没有从应用程序设置中**Force Stop**应用程序,此服务就会继续在后台运行。

更新:感谢Dr.jacky指出了这一点。上面提到的方法只有在调用服务的onDestroy()时才有效,这可能不是情况,我不知道。谢谢。

0s7z1bwu

0s7z1bwu2#

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

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

并且有一个广播接收器,它将再次启动一个服务。我已经试过了。服务从所有类型的杀死重新启动。

i86rm4rw

i86rm4rw3#

在您的服务中,加入下列程式码。

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

tzxcd3kk4#

在onstart命令put 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;
        }
q3aa0525

q3aa05255#

这是因为您正在尝试使用IntentService。
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;
  }
62lalag4

62lalag46#

在服务类中使用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);
    }
w9apscun

w9apscun7#

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

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

ibrsph3r8#

你试试下面的代码:

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

}

lymgl2op

lymgl2op9#

下面是我如何触发我的警报时,应用程序是在后台,甚至杀死使用前台服务:

// Aware user about the foreground service
   private fun setForeGroundNotification() {
    val intent = Intent(this, AlarmForegroundService::class.java)
 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        startForegroundService(intent)
    } else {
        startService(intent)
    }
}

 

public class AlarmForegroundService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public void onCreate() {
    super.onCreate();
    final String CHANNELID = "Foreground Service ID";
    NotificationChannel channel;
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
        channel = new NotificationChannel(
                CHANNELID,
                CHANNELID,
                NotificationManager.IMPORTANCE_LOW
        );

        getSystemService(NotificationManager.class).createNotificationChannel(channel);
        Notification.Builder notification = new Notification.Builder(this, CHANNELID)
                .setContentTitle("App is running in background to check alarm")
                .setContentText("Checking Alarm..")
                .setAutoCancel(false)
                .setSmallIcon(R.mipmap.logo);
        startForeground(1, notification.build());
    }
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    int hours = intent.getIntExtra("hours", 0);
    int minutes = intent.getIntExtra("minutes", 0);

    //Firing alarm at selected wake up time
    Calendar calendar = Calendar.getInstance();
    calendar.set(Calendar.HOUR_OF_DAY, hours);
    calendar.set(Calendar.MINUTE, minutes);

    //To prevent alarm trigger for past time.
    if (calendar.before(Calendar.getInstance())) {
        calendar.add(Calendar.DATE, 1);
    }

    Intent intent1 = new Intent(this, AlarmReceiver.class);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(
            this,
            0,
            intent1,
            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
    );

    AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), (1000 * 60 * 60 * 24), pendingIntent);
    return super.onStartCommand(intent, flags, startId);
}

}

class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
    val intent = Intent(context, AlarmIntentService::class.java)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        context!!.startForegroundService(intent)
    } else {
        context!!.startService(intent)
    }
}

}

public class AlarmIntentService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public void onCreate() {
    super.onCreate();
    NotificationChannel channel = null;
    int notificationId = 123;
    long[] vibPattern = {1000, 1000, 1000, 1000};

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        channel = new NotificationChannel("channel2",
                "Sleep Alarm",
                NotificationManager.IMPORTANCE_HIGH);
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        channel.setVibrationPattern(vibPattern);
    }

    NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        manager.createNotificationChannel(channel);
    }

    //Creating the notification object
    NotificationCompat.Builder notification = new NotificationCompat.Builder(this, "channel2");
    notification.setSmallIcon(R.drawable.ic_bell);
    notification.setContentTitle("Wake up Alarm!!");
    notification.setContentText("It's time to get up");
    notification.setAutoCancel(true);
    notification.setSound(null);
    //notification.build().flags |= Notification.FLAG_AUTO_CANCEL;

    //Action button handling
    Intent onDismiss = new Intent(this, DismissReceiver.class);
    onDismiss.putExtra("NOTIFICATION_ID", notificationId);
    Intent onSnooze = new Intent(this, SnoozeReceiver.class);

    SharedPreferences sh = getSharedPreferences("MySharedPref", Context.MODE_PRIVATE);
    int snooze = sh.getInt("snooze", 0);

    PendingIntent dismissPendingIntent = PendingIntent.getBroadcast(this, 1, onDismiss, PendingIntent.FLAG_IMMUTABLE);
    if (snooze == 0) {
        notification.setContentIntent(dismissPendingIntent);
        notification.addAction(R.drawable.ic_cross, "Dismiss", dismissPendingIntent);
    } else {
        PendingIntent snoozePendingIntent = PendingIntent.getBroadcast(this, 1, onSnooze, PendingIntent.FLAG_IMMUTABLE);
        notification.setContentIntent(dismissPendingIntent);
        notification.setContentIntent(snoozePendingIntent);
        notification.addAction(R.drawable.ic_cross, "Dismiss", dismissPendingIntent);
        notification.addAction(R.drawable.ic_cross, "Snooze", snoozePendingIntent);
    }

    //To trigger the chosen alarm ringtone
    Intent startIntent = new Intent(this, RingtonePlayingService.class);
    this.startService(startIntent);

    manager.notify(notificationId, notification.build());
    startForeground(notificationId, notification.build());
}

}

相关问题