【问题标题】:How to keep a service running when I remove the app from minimised apps list?从最小化应用程序列表中删除应用程序时如何保持服务运行?
【发布时间】:2019-08-20 11:38:50
【问题描述】:

我有一项服务正在运行以在特定时间间隔跟踪用户位置。我为奥利奥和奥利奥后设备使用了前台服务,为奥利奥前设备使用了服务。该服务在应用程序被最小化时运行良好,但是当我从最小化应用程序列表中删除该应用程序时,该服务也会被终止。我已经在装有 Android 9 的小米设备上对此进行了测试。当用户终止应用程序时,如何保持服务运行。

清单:

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

主活动:

@OnClick(R.id.btn_start_tracking)
public void onClickStart(){

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        this.startForegroundService (new Intent (this, LocationService.class));
    } else {
        this.startService (new Intent (this, LocationService.class));
    }

}

服务类放在这里

public class LocationService extends Service implements GoogleApiClient.ConnectionCallbacks,
    GoogleApiClient.OnConnectionFailedListener{



public static final String TAG = LocationService.class.getSimpleName();
private static final long LOCATION_REQUEST_INTERVAL = 3000;
private static final float LOCATION_REQUEST_DISPLACEMENT = 5.0f;
private GoogleApiClient mGoogleApiClient;
private FusedLocationProviderClient mFusedLocationProviderClient;
private LocationRequest mLocationRequest;
private LocationCallback mLocationCallback;



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

@Override
public void onCreate() {
    super.onCreate();

    Log.d ("On Create-------> ","Inside on create");
    buildGoogleApiClient();
    showNotificationAndStartForegroundService();

    mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(LocationService.this);
    mLocationCallback = new LocationCallback() {
        @Override
        public void onLocationResult(LocationResult locationResult) {
            super.onLocationResult(locationResult);
            List<Location> locationList = locationResult.getLocations ();
            for (Location loc : locationList) {
                if (loc.getLatitude () != 0 && loc.getLongitude () != 0) {
                    Log.d (TAG,"Location -------------->>>>" + loc.getLatitude ()+ "   " + loc.getLongitude ());

                    saveUserLocation (loc.getLatitude (),loc.getLongitude (),10990816+1);
                    break;
                }
            }
        }
    };


}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d ("On Start Command---> ","Inside on Start Command");
    return START_STICKY;
}

private synchronized void buildGoogleApiClient() {
    mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addApi(LocationServices.API)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .build();

    mGoogleApiClient.connect();
}


private void createLocationRequest() {
    mLocationRequest = LocationRequest.create();
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    mLocationRequest.setInterval(LOCATION_REQUEST_INTERVAL);
   /* mLocationRequest.setSmallestDisplacement(LOCATION_REQUEST_DISPLACEMENT);*/
    mLocationRequest.setFastestInterval (LOCATION_REQUEST_INTERVAL/2);

    requestLocationUpdate();
}



private void requestLocationUpdate() {
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
            != PackageManager.PERMISSION_GRANTED &&
            ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
                    != PackageManager.PERMISSION_GRANTED) {
        // TODO: Consider calling
        //    ActivityCompat#requestPermissions
        // here to request the missing permissions, and then overriding
        //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
        //                                          int[] grantResults)
        // to handle the case where the user grants the permission. See the documentation
        // for ActivityCompat#requestPermissions for more details.
        return;
    }

    mFusedLocationProviderClient.requestLocationUpdates(mLocationRequest, mLocationCallback,
            Looper.myLooper());
}

private void removeLocationUpdate() {
    mFusedLocationProviderClient.removeLocationUpdates(mLocationCallback);
}

/**
 * This Method shows notification for ForegroundService
 * Start Foreground Service and Show Notification to user for android all version
 */
private void showNotificationAndStartForegroundService() {

    final String CHANNEL_ID = BuildConfig.APPLICATION_ID.concat("_notification_id");
    final String CHANNEL_NAME = BuildConfig.APPLICATION_ID.concat("_notification_name");
    final int NOTIFICATION_ID = 100;

    NotificationCompat.Builder builder;
    NotificationManager notificationManager =
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        int importance = NotificationManager.IMPORTANCE_NONE;
        assert notificationManager != null;
        NotificationChannel mChannel = notificationManager.getNotificationChannel(CHANNEL_ID);
        if (mChannel == null) {
            mChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, importance);
            notificationManager.createNotificationChannel(mChannel);
        }
        builder = new NotificationCompat.Builder(this, CHANNEL_ID);
        builder.setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle(getString(R.string.app_name));
        startForeground(NOTIFICATION_ID, builder.build());
    } else {
        builder = new NotificationCompat.Builder(this, CHANNEL_ID);
        builder.setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle(getString(R.string.app_name));
        startForeground(NOTIFICATION_ID, builder.build());
    }
}

@Override
public void onConnected(@Nullable Bundle bundle) {

    createLocationRequest();
}

@Override
public void onConnectionSuspended(int i) {

}

@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

}

@Override
public void onTaskRemoved(Intent rootIntent) {

    Log.d ("On Task Removed------> ","inside on task removed");
    super.onTaskRemoved (rootIntent);
}

@Override
public void onDestroy() {

    Log.d ("On Destroy","On destroy called in location service");
    if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
        mGoogleApiClient.disconnect();
    }

    super.onDestroy();

}


private void saveUserLocation(Double lat,Double lon,Integer time){

    LocationSaveRequest locationSaveRequest = new LocationSaveRequest ();
    final List<com.vivacom.pi_sales_tracking.retrofit.locationsave.model.Location> locationList = new ArrayList<> ();
    com.vivacom.pi_sales_tracking.retrofit.locationsave.model.Location location = new com.vivacom.pi_sales_tracking.retrofit.locationsave.model.Location ();
    location.setUserId ("100");
    location.setLat (lat);
    location.setLon (lon);
    location.setClientTimestampUtc (System.currentTimeMillis()/1000);
    locationList.add (location);
    locationSaveRequest.setLocations (locationList);


    CallLocationSave.save ("KAKBDURKBJSBSKHGYBKA==", locationSaveRequest, new CallLocationSave.LocationSaveCallBack () {
        @Override
        public void onSuccess() {

            locationList.clear ();

        }

        @Override
        public void onFailure(LocationHistoryResponse locationHistoryResponse) {

            locationList.clear ();
        }
    });
}

}

【问题讨论】:

  • 分享你启动服务的代码和你的Manifest文件
  • 来自清单文件:来自代码:@OnClick(R.id.btn_start_tracking) public void onClickStart(){ if (Build.VERSION. SDK_INT >= Build.VERSION_CODES.O) { this.startForegroundService (new Intent (this, LocationService.class)); } else { this.startService (new Intent (this, LocationService.class)); } }
  • 在原始问题中添加代码以获得更好的可读性
  • 也分享您的服务代码
  • 我已经分享了我的代码。

标签: android background-service


【解决方案1】:

从 Api 级别 26 或以上的服务在后台无法运行,而应用程序因节省电池消耗而被终止。您可以选择前台服务或工作管理器来跟踪位置。

Here你可以试试如何在工作管理器中获取位置。

【讨论】:

【解决方案2】:

如果前台服务对用户不明确可见,则它们不能也不会运行。您需要使用前台通知。这是一项安全功能。

前景

前台服务执行一些操作 引起用户注意。例如,一个音频应用程序会使用 前台服务播放音轨。前台服务必须 显示通知。前台服务继续运行,即使 用户没有与应用交互。

发件人:https://developer.android.com/guide/components/services

我的解决方案,在您的服务中显示前台通知:

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

    createNotificationChannel();
    Intent notificationIntent = new Intent(this, MainActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this,
            0, notificationIntent, 0);

    Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentIntent(pendingIntent)
            .setPriority(NotificationCompat.PRIORITY_LOW)
            .build();

    startForeground(1, notification);

    return START_STICKY;
}

private void createNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel serviceChannel = new NotificationChannel(
                CHANNEL_ID,
                "Application Service Channel",
                NotificationManager.IMPORTANCE_LOW
        );
        NotificationManager manager = getSystemService(NotificationManager.class);
        manager.createNotificationChannel(serviceChannel);
    }
}

【讨论】:

  • 感谢您的帮助。我已经在我的服务中使用了前台通知,并且在 onCreate() 中调用了相应的方法。我需要将它放在 onStartCommand(Intent intent, int flags, int startId) 方法中吗?
  • 我在 onStartCommand() 中使用过它。但结果是一样的。当我从最近的应用列表中删除该应用时,前台通知消失。
  • 实际上,我在服务器中保存了经纬度。从最近的应用列表中删除该应用后,远程数据库中没有保存任何数据。
  • 首先确保你的服务在应用关闭后正在运行,发送数据是另一回事。应用关闭后能看到前台通知吗?
  • 不,应用关闭后我看不到前台通知。
【解决方案3】:

您应该使用START_STICKY 标志,这将在服务被系统或最小化应用列表停止后重新启动服务

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

一旦服务停止,它会在您身边进行测试,它会在一段时间后自动重新启动。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-13
    • 2014-07-16
    相关资源
    最近更新 更多