简体   繁体   English

Android服务没有在棒棒糖中重新启动

[英]Android service not restarting in lollipop

In my application, I use location based service in background. 在我的应用程序中,我在后台使用基于位置的服务。 So I need to restart my service when it gets destroyed. 因此我需要在销毁时重新启动我的服务。

But I got this message in logcat 但是我在logcat中收到了这条消息

Spurious death for ProcessRecord{320afaf6 20614:com.odoo.crm:my_odoo_gps_service/u0a391}, curProc for 20614: null ProcessRecord的虚假死亡{320afaf6 20614:com.odoo.crm:my_odoo_gps_service / u0a391},curProc for 20614:null

My service onTaskRemoved 我的服务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);
 }

My service onDestroy 我的服务onDestroy

@Override
public void onDestroy() {
    System.out.println("destroy service");
    super.onDestroy();
    wakeLock.release();
}

My service onStartCommand 我的服务onStartCommand

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
        return Service.START_STICKY;
}

I don`t know what is the error. 我不知道错误是什么。 I searched both in google & stackoverflow. 我在google和stackoverflow中都进行了搜索。 All of them refer Service.START_STICKY . 所有这些都引用了Service.START_STICKY but I already used it. 但我已经用过了。

Same service restart works in KitKat, but with some delay(~5 mins). 相同的服务重启在KitKat中有效,但有一些延迟(约5分钟)。

Any help is appreciated. 任何帮助表示赞赏。

You can restart it by using a BroadcasteReceiver which handles the broadcast sent from onDestroy() of your service. 您可以使用BroadcasteReceiver重新启动它, BroadcasteReceiver处理从您的服务的onDestroy()发送的广播。

How to do this: 这个怎么做:

StickyService.java 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 RestartServiceReceiver.java

public class RestartServiceReceiver extends BroadcastReceiver
{

    @Override
    public void onReceive(Context context, Intent intent) {
    context.startService(new Intent(context.getApplicationContext(), StickyService.class));

    }

}

Declare the components in manifest file: 声明清单文件中的组件

    <service android:name=".StickyService" >
    </service>

    <receiver android:name=".RestartServiceReceiver" >
        <intent-filter>
            <action android:name="IWillStartAuto" >
            </action>
        </intent-filter>
    </receiver>

Hope this will help you. 希望这会帮助你。

Your code in onTaskRemoved is preventing the system to run the killProcess commands. onTaskRemoved代码阻止系统运行killProcess命令。 The delay on Kitkat is caused by using alarmService.set , which is inexact from API 19. Use setExact instead. 该延迟Kitkat通过使用引起alarmService.set ,这是不精确的从API 19. setExact代替。

If you have a service that you want to keep alive, it is recommended that you attach a notification to it and make it foreground . 如果您有一个要保持活动的service ,建议您向其附加notification并使其成为foreground That way the likeliness of it being killed would be lowered. 这样,被杀的可能性就会降低。

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


}

You can modify this code to accomodate your needs but this is the basic structure to start foreground service. 您可以修改此代码以满足您的需求,但这是启动前台服务的基本结构。 Which restarts if gets killed. 如果被杀,会重新启动。

how you check issocketalive that socket is connected or not ? 你如何检查套接字是否连接的issocketalive? if sockettimeoutexception is generated then try to on set getinputstream and getoutputstream. 如果生成了sockettimeoutexception,则尝试设置getinputstream和getoutputstream。 other issue that may be socket not closed properly. 其他问题可能是套接字未正确关闭。 So if possible then put your socket code here 所以,如果可能的话,请将套接字代码放在这

this worked for me 这对我有用

Add this attribute in android:allowBackup="false" in manifest file in application tag. 在应用程序标记的清单文件中的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>

The idea of having a service ALWAYS running in background in Android is just wrong 99% of the times. 在Android中让服务始终在后台运行的想法在99%的时候都是错误的。

The system need to "shut down" CPU, and switch to a low battery usage profile. 系统需要“关闭” CPU,并切换到低电池使用情况。

You are saying you have a location based service. 您说您有基于位置的服务。 I assume you are using Google Play Services FusedLocationProvider , if not you should . 我假设您正在使用Google Play服务FusedLocationProvider ,如果不是, 您应该使用

The FusedLocationProvider allow you to register for location changes using a PendingIntent . FusedLocationProvider允许您使用PendingIntent注册位置更改。 Meaning your services doesn't need to run all the time, it just need to register for location changes and then react when a new location come and do its stuff. 这意味着您的服务不需要一直运行,只需要注册位置更改,然后在新位置进行时做出反应并执行其操作。

See the FusedLocationProviderApi official documentation . 请参阅FusedLocationProviderApi 官方文档

To start listening for location updates 开始侦听位置更新

  1. connect to the GoogleClient using the LocationServices.API API 使用LocationServices.API API连接到GoogleClient
  2. Build your LocationRequest according to your needs (see the doc ) 根据您的需求构建您的LocationRequest (请参阅文档
  3. Call requestLocationUpdates() using the PendingIntent version 使用PendingIntent版本调用requestLocationUpdates()

To stop listening 停止听

  1. connect to the GoogleClient using the LocationServices.API API 使用LocationServices.API API连接到GoogleClient
  2. Call removeLocationUpdates() using the same PendingIntent 使用相同的PendingIntent调用removeLocationUpdates()

Your PendingIntent can launch another service to handle the new location. 您的PendingIntent可以启动另一个服务来处理新位置。

For example doing this from a service: 例如,从服务中执行此操作:

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

Then the service can immediately stop. 然后服务可以立即停止。

The first time this code run it WILL fail. 第一次运行此代码将失败。 The connection to the google client usually require the user to take some actions. 与谷歌客户端的连接通常要求用户采取一些行动。 The ConnectionResult.hasResolution() method will return true if this is the case. 如果是这种情况, ConnectionResult.hasResolution()方法将返回true。 Otherwise the reason is something else and you can't recover from it. 否则原因是别的,你无法从中恢复。 Meaning the only thing you can do is inform the user the feature will not work or have a nice fallback. 这意味着您唯一能做的就是通知用户该功能不起作用或有一个很好的后备。

The ConnectionResult.getResolution() give you a PendingIntent you need to use an Activity and startIntentSenderForResult() method on the Activity to resolve this intent. ConnectionResult.getResolution()给你PendingIntent ,你需要使用一个ActivitystartIntentSenderForResult()方法的Activity ,以解决这一意图。 So you would create a Notification starting your Activity to resolve that, and in the end call your Service again. 因此,您将创建一个Notification启动您的Activity以解决该问题,并最终再次调用您的Service

I usually just start an Activity dedicated to do all the work. 我通常只是开始一项致力于完成所有工作的Activity It's lot easier but you don't want to call connectBlocking() in it. 它更容易,但你不想在其中调用connectBlocking() Check out this on how to do it. 看看这个怎么做。

You may ask why not requesting location updates directly in the Activity . 您可能会问为什么不直接在Activity请求位置更新。 That's actually perfectly fine, unless you need the location monitor to automatically start with the device, even if the user didn't explicitly opened the App. 这实际上非常好,除非你需要位置监视器自动启动设备,即使用户没有明确打开应用程序。

<receiver android:name=".BootCompletedBroadcastReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

This way you can just run your service to connect and request location updates when the device is rebooted. 这样,您可以在重新启动设备时运行服务以连接并请求位置更新。

Example on how you can build your location request: 有关如何构建位置请求的示例:

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

And your pending intent: 你未决的意图:

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

Your LocationUpdateHandlerService can be an IntentService if you need to do work in background: 如果您需要在后台工作,您的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");
        }

    }
}

But can also be a Broadcast or anything that suits you. 但也可以是广播或任何适合你的东西。

Finally I achieved with help of Evernote JobService 最后,我在Evernote JobService的帮助下完成了

Github link - https://github.com/evernote/android-job Github链接 - https://github.com/evernote/android-job

Step 1: Add evernote jobservice dependency 第1步:添加evernote jobservice依赖项

implementation 'com.evernote:android-job:1.3.0-alpha03'

Step 2: Create DemoJobCreator.java class 第2步:创建DemoJobCreator.java类

public class DemoJobCreator implements JobCreator {

@Override
@Nullable
public Job create(@NonNull String tag) {
    switch (tag) {
        case DemoSyncJob.TAG:
            return new DemoSyncJob();
        default:
            return null;
    }
}
}

Step 3: Create DemoSyncJob.java class 第3步:创建DemoSyncJob.java类

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

Step 4: In your Application file (If not available create it) add following line in onCreate() 第4步:在您的应用程序文件中(如果不可用,创建它)在onCreate()中添加以下行

 JobManager.create(this).addJobCreator(new DemoJobCreator());

Step 5: Finally start JobService in your Activity 第5步:最后在您的Activity中启动JobService

DemoSyncJob.scheduleJob();

This JobService will check service running or not (every 2 second) If service not running it will restart the service. 此JobService将检查服务是否运行(每2秒)如果服务未运行,它将重新启动该服务。

Disclaimer : This may be not right solution. 免责声明:这可能不是正确的解决方案。 But it will 100% working. 但它将100%工作。

I hope it helps atleast anyone in future. 我希望将来至少可以帮助任何人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM