我需要使用后台服务监视用户的位置,然后加载它们并向用户显示路径。
使用一个活动,很容易获得 GPS 位置,但是当我通过一个服务进行这项工作时,我遇到了一个问题,因为它似乎只对环形线程(或类似的东西)有效。
当我在互联网上搜索解决方案时,我发现许多人都遇到了同样的问题,但我找不到一个有效的解决方案。有些人说需要使用 ready-> loop-> exit,有些人说需要使用 handlerThread,但是我仍然不知道如何正确地执行这些操作。
我不明白在服务中实现位置侦听功能到底有什么问题。它看起来非常类似于您在 Activity 中所做的工作。 只需定义一个位置侦听器并注册位置更新即可。 您可以参考以下代码作为示例:
舱单档案:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:label="@string/app_name" android:name=".LocationCheckerActivity" > <intent-filter > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".MyService" android:process=":my_service" /> </application>
服务档案:
import android.app.Service; import android.content.Context; import android.content.Intent; import android.location.Location; import android.location.LocationManager; import android.os.Bundle; import android.os.IBinder; import android.util.Log; public class MyService extends Service { private static final String TAG = "BOOMBOOMTESTGPS"; private LocationManager mLocationManager = null; private static final int LOCATION_INTERVAL = 1000; private static final float LOCATION_DISTANCE = 10f; private class LocationListener implements android.location.LocationListener { Location mLastLocation; public LocationListener(String provider) { Log.e(TAG, "LocationListener " + provider); mLastLocation = new Location(provider); } @Override public void onLocationChanged(Location location) { Log.e(TAG, "onLocationChanged: " + location); mLastLocation.set(location); } @Override public void onProviderDisabled(String provider) { Log.e(TAG, "onProviderDisabled: " + provider); } @Override public void onProviderEnabled(String provider) { Log.e(TAG, "onProviderEnabled: " + provider); } @Override public void onStatusChanged(String provider, int status, Bundle extras) { Log.e(TAG, "onStatusChanged: " + provider); } } LocationListener[] mLocationListeners = new LocationListener[]{ new LocationListener(LocationManager.GPS_PROVIDER), new LocationListener(LocationManager.NETWORK_PROVIDER) }; @Override public IBinder onBind(Intent arg0) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.e(TAG, "onStartCommand"); super.onStartCommand(intent, flags, startId); return START_STICKY; } @Override public void onCreate() { Log.e(TAG, "onCreate"); initializeLocationManager(); try { mLocationManager.requestLocationUpdates( LocationManager.NETWORK_PROVIDER, LOCATION_INTERVAL, LOCATION_DISTANCE, mLocationListeners[1]); } catch (java.lang.SecurityException ex) { Log.i(TAG, "fail to request location update, ignore", ex); } catch (IllegalArgumentException ex) { Log.d(TAG, "network provider does not exist, " + ex.getMessage()); } try { mLocationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, LOCATION_INTERVAL, LOCATION_DISTANCE, mLocationListeners[0]); } catch (java.lang.SecurityException ex) { Log.i(TAG, "fail to request location update, ignore", ex); } catch (IllegalArgumentException ex) { Log.d(TAG, "gps provider does not exist " + ex.getMessage()); } } @Override public void onDestroy() { Log.e(TAG, "onDestroy"); super.onDestroy(); if (mLocationManager != null) { for (int i = 0; i < mLocationListeners.length; i++) { try { mLocationManager.removeUpdates(mLocationListeners[i]); } catch (Exception ex) { Log.i(TAG, "fail to remove location listners, ignore", ex); } } } } private void initializeLocationManager() { Log.e(TAG, "initializeLocationManager"); if (mLocationManager == null) { mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE); } } }
好的,我已经通过在服务的 onCreate 上创建一个处理程序,并通过这里调用 gps 函数来解决这个问题。
代码就是这么简单:
final handler=new Handler(Looper.getMainLooper());
然后,为了强制在 UI 上运行,我在其上调用 post。
post
作为补充,我以这种方式实现,并且通常在 Service 类中工作
为我服务
@Override public void onCreate() { mHandler = new Handler(Looper.getMainLooper()); mHandler.post(this); super.onCreate(); } @Override public void onDestroy() { mHandler.removeCallbacks(this); super.onDestroy(); } @Override public void run() { InciarGPSTracker(); }
public class GPSService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, com.google.android.gms.location.LocationListener { private LocationRequest mLocationRequest; private GoogleApiClient mGoogleApiClient; private static final String LOGSERVICE = "#######"; @Override public void onCreate() { super.onCreate(); buildGoogleApiClient(); Log.i(LOGSERVICE, "onCreate"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(LOGSERVICE, "onStartCommand"); if (!mGoogleApiClient.isConnected()) mGoogleApiClient.connect(); return START_STICKY; } @Override public void onConnected(Bundle bundle) { Log.i(LOGSERVICE, "onConnected" + bundle); Location l = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); if (l != null) { Log.i(LOGSERVICE, "lat " + l.getLatitude()); Log.i(LOGSERVICE, "lng " + l.getLongitude()); } startLocationUpdate(); } @Override public void onConnectionSuspended(int i) { Log.i(LOGSERVICE, "onConnectionSuspended " + i); } @Override public void onLocationChanged(Location location) { Log.i(LOGSERVICE, "lat " + location.getLatitude()); Log.i(LOGSERVICE, "lng " + location.getLongitude()); LatLng mLocation = (new LatLng(location.getLatitude(), location.getLongitude())); EventBus.getDefault().post(mLocation); } @Override public void onDestroy() { super.onDestroy(); Log.i(LOGSERVICE, "onDestroy - Estou sendo destruido "); } @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onConnectionFailed(ConnectionResult connectionResult) { Log.i(LOGSERVICE, "onConnectionFailed "); } private void initLocationRequest() { mLocationRequest = new LocationRequest(); mLocationRequest.setInterval(5000); mLocationRequest.setFastestInterval(2000); mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); } private void startLocationUpdate() { initLocationRequest(); 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; } LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this); } private void stopLocationUpdate() { LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this); } protected synchronized void buildGoogleApiClient() { mGoogleApiClient = new GoogleApiClient.Builder(this) .addOnConnectionFailedListener(this) .addConnectionCallbacks(this) .addApi(LocationServices.API) .build(); } }
所有这些答案都不能在 M-to-Android“ O”-8上运行,由于 Dozer 模式限制了服务——任何需要在后台运行离散事物的服务或任何后台操作将不再能够运行。
因此,这种方法是通过 BroadCastReciever 监听系统 FusedLocationapiClient,这个系统总是监听位置信息,甚至在 Doze 模式下也能工作。
发布链接将是毫无意义的,请搜索与广播接收机的融合位置。
谢谢
我的解决办法是:
Step1: 舱单登记服务
<receiver android:name=".MySMSBroadcastReceiver" android:exported="true"> <intent-filter> <action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED" /> </intent-filter> </receiver>
步骤2: 服务代码
public class FusedLocationService extends Service { private String mLastUpdateTime = null; // bunch of location related apis private FusedLocationProviderClient mFusedLocationClient; private SettingsClient mSettingsClient; private LocationRequest mLocationRequest; private LocationSettingsRequest mLocationSettingsRequest; private LocationCallback mLocationCallback; private Location lastLocation; // location updates interval - 10sec private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 5000; // fastest updates interval - 5 sec // location updates will be received if another app is requesting the locations // than your app can handle private static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS = 500; private DatabaseReference locationRef; private int notificationBuilder = 0; private boolean isInitRef; @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.log("LOCATION GET DURATION", "start in service"); init(); return START_STICKY; } /** * Initilize Location Apis * Create Builder if Share location true */ private void init() { mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this); mSettingsClient = LocationServices.getSettingsClient(this); mLocationCallback = new LocationCallback() { @Override public void onLocationResult(LocationResult locationResult) { super.onLocationResult(locationResult); receiveLocation(locationResult); } }; mLocationRequest = new LocationRequest(); mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS); mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS); mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder(); builder.addLocationRequest(mLocationRequest); mLocationSettingsRequest = builder.build(); startLocationUpdates(); } /** * Request Location Update */ @SuppressLint("MissingPermission") private void startLocationUpdates() { mSettingsClient .checkLocationSettings(mLocationSettingsRequest) .addOnSuccessListener(locationSettingsResponse -> { Log.log(TAG, "All location settings are satisfied. No MissingPermission"); //noinspection MissingPermission mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper()); }) .addOnFailureListener(e -> { int statusCode = ((ApiException) e).getStatusCode(); switch (statusCode) { case LocationSettingsStatusCodes.RESOLUTION_REQUIRED: Log.loge("Location settings are not satisfied. Attempting to upgrade " + "location settings "); break; case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE: Log.loge("Location settings are inadequate, and cannot be " + "fixed here. Fix in Settings."); } }); } /** * onLocationResult * on Receive Location share to other activity and save if save true * * @param locationResult */ private void receiveLocation(LocationResult locationResult) { lastLocation = locationResult.getLastLocation(); LocationInstance.getInstance().changeState(lastLocation); saveLocation(); } private void saveLocation() { String saveLocation = getsaveLocationStatus(this); if (saveLocation.equalsIgnoreCase("true") && notificationBuilder == 0) { notificationBuilder(); notificationBuilder = 1; } else if (saveLocation.equalsIgnoreCase("false") && notificationBuilder == 1) { ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).cancel(1); notificationBuilder = 0; } Log.logd("receiveLocation : Share :- " + saveLocation + ", [Lat " + lastLocation.getLatitude() + ", Lng" + lastLocation.getLongitude() + "], Time :- " + mLastUpdateTime); if (saveLocation.equalsIgnoreCase("true") || getPreviousMin() < getCurrentMin()) { setLatLng(this, lastLocation); mLastUpdateTime = DateFormat.getTimeInstance().format(new Date()); if (isOnline(this) && !getUserId(this).equalsIgnoreCase("")) { if (!isInitRef) { locationRef = getFirebaseInstance().child(getUserId(this)).child("location"); isInitRef = true; } if (isInitRef) { locationRef.setValue(new LocationModel(lastLocation.getLatitude(), lastLocation.getLongitude(), mLastUpdateTime)); } } } } private int getPreviousMin() { int previous_min = 0; if (mLastUpdateTime != null) { String[] pretime = mLastUpdateTime.split(":"); previous_min = Integer.parseInt(pretime[1].trim()) + 1; if (previous_min > 59) { previous_min = 0; } } return previous_min; } @Override public void onDestroy() { super.onDestroy(); stopLocationUpdates(); } /** * Remove Location Update */ public void stopLocationUpdates() { mFusedLocationClient .removeLocationUpdates(mLocationCallback) .addOnCompleteListener(task -> Log.logd("stopLocationUpdates : ")); } private void notificationBuilder() { if (Build.VERSION.SDK_INT >= 26) { String CHANNEL_ID = "my_channel_01"; NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "Channel human readable title", NotificationManager.IMPORTANCE_DEFAULT); ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel); Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("") .setContentText("").build(); startForeground(1, notification); } } @Override public IBinder onBind(Intent intent) { return null; } }
第三步: 清单
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
第四步: 分级
implementation 'com.google.android.gms:play-services-location:16.0.0'
看看谷歌播放位置示例 GitHub 存储库
使用 Foreground Service 进行位置更新: 使用绑定和启动的前台服务获取关于设备位置的更新。
使用 PendingInent 进行位置更新: 使用 PendingIntent获取关于设备位置的更新。示例显示了使用 IntentService和 BroadcastReceiver的实现。
PendingIntent
IntentService
BroadcastReceiver