如何在 Android 中像在 Google 地图中一样获得持续的位置更新?
Posted
技术标签:
【中文标题】如何在 Android 中像在 Google 地图中一样获得持续的位置更新?【英文标题】:How can I get continuous location updates in Android like in Google Maps? 【发布时间】:2017-05-20 22:11:49 【问题描述】:我正在构建一个朋友跟踪 android 应用。当我的朋友激活该应用程序并带着他的 GPS 和蜂窝数据离开时,我需要在我的设备上跟踪他。就是这个概念。
我已经实现了 LocationListener 类,现在我可以从 Gps 或网络获取最后更新的位置,但除非我启动 Google 地图并返回到我的应用程序,否则不会更新。谷歌搜索后,我了解到位置缓存仅由 GMaps 更新。!
-
是否有其他方法可以持续更新位置?
如果我需要在设备锁定后不使用 Wakelock 继续获取位置怎么办?
这是我的位置监听类:
package com.amazinginside;
/** AMAZING LOCATION SUPPORT CLASS, Devoloped By SANGEETH NANDAKUMAR */
import android.app.AlertDialog;
import android.app.Service;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.Settings;
public class AmazingLocation extends Service implements LocationListener
private final Context mContext;
boolean isGPSEnabled = false;
boolean isNetworkEnabled = false;
boolean canGetLocation = false;
Location location;
double latitude=0.0;
double longitude=0.0;
//MINIMUM DISTANCE FOR UPDATE (meters)
private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 0; // 0 Meters
//MINIMUM TIME BETWEEN UPDATES
private static final long MIN_TIME_BW_UPDATES = 1000 * 0; // 0 Seconds
//LOCATION MANAGER
protected LocationManager locationManager;
//CONSTRUCTOR
public AmazingLocation(Context context)
this.mContext = context;
getLocation();
//LOCATION PROVISION
public Location getLocation()
try
//GET LOCATION MANAGER
locationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE);
//CHECK GPS STATE
isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
//CHECK NETWORK STATE
isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (!isGPSEnabled && !isNetworkEnabled)
//NO LOCATION PROVIDERS
else
this.canGetLocation = true;
/** GET LOCATION FROM NETWORK */
//FIRST GET LOCATION FROM NETWORK
if (isNetworkEnabled)
//REQUEST LOCATION
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
if (locationManager != null)
//START WITH LAST KNOWN LOCATION
location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
//EXTRACT LOCATION
if (location != null)
latitude = location.getLatitude();
longitude = location.getLongitude();
/** GET LOCATION FROM GPS SENSOR */
//THEN GET LOCATION FROM GPS
if (isGPSEnabled)
if (location == null)
//REQUEST GPS LOCATION
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
if (locationManager != null)
//EXTRACT LAST KNOWN LOCATION
location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
//RETURN LOCATION
if (location != null)
latitude = location.getLatitude();
longitude = location.getLongitude();
catch (Exception e)
e.printStackTrace();
return location;
//STOP GPS SENSOR
public void stopUsingGPS()
if(locationManager != null)
locationManager.removeUpdates(AmazingLocation.this);
//EXTRACT LATTITUDE
public double getLatitude()
if(location != null)
latitude = location.getLatitude();
// return latitude
return latitude;
//EXTACT LONGITUDE
public double getLongitude()
if(location != null)
longitude = location.getLongitude();
// return longitude
return longitude;
//CAN I GET THE LOCATION.?
public AmazingStatus canGetLocation()
AmazingStatus status=new AmazingStatus();
if(this.canGetLocation)
status.setStatus(true);
status.setErrorcode(0);
status.setErrormsg("Task completed");
else
status.setStatus(false);
status.setErrorcode(145);
status.setErrormsg("Please turn on GPS access manually");
return status;
//SHOW LOCATION SETTINGS
public AmazingStatus showSettingsAlert()
final AmazingStatus status=new AmazingStatus();
AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
alertDialog.setTitle("REQUIRES LOCATION ACCESS");
alertDialog.setMessage("Please allow GPS access to this app");
//POSSITIVE REPLY
alertDialog.setPositiveButton("Allow", new DialogInterface.OnClickListener()
public void onClick(DialogInterface dialog,int which)
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
mContext.startActivity(intent);
status.setStatus(true);
status.setErrorcode(0);
status.setErrormsg("Task completed");
);
//NEGATIVE REPLY
alertDialog.setNegativeButton("Deny", new DialogInterface.OnClickListener()
public void onClick(DialogInterface dialog, int which)
status.setStatus(false);
status.setErrorcode(408);
status.setErrormsg("User denied permission");
dialog.cancel();
);
// Showing Alert Message
alertDialog.show();
return status;
//UNUSED OVERRIDE METHORDS...
@Override
public void onLocationChanged(Location location)
getLocation();
@Override
public void onProviderDisabled(String provider)
@Override
public void onProviderEnabled(String provider)
getLocation();
@Override
public void onStatusChanged(String provider, int status, Bundle extras)
getLocation();
@Override
public IBinder onBind(Intent arg0)
return null;
这是我的onCreate()
方法:
@Override protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//CREATE A BUTTON HANDLER
Button start_btn=(Button)findViewById(R.id.start_location_streaming);
//ON BUTTON CLICK EVENT
start_btn.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
//REPEAT A METHORD AT SPECIFIC INTERVALS
Timer myTimer = new Timer();
myTimer.schedule(new TimerTask()
@Override
public void run()
TimerMethod();
, 0, 8000);
);
这些是其他方法:
private void TimerMethod()
//START METHORD
this.runOnUiThread(Timer_Tick);
//LOCATION REPORTING METHORD
private Runnable Timer_Tick = new Runnable()
public void run()
Toast.makeText(MainActivity.this, "Current latitude : "+Double.toString(getLocation().latitude), Toast.LENGTH_SHORT).show();
Toast.makeText(MainActivity.this, "Current longitude : "+Double.toString(getLocation().longitude), Toast.LENGTH_SHORT).show();
;
private LatLng getLocation()
//CREATE A LOCATION CLASS INSTANCE
AmazingLocation gps = new AmazingLocation(this);
//RETRIVE LOCATION
double latitude = gps.getLatitude();
double longitude = gps.getLongitude();
//RETURN LOCATION
LatLng loc=new LatLng(latitude,longitude);
return loc;
现在的问题是,toast 只显示先前已知的位置,并且不会更新,除非我打开 Google 地图并返回。
任何帮助都会对我有很大帮助。
【问题讨论】:
您的代码已损坏。您正在使用名为 GPSTracker 的旧代码的变体。这段代码设计得很糟糕,充满了错误——尤其是它不知道提供程序启用和实际启用之间的区别。 谢谢加布先生。实际上我很惊讶,因为这段代码被预先破坏了。让我看看你提供的链接。 嗨 Kannan.. 你能解决这个问题吗? 我的 Google Play 服务有问题。融合位置提供程序的工作原理 它是喀拉拉邦的 chitappan 追踪器.. 【参考方案1】:在 Android 中使用 Fused location provider 设置间隔:
例如,像这样创建您的活动:
public class LocationActivity extends Activity implements
LocationListener,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener
private static final String TAG = "LocationActivity";
private static final long INTERVAL = 1000 * 10;
private static final long FASTEST_INTERVAL = 1000 * 5;
Button btnFusedLocation;
TextView tvLocation;
LocationRequest mLocationRequest;
GoogleApiClient mGoogleApiClient;
Location mCurrentLocation;
String mLastUpdateTime;
protected void createLocationRequest()
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(INTERVAL);
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate ...............................");
//show error dialog if GoolglePlayServices not available
if (!isGooglePlayServicesAvailable())
finish();
createLocationRequest();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
setContentView(R.layout.activity_main);
tvLocation = (TextView) findViewById(R.id.tvLocation);
btnFusedLocation = (Button) findViewById(R.id.btnShowLocation);
btnFusedLocation.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View arg0)
updateUI();
);
@Override
public void onStart()
super.onStart();
if (mGoogleApiClient.isConnected())
startLocationUpdates();
Log.d(TAG, "Location update resumed .....................");
@Override
public void onStop()
super.onStop();
Log.d(TAG, "onStop fired ..............");
mGoogleApiClient.disconnect();
Log.d(TAG, "isConnected ...............: " + mGoogleApiClient.isConnected());
private boolean isGooglePlayServicesAvailable()
int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (ConnectionResult.SUCCESS == status)
return true;
else
GooglePlayServicesUtil.getErrorDialog(status, this, 0).show();
return false;
@Override
public void onConnected(Bundle bundle)
Log.d(TAG, "onConnected - isConnected ...............: " + mGoogleApiClient.isConnected());
startLocationUpdates();
protected void startLocationUpdates()
PendingResult<Status> pendingResult = LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
Log.d(TAG, "Location update started ..............: ");
@Override
public void onConnectionSuspended(int i)
@Override
public void onConnectionFailed(ConnectionResult connectionResult)
Log.d(TAG, "Connection failed: " + connectionResult.toString());
@Override
public void onLocationChanged(Location location)
Log.d(TAG, "Firing onLocationChanged..............................................");
mCurrentLocation = location;
mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
updateUI();
private void updateUI()
Log.d(TAG, "UI update initiated .............");
if (null != mCurrentLocation)
String lat = String.valueOf(mCurrentLocation.getLatitude());
String lng = String.valueOf(mCurrentLocation.getLongitude());
tvLocation.setText("At Time: " + mLastUpdateTime + "\n" +
"Latitude: " + lat + "\n" +
"Longitude: " + lng + "\n" +
"Accuracy: " + mCurrentLocation.getAccuracy() + "\n" +
"Provider: " + mCurrentLocation.getProvider());
else
Log.d(TAG, "location is null ...............");
@Override
protected void onPause()
super.onPause();
stopLocationUpdates();
protected void stopLocationUpdates()
LocationServices.FusedLocationApi.removeLocationUpdates(
mGoogleApiClient, this);
Log.d(TAG, "Location update stopped .......................");
@Override
public void onResume()
super.onResume();
if (mGoogleApiClient.isConnected())
startLocationUpdates();
Log.d(TAG, "Location update resumed .....................");
需要 Google Play 服务:
【讨论】:
抱歉回复太晚了。不幸的是,我的 Google Play 服务 SDK 被破坏,无法提供 Fused Location Provider 支持。经过长时间的研究,我已经让它工作了,你的代码现在可以正常工作了。 #已投票 这非常有效。但是您缺少 mGoogleApiClient.connect(); 此代码对我不起作用。收到错误 java.lang.NoClassDefFoundError: 解析失败:Lcom/google/android/gms/common/api/Api$zzc; GoogleApiClient 现已弃用。【参考方案2】:要获得持续的位置更新,您可以参考上面提供的答案。
但您也可以使用 LocationServices,它比其他方法更快,而且获取位置更加简单高效。
此方法退出时间较长,但请按照提供的所有步骤进行操作
所以让我提供一个简短的工作:
在你的 gradle 应用文件中添加这两个依赖项
实现 'com.google.android.gms:play-services-maps:17.0.0' 实施 'com.google.android.gms:play-services-location:17.0.0'
在应用标签外的清单文件中添加这些权限
在 onCreate 之外声明变量
私有 FusedLocationProviderClient fusedLocationClient; 私有 LocationRequest mLLocationRequest; 私有位置回调 mlocationCallback; 私有 LocationSettingsRequest.Builder 构建器; 私有静态最终 int REQUEST_CHECK_SETTINGS = 102;
现在在 onCreate 里面:
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this); fetchLastLocation(); mlocationCallback = 新 LocationCallback() @覆盖 公共无效 onLocationResult(LocationResult locationResult) if (locationResult == null) 返回; 对于(位置位置:locationResult.getLocations()) // 使用位置数据更新 UI // ... Log.e("CONTINIOUSLOC:", location.toString()); ; ;
mLocationRequest = createLocationRequest(); builder = new LocationSettingsRequest.Builder() .addLocationRequest(mLocationRequest); checkLocationSetting(builder);
没有定义 fetchLastLocation 方法
private void fetchLastLocation()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
// TODO: Consider calling
// Activity#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 Activity#requestPermissions for more details.
// Toast.makeText(MainActivity.this, "权限未授予,请允许", Toast.LENGTH_LONG).show(); showPermissionAlert(); 返回; fusedLocationClient.getLastLocation() .addOnSuccessListener(this, new OnSuccessListener() @覆盖 公共无效onSuccess(位置位置) // 获取最后一个已知位置。在极少数情况下,这可能为空。 如果(位置!= null) // 处理位置对象的逻辑 Log.e("最后一个位置:", location.toString()); // 你会在这里得到你最后的位置 );
现在定义另外两种权限请求方法
@覆盖 公共无效 onRequestPermissionsResult(int requestCode,@NonNull String[] 权限,@NonNull int[] grantResults) 开关(请求代码) 案例 123: // 如果请求被取消,结果数组为空。 if (grantResults[0] == PackageManager.PERMISSION_DENIED) // 权限被拒绝,显示警报以解释权限 showPermissionAlert(); 别的 //现在授予权限启动后台服务 if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) fetchLastLocation();
private void showPermissionAlert()
if (ActivityCompat.checkSelfPermission(MainHomeActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(MainHomeActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
ActivityCompat.requestPermissions(MainHomeActivity.this, new String[]Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, 123);
现在定义 createLocationRequest 方法和 checkLocationSetting 方法:
受保护的 LocationRequest createLocationRequest() LocationRequest mLocationRequest = LocationRequest.create(); mLocationRequest.setInterval(30000); mLocationRequest.setFastestInterval(10000); mLocationRequest.setSmallestDisplacement(30); mLLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); 返回mLLocationRequest;
private void checkLocationSetting(LocationSettingsRequest.Builder builder)
SettingsClient client = LocationServices.getSettingsClient(this);
Task<LocationSettingsResponse> task = client.checkLocationSettings(builder.build());
task.addOnSuccessListener(this, new OnSuccessListener<LocationSettingsResponse>()
@Override
public void onSuccess(LocationSettingsResponse locationSettingsResponse)
// All location settings are satisfied. The client can initialize
// location requests here.
// ...
startLocationUpdates();
return;
);
task.addOnFailureListener(this, new OnFailureListener()
@Override
public void onFailure(@NonNull final Exception e)
if (e instanceof ResolvableApiException)
// Location settings are not satisfied, but this can be fixed
AlertDialog.Builder builder1 = new AlertDialog.Builder(mContext);
builder1.setTitle("Continious Location Request");
builder1.setMessage("This request is essential to get location update continiously");
builder1.create();
builder1.setPositiveButton("OK", new DialogInterface.OnClickListener()
@Override
public void onClick(DialogInterface dialog, int which)
ResolvableApiException resolvable = (ResolvableApiException) e;
try
resolvable.startResolutionForResult(MainHomeActivity.this,
REQUEST_CHECK_SETTINGS);
catch (IntentSender.SendIntentException e1)
e1.printStackTrace();
);
builder1.setNegativeButton("Cancel", new DialogInterface.OnClickListener()
@Override
public void onClick(DialogInterface dialog, int which)
Toast.makeText(mContext, "Location update permission not granted", Toast.LENGTH_LONG).show();
);
builder1.show();
);
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data)
if (requestCode == REQUEST_CHECK_SETTINGS)
if (resultCode == RESULT_OK)
// All location settings are satisfied. The client can initialize
// location requests here.
startLocationUpdates();
else
checkLocationSetting(builder);
现在终于定义 startLocationUpdates 和 stopLocationUpdates 方法:
公共无效 startLocationUpdates() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) // TODO: 考虑调用 // 活动#requestPermissions // 这里请求丢失的权限,然后覆盖 // public void onRequestPermissionsResult(int requestCode, String[] 权限, // int[] grantResults) // 处理用户授予权限的情况。请参阅文档 // 对于 Activity#requestPermissions 了解更多详细信息。 返回; fusedLocationClient.requestLocationUpdates(mLocationRequest, 移动回调, null /* 循环器 */);
私人无效停止位置更新() fusedLocationClient.removeLocationUpdates(mlocationCallback);
注意:将上下文替换为您的类上下文并在您的类的 onDestroy 方法中调用 stopLocationUpdates()
注意:更多信息或疑问,您可以参考:
https://developer.android.com/training/location/retrieve-current https://developer.android.com/training/location/change-location-settings https://developer.android.com/training/location/receive-location-updates
您将在 Logcat 中获得您的位置。
希望这会希望你或其他人!
【讨论】:
【参考方案3】:我相信,与其重新发明***,不如使用易于实施且在这种情况下更省电的第三方库之一。我发现的图书馆之一是SmartLocation。您可以在 build.gradle (app) 中添加以下依赖项以开始使用该库。
compile 'io.nlopez.smartlocation:library:3.2.9'
添加依赖后,你应该重新构建项目以获取引用。
例如,您可以在 Activity 中尝试以下代码。
Button start_btn=(Button)findViewById(R.id.start_location_streaming);
Context context = start_btn.getContext();
Handler handler = new Handler();
start_btn.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
SmartLocation.with(context).location().start(locationListener);
);
OnLocationUpdatedListener locationListener = new OnLocationUpdatedListener(
@Override
public void onLocationUpdated(Location location)
double lat = location.getLatitude();
double lng = location.getLongitude();
handler.postDelayed(locationRunnable,8000);
);
Runnable locationRunnable = new Runnable(
@Override
public void run()
SmartLocation.with(context).location().start(locationListener);
);
您可以在 onStop() 方法中停止位置跟踪
@Override
public void onStop()
SmartLocation.with(context).location().stop();
super.onStop();
SmartLocation 库将为您提供超出预期的功能,只需尝试一次。
注意:确保您的应用程序确实具有 ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION(两者)以获得准确的结果。不要忘记在运行时为 Android 6.0 及更高版本请求权限。
【讨论】:
谢谢 Hitesh。我也相信重新发明***不是明智的选择。让我试试这个 SmartLocation 库 当然。但我需要让它工作,然后我会做标记它 最好的方法 - 最好的方法 - 并且简单 - thanx allooooot 它将我重定向到我在印度的利比亚。【参考方案4】:您应该使用 android 服务,而不是应用程序本身。这样你就可以实现在后台连续运行代码,即使应用关闭,你也会收到位置。
https://www.tutorialspoint.com/android/android_services.htm
【讨论】:
感谢您的帮助。如果我没有关闭我的应用程序的 UI,这个 LocationListner 可以在后台运行而不会被操作系统杀死吗?如果是,那为什么它不提供连续的位置坐标? @Kannan ,是的。如果应用程序 ui 没有关闭,那么 LocationListener 必须正在运行而不被杀死。我认为它不会更新,因为您必须打开 gps 然后询问位置。 Google Maps 的作用是打开 gps 并更新当前位置,这就是您打开 G. Maps 时获得更新位置的原因。 感谢 tec 技术,我尝试打开 GPS 和 4G 数据服务。我需要一种方法来摆脱以前已知的准确位置以不断更新引脚。仅当我启动 Google 地图并返回时才会发生这种情况。只有谷歌地图能够更新位置捕获。我需要在我的应用中更新 @Kannan,我用谷歌搜索了一下,我发现了这个:developer.android.com/training/location/…。它来自 android 官方网站,并且说有一种方法可以从您的应用程序中实际更新 LastKnownLocation。请检查一下,它可能会解决您的问题。 由于 Android Oreo 后台服务的变化,位置更新将无法正常工作。见developer.android.com/about/versions/oreo/background.html以上是关于如何在 Android 中像在 Google 地图中一样获得持续的位置更新?的主要内容,如果未能解决你的问题,请参考以下文章
在 Android 中,如何像在谷歌地图中一样显示带有方角的警报对话框?
循环在vue js中像在php foreach中获取html表
Android - 如何在 Google 地图上为 Google 徽标设置底部填充