【问题标题】:What is the best way to receive location updates in background?在后台接收位置更新的最佳方式是什么?
【发布时间】:2021-01-24 05:47:55
【问题描述】:

我正在尝试编写一个在后台跟踪位置并将数据发送到服务器的应用程序——例如,监控我的家人在哪里。
目前我正在使用https://github.com/android/location-samples,尤其是LocationUpdatesBackgroundKotlin,这似乎是接收位置更新的最佳方式,但是

在后台接收到大约 8-10 次位置更新后,状态栏上的 gps 图标消失了,而没有通知应用程序(here 是 android/手机信息,但我希望应用程序与 Android 5.1 兼容) .
我想以某种方式知道是否正在接收位置更新,如果它已死,则重新启动它(在MyLocationManager 的第 105 行使用fusedLocationClient.requestLocationUpdates 重新开始接收更新有助于接收进一步的更新,但我必须通过眼睛监控状态) .

有没有什么出路,或者更可靠的方法?谢谢。
附言已经为android写了一个星期了。

【问题讨论】:

    标签: android android-fusedlocation


    【解决方案1】:

    为了从应用程序中不断获取位置,您必须使用前台服务,您可以在其中初始化位置管理器并根据已设置的参数不断获取位置更新。此外,请确保您具有后台位置权限,因为这是 API 级别 29 之后的要求。以下是如何实现它的非常基本的流程。获取位置权限后请务必启动该服务:

    public class MyCustomService extends Service implements
            GoogleApiClient.ConnectionCallbacks,
            GoogleApiClient.OnConnectionFailedListener {
    
        private GoogleApiClient mGoogleApiClient;
        private PowerManager.WakeLock mWakeLock;
        private LocationRequest mLocationRequest;
        private boolean mInProgress;
    
        private Boolean servicesAvailable = false;
    
        @Override
        public IBinder onBind(Intent arg0) {
            return null;
        }
    
        private static final int UPDATE_INTERVAL_IN_SECONDS = 120;
        private static final int MILLISECONDS_PER_SECOND = 1000;
        public static final long UPDATE_INTERVAL = MILLISECONDS_PER_SECOND * UPDATE_INTERVAL_IN_SECONDS;
        private static final int FASTEST_INTERVAL_IN_SECONDS = 60;
        public static final long FASTEST_INTERVAL = MILLISECONDS_PER_SECOND * FASTEST_INTERVAL_IN_SECONDS;
    
    
        @Override
        public void onCreate() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                startForegroundService();
            }
    
            mInProgress = false;
            mLocationRequest = LocationRequest.create();
            mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
            mLocationRequest.setInterval(UPDATE_INTERVAL);
            mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
            mLocationRequest.setSmallestDisplacement(100);
            servicesAvailable = servicesConnected();
    
            /*
             * Create a new location client, using the enclosing class to
             * handle callbacks.
             */
            setUpLocationClientIfNeeded();
            super.onCreate();
    
        }
    
    
        private void setUpLocationClientIfNeeded() {
            if (mGoogleApiClient == null)
                buildGoogleApiClient();
        }
    
        /*
         * Create a new location client, using the enclosing class to
         * handle callbacks.
         */
        protected synchronized void buildGoogleApiClient() {
            this.mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();
        }
    
        private boolean servicesConnected() {
    
            // Check that Google Play services is available
            int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
    
            // If Google Play services is available
            if (ConnectionResult.SUCCESS == resultCode) {
    
                return true;
            } else {
    
                return false;
            }
        }
    
        /* Used to build and start foreground service. */
        private void startForegroundService() {
            Intent notificationIntent = new Intent(this, HomeActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
    
            String CHANNEL_ID = "1";
            NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setSmallIcon(R.drawable.noti_icon)
                    .setPriority(Notification.PRIORITY_LOW)
                    .setOngoing(true)
                    .setAutoCancel(false)
                    .setContentTitle("ServiceTitle")
                    .setContentText("Service Reason text")
                    .setTicker("TICKER")
                    .setChannelId(CHANNEL_ID)
                    .setVibrate(new long[]{0L})
                    .setContentIntent(pendingIntent);
            Notification notification = builder.build();
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "NOTIFICATION_CHANNEL_NAME", NotificationManager.IMPORTANCE_HIGH);
                channel.setDescription("NOTIFICATION_CHANNEL_DESC");
                channel.enableVibration(false);
                channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
                notificationManager.createNotificationChannel(channel);
            }
            startForeground(123, notification);
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
    
            this.mInProgress = false;
    
            if (this.servicesAvailable && this.mGoogleApiClient != null) {
                this.mGoogleApiClient.unregisterConnectionCallbacks(this);
                this.mGoogleApiClient.unregisterConnectionFailedListener(this);
                this.mGoogleApiClient.disconnect();
                // Destroy the current location client
                this.mGoogleApiClient = null;
            }
    
            if (this.mWakeLock != null) {
                this.mWakeLock.release();
                this.mWakeLock = null;
            }
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            super.onStartCommand(intent, flags, startId);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                startForegroundService();
            }
            setUpLocationClientIfNeeded();
            if (!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress) {
                mInProgress = true;
                mGoogleApiClient.connect();
            }
            return START_STICKY;
    
        }
    
    
        @Override
        public void onConnected(@Nullable Bundle bundle) {
            Intent intent = new Intent(this, LocationReceiver.class);
            PendingIntent pendingIntent = PendingIntent
                    .getBroadcast(this, 54321, intent, PendingIntent.FLAG_CANCEL_CURRENT);
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            if (this.mGoogleApiClient != null)
                LocationServices.FusedLocationApi.requestLocationUpdates(this.mGoogleApiClient,
                        mLocationRequest, pendingIntent);
        }
    
        @Override
        public void onConnectionSuspended(int i) {
            // Turn off the request flag
            mInProgress = false;
            // Destroy the current location client
            mGoogleApiClient = null;
        }
    
        @Override
        public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
            mInProgress = false;
    
            /*
             * Google Play services can resolve some errors it detects.
             * If the error has a resolution, try sending an Intent to
             * start a Google Play services activity that can resolve
             * error.
             */
            if (connectionResult.hasResolution()) {
    
                // If no resolution is available, display an error dialog
            } else {
    
            }
        }
    }
    

    这是你需要在 Androidmanifest 文件中注册的 Location 接收器类

    public class LocationReceiver extends BroadcastReceiver {
    
        private String TAG = "LOCATION RECEIVER";
    
        private LocationResult mLocationResult;
        private Context context;
        Location mLastLocation;
        
    
        @Override
        public void onReceive(Context context, Intent intent) {
            // Need to check and grab the Intent's extras like so
            this.context = context;
            if (LocationResult.hasResult(intent)) {
                this.mLocationResult = LocationResult.extractResult(intent);
                if (mLocationResult.getLocations().get(0).getAccuracy() < 100) {
    
                    // DO WHATEVER YOU WANT WITH LOCATION
                }
            }
        }
    }
    

    所需的权限:

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.ACCESS_GPS" />
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
        <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
    

    注意:上面代码中的一些方法如 FusedLocationApi 和 isGooglePlayServicesAvailable 已被弃用

    【讨论】:

    • 我认为这不是解决方案,原因有两个:首先,前台服务在主线程中运行,因此与主活动没有太大区别。第二:你说官方的谷歌例子是错误的,这是可信的,但不太可能。我会试一试。谢谢。
    • 根据 Google 指南,未经用户同意,您不能在后台获取位置,因此您必须使用前台服务,否则将无法正常工作。我并没有说任何关于官方谷歌示例的内容,所以不要在这种情况下使用它。我也使用前台服务来获取我的应用程序中的固定位置,只要您遵循 Google 的隐私政策指南,它就可以正常工作。
    • 好的。 1.我不是说“未经用户同意”,有在后台检索位置的权限(但android 8根本没有它,所以我在测试时确实从代码中删除了它)2.我想要获取变化的位置,例如公共汽车在哪里,而不是固定的。 3. 如果方便,请考虑分享部分工作代码的可能性。谢谢。
    • @AlexeyBurdin 提供了一个基本代码,您可以从中大致了解如何实现它。希望这会有所帮助。
    • 谢谢你这对我帮助很大。但是您忘记在清单文件中提及服务类。这将是 从其他主要活动启动服务将是 startService(new Intent(this,MyCustomService.class));
    【解决方案2】:

    Android 5 Lollipop 最好(更稳定)使用第三方库进行免费位置接收,即来自'io.nlopez.smartlocation:library:3.3.3' 的io.nlopez.smartlocation.SmartLocation,如here 所述,以及前台服务,如Rudrik Patel 提到。像魅力一样工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-09
      • 1970-01-01
      • 2017-08-28
      • 1970-01-01
      • 2013-07-31
      • 1970-01-01
      • 2015-09-08
      • 1970-01-01
      相关资源
      最近更新 更多