简体   繁体   English

当应用程序被杀死时,GPS 位置无法获得更新

[英]GPS location not getting updates when app is killed

I have a requirement of sending a user's location to a web server after a certain interval, currently sending after every two minutes.我需要在一定时间间隔后将用户的位置发送到 Web 服务器,目前每两分钟发送一次。 It is working fine unless the app is running.除非应用程序正在运行,否则它工作正常。 When the app is in background location stops getting updated, sending the same location again and again.当应用程序在后台位置停止更新时,一次又一次地发送相同的位置。 I am little confused.我有点困惑。 it would be great if anyone suggests an alternative to my approach.如果有人建议替代我的方法,那就太好了。 I start location sending on click of a button and does not stop unless and until the user clicks on the button to stop.我在单击按钮时开始位置发送并且不会停止,除非并且直到用户单击按钮停止。 Below is my code.下面是我的代码。 I want to send current location of the user to a web server whether the app is running or not.无论应用程序是否正在运行,我都想将用户的当前位置发送到网络服务器。 Any help is appreciated.任何帮助表示赞赏。 problem is not in sending location after a certain interval , problem is location stops getting updated if I kill the app coordinates remain the same.问题不是在一定时间间隔后发送位置,问题是如果我杀死应用程序坐标保持不变,位置将停止更新。 If app is running in foreground then even a slight tilt in device makes the coordinates change.如果应用程序在前台运行,那么即使设备稍微倾斜也会使坐标发生变化。 I earlier used firebaseJobDispatcher to call this service.我之前使用过 firebaseJobDispatcher 来调用这个服务。 Problem is not in calling the service.问题不在于调用服务。 Problem is location stops getting updated and remains same every time I call the service if I have killed the app.问题是如果我杀死了应用程序,每次我调用服务时位置都会停止更新并保持不变。

I am using alarm manager to call this service every 2 minutes.我正在使用警报管理器每 2 分钟调用一次此服务。

public class GPSTracker_DUP extends Service implements LocationListener {

    private  Context mContext=null;
    RetrofitAPI retrofitAPI;

    // flag for GPS status
    boolean isGPSEnabled = false;

    // flag for network status
    boolean isNetworkEnabled = false;

    // flag for GPS status
    boolean canGetLocation = false;

    SaveData objSaveData;
    Location location; // location
    double latitude; // latitude
    double longitude; // longitude
    private String provider;
    // The minimum distance to change Updates in meters
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters

    // The minimum time between updates in milliseconds
    private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1; // 1 minute

    // Declaring a Location Manager
    protected LocationManager locationManager;

    public GPSTracker_DUP(Context context) {
        this.mContext = context;
        //getLocation();
    }

    public GPSTracker_DUP(){}



    public Location getLocation() {
        try {
            locationManager = (LocationManager) mContext
                    .getSystemService(LOCATION_SERVICE);


            Criteria criteria = new Criteria();
            provider = locationManager.getBestProvider(criteria, false);

            if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mContext, 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 null;
            }

            // getting GPS status
            isGPSEnabled = locationManager
                    .isProviderEnabled(LocationManager.GPS_PROVIDER);
            if(!isGPSEnabled)
            {
                showSettingsAlert();

            }
            else
            {

                locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,1000*60*2,0,this);
                location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

                if (location != null) {
                    Log.e("Provider ",  provider + " has been selected."+location.getLatitude()+"==="+location.getLongitude());

                    saveLocation(location.getLatitude(),location.getLongitude());

                    //onLocationChanged(location);
                }
            }


            // getting network status
//            isNetworkEnabled = locationManager
//                    .isProviderEnabled(LocationManager.NETWORK_PROVIDER);


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

        return location;
    }


    public static boolean isConnected(Context context){
        NetworkInfo info = getNetworkInfo(context);
        return (info != null && info.isConnected());
    }

    public static NetworkInfo getNetworkInfo(Context context){
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        return cm.getActiveNetworkInfo();
    }
    /**
     * Stop using GPS listener
     * Calling this function will stop using GPS in your app
     * */
    public void stopUsingGPS(){
        if(locationManager != null){
            locationManager.removeUpdates(GPSTracker_DUP.this);
        }
    }

    /**
     * Function to get latitude
     * */
    public double getLatitude(){
        if(location != null){
            latitude = location.getLatitude();
        }

        // return latitude
        return latitude;
    }

    /**
     * Function to get longitude
     * */
    public double getLongitude(){
        if(location != null){
            longitude = location.getLongitude();
        }

        // return longitude
        return longitude;
    }

    /**
     * Function to check GPS/wifi enabled
     * @return boolean
     * */
    public boolean canGetLocation() {
        return this.canGetLocation;
    }


    /**
     * Function to show settings alert dialog
     * On pressing Settings button will lauch Settings Options
     * */
    public void showSettingsAlert(){
        AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);

        // Setting Dialog Title
        alertDialog.setTitle("GPS is settings");

        // Setting Dialog Message
        alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?");

        // On pressing Settings button
        alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog,int which) {
                Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                mContext.startActivity(intent);
            }
        });

        // on pressing cancel button
        alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
            }
        });

        // Showing Alert Message
        alertDialog.show();
    }


    @Override
    public void onLocationChanged(Location location) {
        latitude = location.getLatitude();
        longitude = location.getLongitude();
        Log.e("onlocation","changed");


    }
    public void saveLocation(Double latitude,Double longitude){
        objSaveData = new SaveData(mContext);

            Log.e("Saving Coordinates", latitude + " " + longitude);
            AudioDbHelper audioDbHelper= new AudioDbHelper(mContext);
            UserCoordinates userCoordinates = new UserCoordinates();
            userCoordinates.setLatitude(String.valueOf(latitude));
            userCoordinates.setLongitude(String.valueOf(longitude));
            userCoordinates.setUploaded("no");
            SaveData objSaveData = new SaveData(mContext);
            userCoordinates.setUserEmail(objSaveData.getString("LoginId"));
            String time = new SimpleDateFormat("hh:mm: aa").format(Calendar.getInstance().getTime());
            userCoordinates.setLocationTime(time);
            audioDbHelper.addCoordinates(userCoordinates);

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        stopUsingGPS();
    }

    @Override
    public void onProviderDisabled(String provider) {
    }

    @Override
    public void onProviderEnabled(String provider) {
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
    }

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

Since You need to send location after some interval, It's better to use Job Scheduler which starts the service after an interval, fetches the location and makes the API call.由于您需要在一段时间后发送位置,因此最好使用 Job Scheduler,它会在一段时间后启动服务,获取位置并进行 API 调用。

The best is Job Scheduler and is what is recommended by Google, but Android versions limit it's use, Its better to use Evernote Android Job .最好的是 Job Scheduler,是 Google 推荐的,但 Android 版本限制了它的使用,最好使用Evernote Android Job Depending on the Android version either the JobScheduler, GcmNetworkManager or AlarmManager is used.根据 Android 版本,使用 JobScheduler、GcmNetworkManager 或 AlarmManager。

You don't have to worry about the service getting killed as now it's the OS's responsibility to start the service您不必担心服务被杀死,因为现在启动服务是操作系统的责任

Also about fetching the location, Use Google PLay Location, to fetch location.另外关于获取位置,使用谷歌播放位置,获取位置。 What it does is it fetches the location from Google Play service in your device which is updated from time to time.它的作用是从您设备中不时更新的 Google Play 服务中获取位置信息。

Take a look at this Util class that I use in one of my projects to fetch location from a running service.看看我在我的一个项目中使用的这个 Util 类来从正在运行的服务中获取位置。 It is in Kotlin and uses Dagger2, but you will get the idea.它在 Kotlin 中并使用 Dagger2,但您会明白的。 It has a callback interface which replies with the current location and address fetched via Google Play Location Service它有一个回调接口,通过 Google Play Location Service 来回复当前位置和地址

ServiceLocationUtil.kt ServiceLocationUtil.kt

You need to create a Start_Sticky service for this by default its START_STICKY_COMPATIBILITY .默认情况下,您需要为此创建一个Start_Sticky服务START_STICKY_COMPATIBILITY Override onStartCommand() .覆盖onStartCommand()

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

Whereas due to Background Limitations this will not work in latest versions of android.而由于背景限制,这在最新版本的 android 中不起作用。 So you probably want to checkout Background Location Limits and Android 8.0 Behavior Changes .因此,您可能想要查看后台位置限制Android 8.0 行为更改

I suggest you to use JobShedular if you need to send location after some interval.如果您需要在一段时间后发送位置,我建议您使用JobShedular There are some available like Evernote android-job , Firebase JobDispatcher .有一些可用的,如Evernote android-jobFirebase JobDispatcher

Also readIntelligent Job-Scheduling .另请阅读智能作业调度

You should use WorkManager or FirebaseJobDispatcher for background processes.您应该将 WorkManager 或 FirebaseJobDispatcher 用于后台进程。 But FirebaseJobDispatcher is not supported from Android Q.但 Android Q 不支持 FirebaseJobDispatcher。

This is my solution using WorkManager for getting location in background这是我使用 WorkManager 在后台获取位置的解决方案

Define this in activity or fragment在活动或片段中定义它

   private fun startTaskWithWorkManager() {
    val constraints: Constraints = Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .build()
    val locationWorker =
        PeriodicWorkRequest.Builder(LocationWorker::class.java, MIN_PERIODIC_INTERVAL_MILLIS, TimeUnit.MILLISECONDS)
            .setConstraints(constraints)
            .addTag(LOCATION_WORKER_TAG)
            .setInputData(createInputData())
            .build()
    WorkManager.getInstance()
        .enqueueUniquePeriodicWork(LOCATION_WORKER_TAG, ExistingPeriodicWorkPolicy.KEEP, locationWorker)
}

After that you should create a class that will extends from ListenableWorker.之后,您应该创建一个将从 ListenableWorker 扩展的类。 In my case I should have to use ListenableWorker instead of Worker.在我的情况下,我应该使用 ListenableWorker 而不是 Worker。 The difference you can find here and here .您可以在此处此处找到不同之

class LocationWorker(context: Context, private val workerParams: WorkerParameters) :
ListenableWorker(context, workerParams) {

lateinit var mFuture: SettableFuture<ListenableWorker.Result>
private var fusedLocationProviderClient = FusedLocationProviderClient(context)

@SuppressLint("RestrictedApi", "MissingPermission")
override fun startWork(): ListenableFuture<Result> {
    val uniqueId = workerParams.inputData.getString(UNIQUE_ID_KEY)
    mFuture = SettableFuture.create()
    Timber.d("mFutureStart")
    fusedLocationProviderClient.lastLocation.addOnSuccessListener { location ->
        Timber.d("location == $location")
        if (location != null) {
             mFuture.set(Result.success())
        } else mFuture.set(Result.failure())
      }
    return mFuture
  }
}

Thats it :) Work like a charm就是这样:) 像魅力一样工作

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

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