使用WorkManager实现Android定期位置更新

7

我正在使用WorkManager,如下所示 -

class LocationWorker(
    ctx: Context, params: WorkerParameters
) : CoroutineWorker(ctx, params), KoinComponent {

    private val locationDataRepository: LocationDataRepository by inject()

    override suspend fun doWork(): Result {
        return try {
            locationDataRepository.triggerLocationUpdates()
            Result.success()
        } catch (e: Exception) {
            Result.failure()
        }
    }
}

我作为触发工作者的方式是 -

val myWorker =
            PeriodicWorkRequestBuilder<LocationWorker>(
                15,
                TimeUnit.MINUTES
            ).addTag(
                "location"
            ).build()
        WorkManager.getInstance(context).enqueueUniquePeriodicWork(
            "location",
            ExistingPeriodicWorkPolicy.KEEP,
            myWorker
        )

正如您所看到的,WorkManager 的最小周期为 15 分钟。我希望每隔几秒钟就能跟踪位置,而且即使手机屏幕关闭,也要跟踪位置。对于我的需求,WorkManager 是否是正确的选择,或者你建议我使用其他 API?


1
我认为是的,你可以阅读这篇中等博客文章,你会更好地理解它。编程愉快 :) https://medium.com/google-developer-experts/services-the-life-with-without-and-worker-6933111d62a6 - umer farooq
WorkManager 不是设计用来每秒运行任务的,因为它有两个选项来构建工作请求:PeriodicWorkRequest - 每 15 分钟运行重复任务,即使我们将时间间隔更改为 < 15 分钟,它也会默认运行 15 分钟。OneTimeWorkRequest - 只运行一次。 - umer farooq
你可以实现作业调度器,但它会耗尽电池。 - umer farooq
我认为Firebase作业调度器不错。但是请根据您的搜索和其他人的意见做出决定。 :) - umer farooq
2
@Coderbox 是的,我也使用前台服务来获取位置更新。然而,缺点是当应用程序被杀死时,前台服务无法运行。 - Ma2340
显示剩余3条评论
2个回答

2

它可能会对你有帮助,即使应用程序被杀死也能正常工作。但当设备进入待机模式时,由于设备静止,无法访问GPS,我还是有点担心。

前台服务的主要目的是在应用程序被杀死时运行一个持久的通知。

LocationService.class

public class LocationService extends Service implements
        GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
    public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 1000 * 30; //30 secs
    public static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
            UPDATE_INTERVAL_IN_MILLISECONDS / 2;
    protected static final String TAG = "LocationUpdateService";
    public static final int HORIZONTAL_ACCURACY_IN_METERS = 100;

    /**
     * The identifier for the notification displayed for the foreground service.
     */
    private static final int NOTIFICATION_ID = 12345678;

    public static boolean mRequestingLocationUpdates = false;
    public boolean isEnded = false;
    protected GoogleApiClient mGoogleApiClient;
    protected LocationRequest mLocationRequest;



    private SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
    private double latitude = 0;
    private double longitude = 0;
    private float[] results1 = new float[1];
    private float distanceInMeters = 0;
    private Handler mHandler;


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


        String CHANNEL_ID = "FOREGROUND_CHANNEL";
        if (Build.VERSION.SDK_INT >= 26) {

            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "location_notification",
                    NotificationManager.IMPORTANCE_DEFAULT);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
        }

        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setOngoing(true)
                .setContentTitle("G Star")

                .setSmallIcon(R.drawable.gm_noti_logo)
                .setContentText("Running").build();

        startForeground(NOTIFICATION_ID, notification);
        Utility.getInstance().makeDescriptiveLogs("ON CREATE WAS HIT");

        sendLocationDataToServerPeriodically();


    }

    private void sendLocationDataToServerPeriodically() {


//getting the alarm manager
        AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

        //creating a new intent specifying the broadcast receiver
        Intent intentLR = new Intent(this, PostLocationReceiver.class);

        PendingIntent pi = PendingIntent.getBroadcast(this, 0, intentLR,
                PendingIntent.FLAG_UPDATE_CURRENT);


        if (android.os.Build.VERSION.SDK_INT >= 23) {
            assert am != null;
            am.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                    AlarmManager.INTERVAL_FIFTEEN_MINUTES, pi);
        } else if (Build.VERSION.SDK_INT >= 19) {
            if (am != null) {
                am.setInexactRepeating(AlarmManager.RTC_WAKEUP,
                        System.currentTimeMillis(), AlarmManager.INTERVAL_FIFTEEN_MINUTES, pi);
            }
        } else {
            if (am != null) {
                am.setRepeating(AlarmManager.RTC_WAKEUP,
                        System.currentTimeMillis(), AlarmManager.INTERVAL_FIFTEEN_MINUTES, pi);
            }
        }


    }

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

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

        isEnded = false;

        Utility.getInstance().makeDescriptiveLogs("ONSTART COMMAND WAS HIT");
        buildGoogleApiClient();
        if (mGoogleApiClient.isConnected() && mRequestingLocationUpdates) {
            startLocationUpdates();
        }
        mHandler = new Handler();
        return START_STICKY;
    }

    @Override
    public void onConnected(Bundle bundle) {
        startLocationUpdates();
    }

    @Override
    public void onConnectionSuspended(int i) {
        // The connection to Google Play services was lost for some reason. We call connect() to
        // attempt to re-establish the connection.

        mGoogleApiClient.connect();
    }

    @Override
    public void onLocationChanged(Location location) {

        if (location.getAccuracy() < HORIZONTAL_ACCURACY_IN_METERS)
            updateUI(location);
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        // Refer to the javadoc for ConnectionResult to see what error codes might be returned in
        // onConnectionFailed.

    }

    protected synchronized void buildGoogleApiClient() {

        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();

        createLocationRequest();
    }

    /**
     * Updates the latitude, the longitude, and the last location time in the UI.
     */
    private void updateUI(Location mCurrentLocation) {



        mHandler.post(() -> {
            /*GET DEVICE CURRENT BATTERY LEVEL*/
            int batteryPercent = Utility.getInstance().getBatteryPercentage(LocationService.this);


            /*  CALCULATE DISTANCE BETWEEN LAT LONG INTERVALS*/
            if (latitude != 0 && longitude != 0) {
                Location.distanceBetween(latitude, longitude, mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude(), results1);
                distanceInMeters = results1[0];
            }


            latitude = mCurrentLocation.getLatitude();
            longitude = mCurrentLocation.getLongitude();

            /*CHECK IF DEVICE HAS ACTIVE INTERNET CONNECTION*/
            String networkStatus = Utility.getInstance().checkConnection(LocationService.this) ? "1" : "0";


            /*CHECK NETWORK SIGNAL STRENGTH*/
            String signalStrength = Utility.getInstance().getSignalStrength(LocationService.this);

            SQLiteDBHandler db = SQLiteDBHandler.getInstance(LocationService.this);
            db.insertDeviceLocation(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude(), Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? mCurrentLocation.getSpeedAccuracyMetersPerSecond() : mCurrentLocation.getSpeed(), sdf.format(Calendar.getInstance().getTime()), distanceInMeters, batteryPercent, networkStatus, signalStrength);


        });

    }

    protected void createLocationRequest() {
        mGoogleApiClient.connect();
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setSmallestDisplacement(5);
        mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    }

    /**
     * Requests location updates from the FusedLocationApi.
     */
    public void startLocationUpdates() {
        if (!mRequestingLocationUpdates) {
            mRequestingLocationUpdates = true;

            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                return;
            }


            LocationServices.FusedLocationApi.requestLocationUpdates(
                    mGoogleApiClient, mLocationRequest, this);

            isEnded = true;
        }
    }

    /**
     * Removes location updates from the FusedLocationApi.
     */
    public void stopLocationUpdates() {
        if (mRequestingLocationUpdates) {
            mRequestingLocationUpdates = false;



            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);

        }
    }

    @Override
    public void onDestroy() {

        mHandler.removeCallbacksAndMessages(null);

        stopLocationUpdates();

        super.onDestroy();
    }


}

那么你的意思是我们不需要 WorkManager 吗? - IgorGanapolsky
@IgorGanapolsky 不,我分享了对我有用的内容,希望对某人有所帮助。使用WorkManager而不是AlarmManager会有更好的实现...这会为设备带来更多优势。 - coderBox
@coderBox 这会对电池优化产生什么影响? - Aanal Shah
现在的问题是,在Android 12及以上版本中,服务无法正常工作。因此,我们认为需要使用"Work Manager"来解决这个问题。 - karan

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接