如何使用 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)

使用 android fused location api 在 MyLocation 上未显示蓝点

为 Fused location API 添加 addGpsStatusListener