如何使用 FUSED LOCATION API 优化电池 - Android
Posted
技术标签:
【中文标题】如何使用 FUSED LOCATION API 优化电池 - Android【英文标题】:How to optimise battery with FUSED LOCATION API - Android 【发布时间】:2017-05-04 20:55:59 【问题描述】:您好,我在 android 中遇到以下问题/问题与位置 API 相关
电池消耗高达 30% - 40%,导致大量电池消耗。
状态栏中的位置图标始终处于打开状态,即使在关闭应用和卸载应用时它也会自动关闭。
要求:
打开应用时需要用户位置。 即使应用程序未打开或未使用基于距离,我也需要用户位置 - 需要在后台使用用户位置。方法:
带 GPS
API 使用 FUSED LOCATION API 和未决意图。
LocationManager - 检查 GPS 开/关状态。
代码演练:
在 OnCreate 中我正在获取位置管理器实例 - 获取位置管理器的实例。
检查是否启用 GPS 或网络状态是否可用,否则显示对话框以启用位置:代码:-
// get GPS state.
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (isGPSLocationEnabled(locationManager))
buildGooleLocationApiClient();
else if (isNetworkLocationEnabled(locationManager))
buildGooleLocationApiClient();
else
showAlert();
goolgeLocationAPiClient 的代码:在此方法中,我正在检查 android 版本、请求权限并启用服务
private void buildGooleLocationApiClient()
if (Build.VERSION.SDK_INT >= 23)
int isFineLocationPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
int isCoarseLocationPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION);
if (isFineLocationPermission == PackageManager.PERMISSION_DENIED || isCoarseLocationPermission == PackageManager.PERMISSION_DENIED)
requestPermission();
else
checkGoogleLocationApiClient();
else
checkGoogleLocationApiClient();
构建 GoogleAPI 客户端:
private void checkGoogleLocationApiClient()
try
if (mGoogleApiClient != null)
if (mGoogleApiClient.isConnected())
getMyLocationCampaigns();
else
mGoogleApiClient.connect();
else
buildGoogleApiClient();
catch (Exception e)
e.printStackTrace();
private void getMyLocationCampaigns()
if (mCurrentLocation != null)
getData(mCurrentLocation.getLatitude()+"",mCurrentLocation.getLongitude()+"");
else
try
mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
getData(mCurrentLocation.getLatitude()+"",mCurrentLocation.getLongitude()+"");
catch (SecurityException ex)
ex.printStackTrace();
getData("","");
private synchronized void buildGoogleApiClient()
try
Log.i(TAG, "activity Building GoogleApiClient===");
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
createLocationRequest();
catch (Exception e)
e.printStackTrace();
getData("","");
private void createLocationRequest()
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(60 * 60 * 1000);
mLocationRequest.setFastestInterval(60 * 1000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
mLocationRequest.setSmallestDisplacement(100);
connectGoogleApiClient();
private void connectGoogleApiClient()
if (mGoogleApiClient != null)
if (!mGoogleApiClient.isConnected())
mGoogleApiClient.connect();
@Override
public void onLocationChanged(Location location)
mCurrentLocation = location;
@Override
public void onConnected(@Nullable Bundle bundle)
if (mCurrentLocation == null)
try
mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mCurrentLocation != null)
// MyAPICALL getData(mCurrentLocation.getLatitude()+"",mCurrentLocation.getLongitude()+"");
else
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,mLocationRequest, this);
mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mCurrentLocation == null)
if (locationManager != null)
String provider = Utils.getUserLastLocation(locationManager);
if (provider != null)
try
Location location = locationManager.getLastKnownLocation(provider);
if (location != null)
getData(location.getLatitude() + "", location.getLongitude() + "");
else
getData("", "");
catch (SecurityException e)
e.printStackTrace();
else
getData(mCurrentLocation.getLatitude()+"",mCurrentLocation.getLongitude()+"");
catch (SecurityException ex)
ex.printStackTrace();
catch (Exception e)
e.printStackTrace();
getData("","");
以待定意图在后台获取位置的方法
private void startLocationUpdates()
try
Intent receiverIntentService = new Intent(this, LocationIntentService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, 1, receiverIntentService, 0);
if (mGoogleApiClient != null)
if (mGoogleApiClient.isConnected())
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, pendingIntent);
catch (SecurityException se)
se.printStackTrace();
BroadCastReceiver:如果设备重启:
public class LocationBroadcastReceiver extends BroadcastReceiver implements
GoogleApiClient.ConnectionCallbacks,GoogleApiClient.OnConnectionFailedListener, LocationListener
Context context;
protected GoogleApiClient mGoogleApiClient;
protected LocationRequest mLocationRequest;
protected Location mCurrentLocation;
public static Boolean mRequestingLocationUpdates = false;
SharedPreferences checkUserStatus;
public LocationBroadcastReceiver()
@Override
public void onReceive(Context context, Intent intent)
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
try
this.context = context;
checkUserStatus = context.getSharedPreferences(Params.LOGIN_DETAILS_PREFERENCE, 0);
String isUserLogedIn = checkUserStatus.getString(Params.TOKEN,"");
// if user is still logged in then only trigger background service
if (!isUserLogedIn.equals(""))
buildGoogleApiClient();
if (mGoogleApiClient != null)
if (mGoogleApiClient.isConnected() && mRequestingLocationUpdates)
startLocationUpdates();
else
buildGoogleApiClient();
else
buildGoogleApiClient();
catch (Exception e)
e.printStackTrace();
@Override
public void onConnected(Bundle bundle)
startLocationUpdates();
@Override
public void onConnectionSuspended(int i)
mGoogleApiClient.connect();
@Override
public void onConnectionFailed(ConnectionResult connectionResult)
Log.i("Broadcast receiver", "Connection failed: ConnectionResult.getErrorCode() = " + connectionResult.getErrorCode());
@Override
public void onLocationChanged(Location location)
mCurrentLocation = location;
protected synchronized void buildGoogleApiClient()
mGoogleApiClient = new GoogleApiClient.Builder(context)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
createLocationRequest();
protected void createLocationRequest()
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(60 * 60 * 1000);
mLocationRequest.setFastestInterval(60 * 1000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
mLocationRequest.setSmallestDisplacement(100);
protected void startLocationUpdates()
try
Intent receiverIntentService = new Intent(context,LocationIntentService.class);
PendingIntent pendingIntent = PendingIntent.getService(context,1,receiverIntentService,0);
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, pendingIntent);
catch (SecurityException se)
se.printStackTrace();
catch (Exception e)
e.printStackTrace();
我的意图服务类:获取用户更新的位置并进行 API 调用
public class LocationIntentService extends IntentService
Context context;
Bitmap myBitmap;
URL url;
SharedPreferences.Editor mMyLastLocationHolder;
SharedPreferences mMyLastLocation;
SharedPreferences checkUserStatus;
public LocationIntentService()
super("LocationIntentService");
@Override
protected void onHandleIntent(Intent intent)
if (intent != null)
Bundle bundle = intent.getExtras();
if (bundle != null)
Location location = bundle.getParcelable("com.google.android.location.LOCATION");
if (location != null)
context = getApplicationContext();
// API call to server
updateAPI(location.getLatitude()+"",location.getLongitude()+"");
Log.v("TAG LOCATION ", " ==== " + location.getLatitude() + " - " + location.getLongitude() + " ==== ");
Log.v("TAG LOCATION ", " ==== calling my-campaigns near me ========");
/**
* Handle action Foo in the provided background thread with the provided
* parameters.
*/
private void handleActionFoo(String param1, String param2)
// TODO: Handle action Foo
throw new UnsupportedOperationException("Not yet implemented");
/**
* Handle action Baz in the provided background thread with the provided
* parameters.
*/
private void handleActionBaz(String param1, String param2)
// TODO: Handle action Baz
throw new UnsupportedOperationException("Not yet implemented");
【问题讨论】:
我正在使用 FusedLocationApi 做类似的事情(我使用了长时间运行的服务而不是 IntentService),并且我的电池使用量很少(每 3 分钟更新一次位置)。我怀疑您的问题更有可能来自 LocationManager。你能在没有它的情况下尝试破解吗? 【参考方案1】:我希望这可以帮助您找到最佳解决方案/方法。
个人更喜欢使用具有一定优先级和间隔的 GoogleApiClient 和 LocationRequest。 编写实现以下接口的服务:
-
GoogleApiClient.ConnectionCallbacks
GoogleApiClient.OnConnectionFailedListener
位置监听器
public class PositionService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener
使用 GoogleApiClient 和 LocationRequest 类。
进入 onCreate() 实例化一个 GoogleApiClient 对象、一个 LocationRequest 对象并让 mGoogleApiClient 连接。
public void onCreate()
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mLocationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY)
.setInterval(mInterval).setFastestInterval(mFastInterval);
mGoogleApiClient.connect();
进入 onDestroy() 方法使 mGoogleApiClient 断开连接
@Override
public void onDestroy()
mGoogleApiClient.disconnect();
现在实现接口
@Override
public void onLocationChanged(Location location)
Log.d("NewLocation", location.toString());
@Override
public void onConnected(@Nullable Bundle bundle) throws SecurityException
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
@Override
public void onConnectionSuspended(int i)
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult)
现在,GoogleApiClient 会根据 LocationRequest 的设置,通知您调用 onLocationChanged() 回调的位置
您的业务逻辑应该放在 onLocationChanged() 方法中。只需为 LocationRequest 选择一个好的间隔时间和优先级。 (见documentation)
请参考location strategies的官方文档,我的解决方案就是以此为基础的。
我习惯于在前台启动服务以防止操作系统的意外行为(例如服务被终止)
【讨论】:
Matt - 感谢您的回答,但是当您可以将 fusedlocationAPI 与用于该目的的pendingintent 一起使用时,为什么您需要创建一个单独的服务。后台服务消耗更多功率,并且必须在 fusedlocation 自行管理的地方显式处理。我想知道您对给出解决方案的建议的思考过程和原因。 我参考了官方文档developer.android.com/guide/topics/location/strategies.html,它建议使用LocationListener(或实现相应的接口)。我创建一个单独的服务没有具体原因。【参考方案2】:这只会更好地解释你的逻辑
而不是长时间运行的服务或 IntentService 只需使用 Firebase JobDispatcher 或任何 3rd Party lib Jobscheduler API,以便将所有位置更新代码移动到 Jobscheduler (https://github.com/googlesamples/android-JobScheduler/blob/master/Application/src/main/java/com/example/android/jobscheduler/service/MyJobService.java)
根据您的位置更新间隔启动作业,根据您的要求配置或更改作业!与长时间运行的服务相比,它确实是一个更好的解决方案!!!(您可以使用 eventBus 或 RxBus 在 Activity 或 Fragment 中进行位置更新!!)
提示:每次 Job 在 Job 关闭之前启动位置更新时,都会设置一些 3 秒或更长时间的系统延迟,因为有时 Googleapiclient 在延迟后需要更多时间来更新新更新的 GPS 时间您可以使用正在运行的 JobService 关闭 Googleapiclient 所有不需要的回调。通过检测用户活动,使用 Google Awareness Api 或 Google Fit API 智能地控制作业配置!
多合一作业作业调度程序库:https://github.com/evernote/android-job
P.S:代码会很快更新
【讨论】:
【参考方案3】:文档states
在以下情况下,活动应强烈考虑删除所有位置请求 进入后台(例如在 onPause() 处),或至少交换 请求更大的间隔和更低的质量。
因此,当我遇到类似问题时,我所做的是:
-
我创建了两个位置请求,第一个的优先级为 PRIORITY_HIGH_ACCURACY,间隔为 1 分钟,而第二个的优先级为 PRIORITY_LOW_POWER,内部为 1小时,最小位移1公里
应用启动时,我使用第一个位置请求(高优先级)来获得更频繁、更准确的位置更新
当应用进入后台时,我切换到第二个位置请求(低优先级)以消除电池使用量,同时减少位置更新频率
(可选)您还可以在应用启动时获取电池百分比,并根据限制(例如 15%)选择应用在前台时可能要使用的位置请求
这些步骤帮助我将应用的电池使用量从 >30% 降低到
【讨论】:
以上是关于如何使用 FUSED LOCATION API 优化电池 - Android的主要内容,如果未能解决你的问题,请参考以下文章
使用 Location Manager 和 Fused Location Provider Api 获取位置有啥区别?
Android Fused Location API - 如何在一个应用程序中使用 PRIORITY_BALANCED_POWER_ACCURACY 和 PRIORITY_HIGH_ACCURACY?
Fused Location API 所需的最低播放服务版本
Google Fused Location API 导致错误(Android)