Android新手求助:如何在Service中运行onLocationChanged方法
解决Service中持续获取位置并生成地图标记的问题
作为Android新手,在Service里处理位置更新确实容易踩几个典型的坑,我来帮你一步步梳理和修正:
首先要规避的核心问题
- Service不能直接操作UI控件:你的代码里有
tvlattitude和tvlongitude这两个TextView,Service是后台组件,没有自己的UI上下文,直接持有这些控件引用会导致内存泄漏或者空指针异常,绝对不能这么做。 - GoogleMap不应在Service中持有:
mMap实例是和Activity/MapFragment的生命周期强绑定的,Service里直接使用它大概率会出问题——比如Activity已经销毁但Service还在运行,调用Map的方法就会崩溃。
修正后的实现思路
我们把职责拆分清楚,让每个组件干自己擅长的事:
- Service只负责后台持续获取位置更新
- 通过本地广播(LocalBroadcastManager)或者LiveData把位置数据传递给前台的Map Activity
- Map Activity接收位置数据后,在GoogleMap上添加标记
代码示例
1. 修正后的MyService类
public class MyService extends Service implements LocationListener { private GoogleApiClient mGoogleApiClient; private LocationRequest mLocationRequest; @Override public void onCreate() { super.onCreate(); // 初始化GoogleApiClient buildGoogleApiClient(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (mGoogleApiClient != null && !mGoogleApiClient.isConnected()) { mGoogleApiClient.connect(); } // Android 8.0+ 需设置前台服务,避免被系统杀死 startForegroundServiceNotification(); return START_STICKY; // 保证Service被意外杀死后能重启 } private void startForegroundServiceNotification() { // 先创建通知渠道(Android 8.0+ 必需) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel("LOCATION_CHANNEL", "位置更新", NotificationManager.IMPORTANCE_LOW); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } // 构建前台通知 Notification notification = new NotificationCompat.Builder(this, "LOCATION_CHANNEL") .setContentTitle("位置追踪中") .setContentText("正在持续获取位置数据") .setSmallIcon(R.drawable.ic_location) .build(); startForeground(1, notification); } private synchronized void buildGoogleApiClient() { mGoogleApiClient = new GoogleApiClient.Builder(this) .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() { @Override public void onConnected(@Nullable Bundle bundle) { // 连接成功后开始请求位置更新 startLocationUpdates(); } @Override public void onConnectionSuspended(int i) { mGoogleApiClient.connect(); } }) .addOnConnectionFailedListener(connectionResult -> { // 这里可以添加连接失败的提示逻辑 }) .addApi(LocationServices.API) .build(); } private void startLocationUpdates() { mLocationRequest = new LocationRequest(); mLocationRequest.setInterval(10000); // 10秒更新一次,可按需调整 mLocationRequest.setFastestInterval(5000); mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); // 检查位置权限 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); } @Override public void onLocationChanged(Location location) { // 获取到位置后,通过本地广播发送给Activity Intent intent = new Intent("LOCATION_UPDATED_ACTION"); intent.putExtra("lat", location.getLatitude()); intent.putExtra("lng", location.getLongitude()); LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } @Override public void onDestroy() { super.onDestroy(); // 停止位置更新,释放资源 if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) { LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this); mGoogleApiClient.disconnect(); } } @Nullable @Override public IBinder onBind(Intent intent) { return null; // 不需要绑定服务时返回null } // LocationListener的其他方法空实现即可 @Override public void onStatusChanged(String provider, int status, Bundle extras) {} @Override public void onProviderEnabled(String provider) {} @Override public void onProviderDisabled(String provider) {} }
2. 接收位置并添加标记的Map Activity
public class MapActivity extends AppCompatActivity implements OnMapReadyCallback { private GoogleMap mMap; private BroadcastReceiver locationReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_map); // 初始化MapFragment SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map); mapFragment.getMapAsync(this); // 注册广播接收器,接收Service发来的位置数据 locationReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { double lat = intent.getDoubleExtra("lat", 0); double lng = intent.getDoubleExtra("lng", 0); // 在地图上添加新标记 addNewLocationMarker(lat, lng); } }; LocalBroadcastManager.getInstance(this).registerReceiver(locationReceiver, new IntentFilter("LOCATION_UPDATED_ACTION")); } @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; // 初始化地图初始位置(可按需修改) LatLng initialLoc = new LatLng(39.9042, 116.4074); mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(initialLoc, 12)); } private void addNewLocationMarker(double lat, double lng) { if (mMap != null) { LatLng newLoc = new LatLng(lat, lng); mMap.addMarker(new MarkerOptions() .position(newLoc) .title("当前位置") .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE))); // 可选:自动移动地图到新位置 mMap.moveCamera(CameraUpdateFactory.newLatLng(newLoc)); } } @Override protected void onStart() { super.onStart(); // 启动位置追踪Service startService(new Intent(this, MyService.class)); } @Override protected void onStop() { super.onStop(); // 停止Service并注销广播 stopService(new Intent(this, MyService.class)); LocalBroadcastManager.getInstance(this).unregisterReceiver(locationReceiver); } }
额外注意事项
- 权限配置:别忘了在Manifest里添加必要权限:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <service android:name=".MyService" /> - 运行时权限申请:在Activity里要先申请位置权限,用户同意后再启动Service,不然位置更新会失效。
- 性能优化:位置更新间隔不要设置得太频繁,避免过度耗电,根据你的实际需求调整
setInterval和setFastestInterval的数值。
内容的提问来源于stack exchange,提问作者stuck
相关产品推荐
相关产品推荐

