Android服务不断停止-“不允许后台执行:接收Intent”

时间:2019-05-23 12:24:29

标签: android android-service

我有一个位置服务,当用户单击按钮并应用位置时,该服务就会运行。

但是,大约一分钟后它被杀死。我已经在android 8/9上测试过。 我在这里已经阅读了其他一些答案,但是它们并没有帮助,但是我对意图和广播的了解不是很好。

LocationService.java

public class LocationService extends Service {

    MockLocationProvider mockNetwork;
    MockLocationProvider mockGps;

    private static final int NOTIFICATION = 1;
    static NotificationManager notificationManager;

    Context context;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //TODO do something useful

        context = getApplicationContext();

        pushLocation(intent);

        return LocationService.START_STICKY;
    }

    @Override
    public void onDestroy() {
        shutdown();

        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private void pushLocation(Intent intent) {
        try {
            if (intent.hasExtra("lat") && intent.hasExtra("lng") ) {
                double lat = intent.getDoubleExtra("lat", 45);
                double lng = intent.getDoubleExtra("lng", 45);

                mockNetwork = new MockLocationProvider(LocationManager.NETWORK_PROVIDER, context);
                mockGps = new MockLocationProvider(LocationManager.GPS_PROVIDER, context);

                mockNetwork.pushLocation(lat, lng);
                mockGps.pushLocation(lat, lng);

                setNotification(lat, lng);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void setNotification(Double mLat, Double mLng) {
        notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        NotificationCompat.Builder builder = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            int importance = NotificationManager.IMPORTANCE_DEFAULT;

            NotificationChannel notificationChannel = new NotificationChannel("ID", "Name", importance);
            notificationManager.createNotificationChannel(notificationChannel);
            builder = new NotificationCompat.Builder(context, notificationChannel.getId());

        } else {
            builder = new NotificationCompat.Builder(context);
        }

        //open MainActivity when clicked
        pendingIntent = PendingIntent.getActivity(context, 0,
                new Intent(context, MainActivity.class)
                        .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP),
                0);

        //action when notification button clicked
        Intent intentAction = new Intent(context, ActionReceiver.class);
        intentAction.putExtra("action","actionNotification");
        pendingCloseIntent = PendingIntent.getBroadcast(context,0, intentAction, PendingIntent.FLAG_UPDATE_CURRENT);

        //build notification
        builder = builder
                .setSmallIcon(R.drawable.ic_logo)
                .setColor(ContextCompat.getColor(context, R.color.colorPrimary))
                .setContentTitle(getString(R.string.app_name) + " is running...")
                .setCategory(NotificationCompat.CATEGORY_SERVICE)
                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
                .setContentText(mLat + ", " + mLng)
                .setContentIntent(pendingIntent)
                .setWhen(System.currentTimeMillis())
                .setTicker(getString(R.string.app_name) + " is running...") //accessibility
                .addAction(android.R.drawable.ic_menu_close_clear_cancel, "STOP", pendingCloseIntent)
                .setOngoing(true);

        notificationManager.notify(NOTIFICATION, builder.build());
    }

    public void shutdown() {
        NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.cancel(NOTIFICATION);

        if (mockNetwork != null)
            mockNetwork.shutdown();
        if (mockGps != null)
            mockGps.shutdown();
    }
}

ActionReceiver.java

public class ActionReceiver extends BroadcastReceiver {

    Context context;

    MockLocationProvider mockNetwork;
    MockLocationProvider mockGps;

    private static final int NOTIFICATION = 1;

    @Override
    public void onReceive(Context context, Intent intent) {

        String action = intent.getStringExtra("action");
        if(action.equals("actionNotification")){

            mockNetwork = new MockLocationProvider(LocationManager.NETWORK_PROVIDER, context);
            mockGps = new MockLocationProvider(LocationManager.GPS_PROVIDER, context);

            NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
            notificationManager.cancel(NOTIFICATION);

            try {
                if (mockNetwork != null)
                    mockNetwork.shutdown();
                if (mockGps != null)
                    mockGps.shutdown();

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

这些已在我的AndroidManifest中注册,并具有以下权限:


...

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

...

<receiver android:name="com.package.locationtest.ActionReceiver" />

<service
    android:name=".LocationService"
    android:label="Service">
</service>

我不明白这里的问题。有人可以帮忙吗?

2 个答案:

答案 0 :(得分:0)

您应该使用ForegroundService而不是Service。要启动它时,请调用startForegroundService(),然后在onCreate方法中调用startForeground()。使用startForeground,您必须传递一条通知以通知用户该服务正在运行。

此外,我发现某些设备(例如HUAWEI)具有称为“功耗密集型应用程序监视器”的功能。除非用户为其授予特殊权限,否则它将杀死长时间在后台运行的每个应用程序。 执行此操作的路径: 设置->安全和隐私->位置服务->最近的位置请求:您的应用名称->电池->取消选中耗电的提示,应用启动:手动管理:检查所有三个位置:自动启动,二次启动,在以下位置运行背景。

我不知道有没有办法以编程方式执行此操作。我认为最好的方法是创建一种帮助活动,并向用户解释如果应用程序无法运行该怎么办。

答案 1 :(得分:0)

我认为我已经对其进行了排序,或者几乎对其进行了排序。我通过以下方式致电服务:

startForegroundService(intent);

代替:

context.startService(intent);

我添加了权限:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

并如下修改服务:

public class LocationService extends Service {

    MockLocationProvider mockNetwork;
    MockLocationProvider mockGps;

    static NotificationManager notificationManager;

    Context context;

    private static final int ID_SERVICE = 101;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        context = getApplicationContext();

        // Create the Foreground Service
        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? createNotificationChannel(notificationManager) : "";

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId);
        Notification notification = notificationBuilder
                .setOngoing(true)
                .setSmallIcon(R.drawable.ic_menu_pin_drop_24dp)
                .setColor(ContextCompat.getColor(context, R.color.default_error_color))
                .setContentTitle(getString(R.string.app_name) + " is running...")
                .setPriority(PRIORITY_MIN)
                .setVisibility(NotificationCompat.VISIBILITY_PRIVATE)
                .setCategory(NotificationCompat.CATEGORY_SERVICE)
                .build();

        startForeground(ID_SERVICE, notification);
        pushLocation(intent);

        return LocationService.START_STICKY;
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private String createNotificationChannel(NotificationManager notificationManager){
        String channelId = "my_service_channelid";
        String channelName = "My Foreground Service";
        NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
        // omitted the LED color
        channel.setImportance(NotificationManager.IMPORTANCE_NONE);
        channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
        notificationManager.createNotificationChannel(channel);
        return channelId;
    }

    @Override
    public IBinder onBind(Intent intent) {
        pushLocation(intent);

        return null;
    }

    private void pushLocation(Intent intent) {
        try {
            if (intent.hasExtra("lat") && intent.hasExtra("lng") ) {
                double lat = intent.getDoubleExtra("lat", 45);
                double lng = intent.getDoubleExtra("lng", 45);

                mockNetwork = new MockLocationProvider(LocationManager.NETWORK_PROVIDER, context);
                mockGps = new MockLocationProvider(LocationManager.GPS_PROVIDER, context);

                mockNetwork.pushLocation(lat, lng);
                mockGps.pushLocation(lat, lng);

                //setNotification(lat, lng);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

我在这里使用了示例通知代码:

Android - implementing startForeground for a service?

我注意到该服务在大约25-30分钟后被无提示终止,但是似乎很快又重新启动了。也许与onBind方法有关?

有人知道如何防止重新启动吗?

编辑:对服务没有关闭进行更多测试,我认为这更多与导致问题的电话上启用了“改善位置”选项有关!