Ich möchte IntentService
im Hintergrund laufen, auch wenn die App beendet ist. Und mit "getötet" meine ich, dass Sie lange auf die Home-Taste drücken -> alle laufenden Apps anzeigen -> meine App beiseite wischen -> App getötet ODER die Zurück-Taste für eine lange Zeit drücken -> App getötet
Mein Code lautet wie folgt. In meiner Hauptaktivität:
Intent intent = new Intent(this, MyService.class);
this.startService(intent);
In meinem 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();
}
}
}
Ich sehe, dass der Dienst ausgeführt wird, wenn die App geöffnet ist . Es läuft noch, wenn ich die App über die Home-Taste minimiere . Es läuft noch, wenn ich die App über den Zurück-Button schließe. Aber es wird aufhören, wenn ich es wie oben erwähnt töte. Wie löse ich das?
android
service
background
intentservice
user2078872
quelle
quelle
Antworten:
Alle Antworten scheinen richtig zu sein, also werde ich weitermachen und eine vollständige geben hier Antwort geben.
Erstens ist der einfachste Weg, das zu tun, was Sie versuchen, eine Sendung in Android zu starten, wenn die App manuell beendet wird, und eine benutzerdefinierte zu definieren
BroadcastReceiver
zu , um anschließend einen Neustart des Dienstes auszulösen.Lassen Sie uns nun in den Code springen.
Erstellen Sie Ihren Service in
YourService.java
Beachten Sie die
onCreate()
Methode, bei der wir einen Vordergrunddienst für Build-Versionen, die größer als Android Oreo sind, anders starten . Dies liegt an den kürzlich eingeführten strengen Benachrichtigungsrichtlinien, bei denen wir unseren eigenen Benachrichtigungskanal definieren müssen , um sie korrekt anzuzeigen.Die
this.sendBroadcast(broadcastIntent);
in deronDestroy()
Methode ist die Anweisung, die asynchron eine Sendung mit dem Aktionsnamen sendet"restartservice"
. Wir werden dies später als Auslöser verwenden, um unseren Dienst neu zu starten.Hier haben wir eine einfache Timer - Task definiert, der druckt einen Zählerwert jedes 1 Sekunde in die
Log
während selbst druckt es jedes Mal erhöht wird .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; } }
Erstellen Sie einen Broadcast-Empfänger, um auf Ihre benutzerdefinierten Broadcasts in zu antworten
Restarter.java
Die Sendung mit dem Aktionsnamen,
"restartservice"
den Sie gerade definiert haben,YourService.java
soll nun eine Methode auslösen, mit der Ihr Dienst neu gestartet wird . Dies geschieht mitBroadcastReceiver
Android.Wir überschreiben die integrierte
onRecieve()
MethodeBroadcastReceiver
, um die Anweisung hinzuzufügen, mit der der Dienst neu gestartet wird. DasstartService()
wird nicht wie vorgesehen in und über Android Oreo 8.1, als strenge Hintergrund Politik wird in Kürze den Dienst nach dem Neustart beenden , sobald die App getötet wird. Daher verwenden wir diestartForegroundService()
für höhere Versionen und zeigen eine fortlaufende Benachrichtigung an, um den Dienst am Laufen zu halten.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)); } } }
Definieren Sie
MainActivity.java
, dass Sie den Dienst beim Start der App aufrufen möchten.Hier definieren wir eine separate
isMyServiceRunning()
Methode, um den aktuellen Status des Hintergrunddienstes zu überprüfen. Wenn der Dienst nicht ausgeführt wird, starten wir ihn mitstartService()
.Da die App bereits im Vordergrund ausgeführt wird, müssen wir den Dienst nicht als Vordergrunddienst starten , um zu verhindern, dass er selbst beendet wird.
Beachten Sie, dass
onDestroy()
wir in dediziert aufrufenstopService()
, damit unsere überschriebene Methode aufgerufen wird. Wenn dies nicht getan worden wäre, hätte der Dienst automatisch beendet , nachdem die App beendet wurde, ohne unsere geänderteonDestroy()
Methode in aufzurufenYourService.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(); } }
Registrieren Sie sie schließlich in Ihrem
AndroidManifest.xml
Alle oben genannten drei Klassen müssen separat angemeldet werden
AndroidManifest.xml
.Beachten Sie, dass wir eine Definition
intent-filter
mit dem Aktionsnamen wie ,"restartservice"
wo derRestarter.java
als registriert istreceiver
. Dies stellt sicher, dass unser BenutzerdefiniertBroadcastReciever
aufgerufen wird, wenn das System auf eine Sendung mit dem angegebenen Aktionsnamen stößt .<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>
Dies sollte Ihren Dienst jetzt erneut starten, wenn die App vom Task-Manager beendet wurde. Dieser Dienst wird im Hintergrund ausgeführt, solange der Benutzer
Force Stop
die App nicht über die Anwendungseinstellungen verwendet .UPDATE: Ein großes Lob an Dr. Jacky für den Hinweis. Der oben erwähnte Weg funktioniert nur, wenn der
onDestroy()
Dienst aufgerufen wird, was zu bestimmten Zeiten, die mir nicht bekannt waren , möglicherweise nicht der Fall ist. Vielen Dank.quelle
onDestroy
möglicherweise nicht aufgerufen: stackoverflow.com/a/7237522/421467Wenn Ihr Dienst von Ihrer App gestartet wird, wird Ihr Dienst tatsächlich im Hauptprozess ausgeführt. Wenn die App beendet wird, wird auch der Dienst gestoppt. Sie können also eine Sendung von der
onTaskRemoved
Methode Ihres Dienstes wie folgt senden :Intent intent = new Intent("com.android.ServiceStopped"); sendBroadcast(intent);
und einen Rundfunkempfänger haben, der erneut einen Dienst startet. Ich habe es versucht. Der Dienst wird von allen Arten von Kills neu gestartet.
quelle
onTaskRemoved
Formulardienst auf. Es tötet es einfach.Fügen Sie in Ihrem Dienst den folgenden Code hinzu.
@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); }
quelle
inside onstart command put
START_STICKY
... Dieser Dienst wird nicht beendet, es sei denn, er erledigt zu viele Aufgaben und der Kernel möchte ihn dafür beenden ...@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; }
quelle
Der Grund dafür ist, dass Sie versuchen, einen IntentService zu verwenden. Hier ist die Zeile aus den API-Dokumenten
Wenn Sie also möchten, dass Ihr Dienst auf unbestimmte Zeit ausgeführt wird, empfehlen wir Ihnen, stattdessen die Dienstklasse zu erweitern. Dies garantiert jedoch nicht, dass Ihr Dienst auf unbestimmte Zeit ausgeführt wird. Ihr Dienst hat weiterhin die Möglichkeit, vom Kernel in einem Zustand mit geringem Arbeitsspeicher beendet zu werden, wenn er eine niedrige Priorität hat. Sie haben also zwei Möglichkeiten:
1) Lassen Sie ihn durch Aufrufen der
startForeground()
Methode im Vordergrund laufen .2) Starten Sie den Dienst neu, wenn er beendet wird. Hier ist ein Teil des Beispiels aus den Dokumenten, in denen über den Neustart des Dienstes nach dessen Beendigung gesprochen wird
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; }
quelle
Sie können das
android:stopWithTask="false"
Manifest als Balg verwenden. Dies bedeutet, dass Ihr Dienst nicht beendet wird, selbst wenn der Benutzer die App beendet, indem er sie von der Aufgabenliste entfernt.<service android:name=".service.StickyService" android:stopWithTask="false"/>
quelle