Android 棒棒糖中的服务未重新启动
Android service not restarting in lollipop
在我的应用程序中,我在后台使用基于位置的服务。所以我需要在它被破坏时重新启动我的服务。
但是我在 logcat
中收到了这条消息
ProcessRecord{320afaf6 20614:com.odoo.crm:my_odoo_gps_service/u0a391} 的虚假死亡,20614 的 curProc:null
我的服务onTaskRemoved
@Override
public void onTaskRemoved(Intent rootIntent) {
System.out.println("onTaskRemoved called");
Intent restartServiceIntent = new Intent(App.getAppContext(), this.getClass());
restartServiceIntent.setPackage(getPackageName());
PendingIntent restartServicePendingIntent =
PendingIntent.getService(App.getAppContext(), 1, restartServiceIntent,
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService =
(AlarmManager) App.getAppContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000,
restartServicePendingIntent);
}
我的服务onDestroy
@Override
public void onDestroy() {
System.out.println("destroy service");
super.onDestroy();
wakeLock.release();
}
我的服务onStartCommand
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
我不知道错误是什么。我在 google 和 Whosebug 中都进行了搜索。
都是指Service.START_STICKY。不过我已经用过了
相同的服务重启在 KitKat 中有效,但有一些延迟(~5 分钟)。
感谢任何帮助。
您可以使用 BroadcasteReceiver
来重新启动它,它处理从您的服务 onDestroy()
发送的广播。
如何操作:
StickyService.java
public class StickyService extends Service
{
@Override
public IBinder onBind(Intent arg0) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
sendBroadcast(new Intent("IWillStartAuto"));
}
@Override
public void onDestroy() {
super.onDestroy();
sendBroadcast(new Intent("IWillStartAuto"));
}
}
RestartServiceReceiver.java
public class RestartServiceReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context.getApplicationContext(), StickyService.class));
}
}
在清单文件中声明组件:
<service android:name=".StickyService" >
</service>
<receiver android:name=".RestartServiceReceiver" >
<intent-filter>
<action android:name="IWillStartAuto" >
</action>
</intent-filter>
</receiver>
希望对您有所帮助。
您在 onTaskRemoved
中的代码阻止系统 运行 killProcess
命令。 Kitkat
上的延迟是由使用 alarmService.set
引起的,它是 inexact from API 19. Use setExact。
如果你有一个service
想要保鲜,建议你附上一个notification
让它成为foreground
。这样它被杀的可能性就会降低。
如何检查 issocketalive 套接字是否已连接?
如果生成 sockettimeoutexception,则尝试设置 getinputstream 和 getoutputstream。
其他可能是套接字未正确关闭的问题。
因此,如果可能的话,请将您的套接字代码放在这里
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Environment;
import android.os.IBinder;
import android.support.v7.app.NotificationCompat;
import java.io.File;
import java.io.IOException;
import activity.MainActivity;
import activity.R;
import fragment.MainFragment;
public class MyService extends Service {
public static final int NOTIFICATION_CODE = 1;
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(NOTIFICATION_CODE, getNotification());
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
stopForeground(true);
super.onDestroy();
}
@Override
public boolean stopService(Intent name) {
return super.stopService(name);
}
/**
* Create and return a simple notification.
*/
private Notification getNotification() {
Notification notification;
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setColor(getResources()
.getColor(R.color.material_deep_teal_500))
.setAutoCancel(true);
notification = builder.build();
notification.flags = Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTO_CANCEL;
return notification;
}
}
您可以修改此代码以满足您的需要,但这是启动前台服务的基本结构。如果被杀,它会重新启动。
这对我有用
在 android:allowBackup="false" 的清单文件的应用程序标签中添加此属性。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="false"
tools:replace="android:allowBackup">
</application>
</manifest>
在 Android 中始终 运行 在后台提供服务的想法在 99% 的情况下都是错误的。
系统需要 "shut down" CPU,并切换到低电量使用模式。
你是说你有基于位置的服务。我假设您正在使用 Google 播放服务 FusedLocationProvider
,如果不是 您应该 。
FusedLocationProvider
允许您使用 PendingIntent
注册位置更改。这意味着您的服务不需要一直 运行,它只需要注册位置更改,然后在新位置出现时做出反应并执行其操作。
参见 FusedLocationProviderApi
official documentation。
开始侦听位置更新
- 使用
LocationServices.API
API 连接到 GoogleClient
- 根据您的需要构建您的
LocationRequest
(参见 the doc)
- 使用
PendingIntent
版本调用 requestLocationUpdates()
停止收听
- 使用
LocationServices.API
API 连接到 GoogleClient
- 使用相同的
PendingIntent
调用 removeLocationUpdates()
您的 PendingIntent 可以启动另一个服务来处理新位置。
例如,从服务执行此操作:
public void startMonitoringLocation(Context context) {
GoogleApiClient client = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API)
.build()
ConnectionResult connectionResult = mApiClient.blockingConnect();
if (connectionResult.isSuccess()) {
LocationServices.FusedLocationApi
.requestLocationUpdates(client, buildLocationRequest(), buildPendingIntent(context));
} else {
handleConnectionFailed(context);
}
}
然后服务可以立即停止。
此代码第一次 运行 会失败。与 google 客户端的连接通常需要用户执行一些操作。如果是这种情况,ConnectionResult.hasResolution()
方法将 return 为真。否则原因是其他原因,您无法从中恢复。这意味着您唯一能做的就是通知用户该功能将无法使用或有一个很好的后备。
ConnectionResult.getResolution()
给你一个 PendingIntent
你需要在 Activity
上使用 Activity
和 startIntentSenderForResult()
方法来解决这个意图。所以你会创建一个 Notification
开始你的 Activity
来解决这个问题,最后再次调用你的 Service
。
我通常只是开始一个 Activity
专门做所有的工作。它更容易,但你不想在其中调用 connectBlocking()
。查看 this 如何操作。
您可能会问为什么不直接在 Activity
中请求位置更新。这实际上完全没问题,除非您需要位置监视器随设备自动启动,即使用户没有明确打开应用程序也是如此。
<receiver android:name=".BootCompletedBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
这样您就可以 运行 您的服务在设备重启时连接并请求位置更新。
关于如何构建位置请求的示例:
public LocationRequest buildLocationRequest() {
LocationRequest locRequest = LocationRequest.create();
// Use high accuracy
locRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// how often do you need to check for the location
// (this is an indication, it's not exact)
locRequest.setInterval(REQUIRED_INTERVAL_SEC * 1000);
// if others services requires the location more often
// you can still receive those updates, if you do not want
// too many consider setting this lower limit
locRequest.setFastestInterval(FASTEST_INTERVAL_SEC * 1000);
// do you care if the user moved 1 meter? or if he move 50? 1000?
// this is, again, an indication
locRequest.setSmallestDisplacement(SMALLEST_DISPLACEMENT_METERS);
return locRequest;
}
您的未决意向:
public PendingIntent buildPendingIntent(Context context) {
Intent intent = new Intent(context, LocationUpdateHandlerService.class);
intent.setAction(ACTION_LOCATION_UPDATE);
intent.setPackage(context.getPackageName());
return PendingIntent.getService(context, REQUEST_CODE, intent, PendingIntent.FLAG_CANCEL_CURRENT);
}
如果您需要在后台工作,您的 LocationUpdateHandlerService
可以是 IntentService
:
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
Bundle extras = intent.getExtras();
if (extras != null && extras.containsKey(FusedLocationProviderApi.KEY_LOCATION_CHANGED)) {
Location location = extras.getParcelable(FusedLocationProviderApi.KEY_LOCATION_CHANGED);
handleLocationChanged(location);
} else {
Log.w(TAG, "Didn't receive any location update in the receiver");
}
}
}
但也可以是广播或任何适合您的内容。
最后在Evernote JobService
的帮助下实现了
Github link - https://github.com/evernote/android-job
第一步:添加evernote jobservice依赖
implementation 'com.evernote:android-job:1.3.0-alpha03'
步骤 2:创建 DemoJobCreator.java class
public class DemoJobCreator implements JobCreator {
@Override
@Nullable
public Job create(@NonNull String tag) {
switch (tag) {
case DemoSyncJob.TAG:
return new DemoSyncJob();
default:
return null;
}
}
}
步骤 3:创建 DemoSyncJob.java class
public class DemoSyncJob extends Job {
public static final String TAG = ">>>> job_demo_tag";
@Override
@NonNull
protected Result onRunJob(Params params) {
// run your job here
Log.d(TAG, "onRunJob: ");
if(!isMyServiceRunning(this.getContext(), TestService.class)){
Intent intent=new Intent(context,TestService.class);
context.startService(intent);
}
scheduleJob();
return Job.Result.SUCCESS;
}
public static void scheduleJob() {
new JobRequest.Builder(DemoSyncJob.TAG)
.setExecutionWindow(2_000L, 2_000L)
//.setPeriodic(900000) -> recommended. but it will work after 15 min (if you used this no need scheduleJob(); inside onRunJob();)
.build()
.schedule();
}
public static boolean isMyServiceRunning(Context context, Class<?> serviceClass) {
try {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
}catch (Exception e){
Log.e(TAG, "isMyServiceRunning: ",e );
}
return false;
}
}
第 4 步:在您的应用程序文件中(如果不可用,请创建它)在 onCreate()
中添加以下行
JobManager.create(this).addJobCreator(new DemoJobCreator());
第 5 步:最后在您的 Activity
中启动 JobService
DemoSyncJob.scheduleJob();
此 JobService 将检查服务 运行 是否(每 2 秒一次)如果服务不是 运行 它将重新启动服务。
免责声明:这可能不是正确的解决方案。但它会 100% 工作。
我希望它至少对以后的任何人有所帮助。
在我的应用程序中,我在后台使用基于位置的服务。所以我需要在它被破坏时重新启动我的服务。
但是我在 logcat
中收到了这条消息ProcessRecord{320afaf6 20614:com.odoo.crm:my_odoo_gps_service/u0a391} 的虚假死亡,20614 的 curProc:null
我的服务onTaskRemoved
@Override
public void onTaskRemoved(Intent rootIntent) {
System.out.println("onTaskRemoved called");
Intent restartServiceIntent = new Intent(App.getAppContext(), this.getClass());
restartServiceIntent.setPackage(getPackageName());
PendingIntent restartServicePendingIntent =
PendingIntent.getService(App.getAppContext(), 1, restartServiceIntent,
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService =
(AlarmManager) App.getAppContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000,
restartServicePendingIntent);
}
我的服务onDestroy
@Override
public void onDestroy() {
System.out.println("destroy service");
super.onDestroy();
wakeLock.release();
}
我的服务onStartCommand
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
我不知道错误是什么。我在 google 和 Whosebug 中都进行了搜索。 都是指Service.START_STICKY。不过我已经用过了
相同的服务重启在 KitKat 中有效,但有一些延迟(~5 分钟)。
感谢任何帮助。
您可以使用 BroadcasteReceiver
来重新启动它,它处理从您的服务 onDestroy()
发送的广播。
如何操作:
StickyService.java
public class StickyService extends Service
{
@Override
public IBinder onBind(Intent arg0) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
sendBroadcast(new Intent("IWillStartAuto"));
}
@Override
public void onDestroy() {
super.onDestroy();
sendBroadcast(new Intent("IWillStartAuto"));
}
}
RestartServiceReceiver.java
public class RestartServiceReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context.getApplicationContext(), StickyService.class));
}
}
在清单文件中声明组件:
<service android:name=".StickyService" >
</service>
<receiver android:name=".RestartServiceReceiver" >
<intent-filter>
<action android:name="IWillStartAuto" >
</action>
</intent-filter>
</receiver>
希望对您有所帮助。
您在 onTaskRemoved
中的代码阻止系统 运行 killProcess
命令。 Kitkat
上的延迟是由使用 alarmService.set
引起的,它是 inexact from API 19. Use setExact。
如果你有一个service
想要保鲜,建议你附上一个notification
让它成为foreground
。这样它被杀的可能性就会降低。
如何检查 issocketalive 套接字是否已连接? 如果生成 sockettimeoutexception,则尝试设置 getinputstream 和 getoutputstream。 其他可能是套接字未正确关闭的问题。 因此,如果可能的话,请将您的套接字代码放在这里
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Environment;
import android.os.IBinder;
import android.support.v7.app.NotificationCompat;
import java.io.File;
import java.io.IOException;
import activity.MainActivity;
import activity.R;
import fragment.MainFragment;
public class MyService extends Service {
public static final int NOTIFICATION_CODE = 1;
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(NOTIFICATION_CODE, getNotification());
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
stopForeground(true);
super.onDestroy();
}
@Override
public boolean stopService(Intent name) {
return super.stopService(name);
}
/**
* Create and return a simple notification.
*/
private Notification getNotification() {
Notification notification;
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setColor(getResources()
.getColor(R.color.material_deep_teal_500))
.setAutoCancel(true);
notification = builder.build();
notification.flags = Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTO_CANCEL;
return notification;
}
}
您可以修改此代码以满足您的需要,但这是启动前台服务的基本结构。如果被杀,它会重新启动。
这对我有用
在 android:allowBackup="false" 的清单文件的应用程序标签中添加此属性。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="false"
tools:replace="android:allowBackup">
</application>
</manifest>
在 Android 中始终 运行 在后台提供服务的想法在 99% 的情况下都是错误的。
系统需要 "shut down" CPU,并切换到低电量使用模式。
你是说你有基于位置的服务。我假设您正在使用 Google 播放服务 FusedLocationProvider
,如果不是 您应该 。
FusedLocationProvider
允许您使用 PendingIntent
注册位置更改。这意味着您的服务不需要一直 运行,它只需要注册位置更改,然后在新位置出现时做出反应并执行其操作。
参见 FusedLocationProviderApi
official documentation。
开始侦听位置更新
- 使用
LocationServices.API
API 连接到 - 根据您的需要构建您的
LocationRequest
(参见 the doc) - 使用
PendingIntent
版本调用requestLocationUpdates()
GoogleClient
停止收听
- 使用
LocationServices.API
API 连接到 - 使用相同的
PendingIntent
调用
GoogleClient
removeLocationUpdates()
您的 PendingIntent 可以启动另一个服务来处理新位置。
例如,从服务执行此操作:
public void startMonitoringLocation(Context context) {
GoogleApiClient client = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API)
.build()
ConnectionResult connectionResult = mApiClient.blockingConnect();
if (connectionResult.isSuccess()) {
LocationServices.FusedLocationApi
.requestLocationUpdates(client, buildLocationRequest(), buildPendingIntent(context));
} else {
handleConnectionFailed(context);
}
}
然后服务可以立即停止。
此代码第一次 运行 会失败。与 google 客户端的连接通常需要用户执行一些操作。如果是这种情况,ConnectionResult.hasResolution()
方法将 return 为真。否则原因是其他原因,您无法从中恢复。这意味着您唯一能做的就是通知用户该功能将无法使用或有一个很好的后备。
ConnectionResult.getResolution()
给你一个 PendingIntent
你需要在 Activity
上使用 Activity
和 startIntentSenderForResult()
方法来解决这个意图。所以你会创建一个 Notification
开始你的 Activity
来解决这个问题,最后再次调用你的 Service
。
我通常只是开始一个 Activity
专门做所有的工作。它更容易,但你不想在其中调用 connectBlocking()
。查看 this 如何操作。
您可能会问为什么不直接在 Activity
中请求位置更新。这实际上完全没问题,除非您需要位置监视器随设备自动启动,即使用户没有明确打开应用程序也是如此。
<receiver android:name=".BootCompletedBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
这样您就可以 运行 您的服务在设备重启时连接并请求位置更新。
关于如何构建位置请求的示例:
public LocationRequest buildLocationRequest() {
LocationRequest locRequest = LocationRequest.create();
// Use high accuracy
locRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// how often do you need to check for the location
// (this is an indication, it's not exact)
locRequest.setInterval(REQUIRED_INTERVAL_SEC * 1000);
// if others services requires the location more often
// you can still receive those updates, if you do not want
// too many consider setting this lower limit
locRequest.setFastestInterval(FASTEST_INTERVAL_SEC * 1000);
// do you care if the user moved 1 meter? or if he move 50? 1000?
// this is, again, an indication
locRequest.setSmallestDisplacement(SMALLEST_DISPLACEMENT_METERS);
return locRequest;
}
您的未决意向:
public PendingIntent buildPendingIntent(Context context) {
Intent intent = new Intent(context, LocationUpdateHandlerService.class);
intent.setAction(ACTION_LOCATION_UPDATE);
intent.setPackage(context.getPackageName());
return PendingIntent.getService(context, REQUEST_CODE, intent, PendingIntent.FLAG_CANCEL_CURRENT);
}
如果您需要在后台工作,您的 LocationUpdateHandlerService
可以是 IntentService
:
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
Bundle extras = intent.getExtras();
if (extras != null && extras.containsKey(FusedLocationProviderApi.KEY_LOCATION_CHANGED)) {
Location location = extras.getParcelable(FusedLocationProviderApi.KEY_LOCATION_CHANGED);
handleLocationChanged(location);
} else {
Log.w(TAG, "Didn't receive any location update in the receiver");
}
}
}
但也可以是广播或任何适合您的内容。
最后在Evernote JobService
的帮助下实现了Github link - https://github.com/evernote/android-job
第一步:添加evernote jobservice依赖
implementation 'com.evernote:android-job:1.3.0-alpha03'
步骤 2:创建 DemoJobCreator.java class
public class DemoJobCreator implements JobCreator {
@Override
@Nullable
public Job create(@NonNull String tag) {
switch (tag) {
case DemoSyncJob.TAG:
return new DemoSyncJob();
default:
return null;
}
}
}
步骤 3:创建 DemoSyncJob.java class
public class DemoSyncJob extends Job {
public static final String TAG = ">>>> job_demo_tag";
@Override
@NonNull
protected Result onRunJob(Params params) {
// run your job here
Log.d(TAG, "onRunJob: ");
if(!isMyServiceRunning(this.getContext(), TestService.class)){
Intent intent=new Intent(context,TestService.class);
context.startService(intent);
}
scheduleJob();
return Job.Result.SUCCESS;
}
public static void scheduleJob() {
new JobRequest.Builder(DemoSyncJob.TAG)
.setExecutionWindow(2_000L, 2_000L)
//.setPeriodic(900000) -> recommended. but it will work after 15 min (if you used this no need scheduleJob(); inside onRunJob();)
.build()
.schedule();
}
public static boolean isMyServiceRunning(Context context, Class<?> serviceClass) {
try {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
}catch (Exception e){
Log.e(TAG, "isMyServiceRunning: ",e );
}
return false;
}
}
第 4 步:在您的应用程序文件中(如果不可用,请创建它)在 onCreate()
中添加以下行 JobManager.create(this).addJobCreator(new DemoJobCreator());
第 5 步:最后在您的 Activity
中启动 JobServiceDemoSyncJob.scheduleJob();
此 JobService 将检查服务 运行 是否(每 2 秒一次)如果服务不是 运行 它将重新启动服务。
免责声明:这可能不是正确的解决方案。但它会 100% 工作。
我希望它至少对以后的任何人有所帮助。