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

开始侦听位置更新

  1. 使用 LocationServices.API API
  2. 连接到 GoogleClient
  3. 根据您的需要构建您的 LocationRequest(参见 the doc
  4. 使用 PendingIntent 版本调用 requestLocationUpdates()

停止收听

  1. 使用 LocationServices.API API
  2. 连接到 GoogleClient
  3. 使用相同的 PendingIntent
  4. 调用 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 上使用 ActivitystartIntentSenderForResult() 方法来解决这个意图。所以你会创建一个 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% 工作。

我希望它至少对以后的任何人有所帮助。