如何在后台运行 android 服务 24x7 以获取用户的位置

Posted

技术标签:

【中文标题】如何在后台运行 android 服务 24x7 以获取用户的位置【英文标题】:How to run service 24x7 in android in background to get location of user 【发布时间】:2019-01-23 03:36:57 【问题描述】:

我已经尝试了很多不同的方法来运行后台服务来获取用户的位置,但是 android 系统会在一段时间后自动终止服务。 虽然它适用于某些设备,但不适用于大多数设备。 我正在构建类似于 UBER 的应用程序。我希望在一段时间后更新驱动程序的位置,即使应用程序处于后台或前台,直到用户没有将状态设置为离线

【问题讨论】:

你试过前台服务吗? 【参考方案1】:

您可以使用Foreground Service,这只是一个普通服务,在前台有一个持续的通知,它会阻止操作系统停止/终止您的服务进程。

顺便说一句,这并不能保证您的服务会在设备进入打盹或应用待机模式时给予 CPU/处理时间。

虽然您无法绕过这些打瞌睡、待机和电池优化,但我已经测试了一种技巧来避免这些问题,方法是在前台服务中创建唤醒锁并在单独的进程中启动该服务。

希望对你有帮助。

【讨论】:

前台服务唤醒锁有什么好处?? 是的前台服务有效。我在前台服务中使用 FusedLocationProviderClientApi 来获取位置更新。但它只有在打开 gps 时才有效。如何要求用户从前台服务打开位置?? 如果我获得了唤醒锁并在单独的进程中启动服务,那么不要求用户为我的应用禁用电池优化是可以的。电池优化和打盹模式不会影响我的应用程序吗?您是否在 Android 30 上对此进行了测试?谢谢【参考方案2】:

您可以使用 Android 提供的一些机制。

    对于运行 pre-Oreo 的设备,您可以直接使用后台服务,它应该大部分时间都存在,通过在清单文件中声明它来将其保留在单独的进程中。您还可以注册到设备启动完成广播,这样您将在设备重启时收到回电,然后您将有机会重新启动后台服务。对于运行 oreo+ 的设备,最可靠的方法是使用前台服务。确保您的服务在任何情况下都具有粘性。 设置 fire base 调度作业以在服务停止时重新启动服务 获取更多反馈的地理围栏政策 安排警报管理器以在服务停止时重新启动您的服务 使用 google 活动识别 api 还可以获得回调,从而有更多机会获取更多位置信息 推送通知

我建议将所有这些与针对您的应用程序量身定制的策略结合使用,它们应该可以随时为您提供足够的用户位置信息。

【讨论】:

【参考方案3】:

是的,您可以这样做,但可能会消耗更多的电池电量。 找到下面的代码,它会帮助你,

还有另一种使用移动位置的方法,Google 发布了融合的位置提供程序,这对您的情况更有帮助。

1.在您的 Build.gradle 文件中添加 Google Location gradle 行

实现 'com.google.android.gms:play-services-location:15.0.1'

2.使用定位服务获取用户位置

import android.Manifest;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.NotificationCompat;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationAvailability;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.uffizio.taskeye.BuildConfig;
import com.uffizio.taskeye.R;
import com.uffizio.taskeye.extra.Constants;
import com.uffizio.taskeye.ui.activity.MainActivity;


/**
 * Created by Kintan on 16/8/18.
 */

public class LocationServiceDemo extends Service implements GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener 

    public static LocationServiceDemo locationService;
    private static GoogleApiClient mGoogleApiClient;
    private static LocationRequest mLocationRequest;
    private FusedLocationProviderClient mFusedProviderClient;
    private MyLocationCallback mMyLocationCallback;
    private Location curLocation;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) 
        return new MyBinder();
    

    @Override
    public void onCreate() 
        super.onCreate();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
            showNotificationAndStartForegroundService();

        locationService = this;
        init();
    

    //Google location Api build
    protected synchronized void buildGoogleApiClient() 
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
        mGoogleApiClient.connect();
    

    protected void createLocationRequest() 
        mMyLocationCallback = new MyLocationCallback();
        mLocationRequest = LocationRequest.create();
        mLocationRequest.setInterval(5000);
        mLocationRequest.setFastestInterval(3000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        mLocationRequest.setSmallestDisplacement(5.0f);

        requestUpdate();

    

    //Start Foreground Service and Show Notification to user for Android O and higher Version
    private void showNotificationAndStartForegroundService() 

        final String CHANNEL_ID = BuildConfig.APPLICATION_ID.concat("_notification_id");
        final int REQUEST_CODE = 1;

        PendingIntent pendingIntent = PendingIntent.getActivity(this,
                REQUEST_CODE, new Intent(this, MainActivity.class),
                PendingIntent.FLAG_UPDATE_CURRENT);

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this,
                CHANNEL_ID)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle(getString(R.string.app_name))
                .setAutoCancel(false)
                .setContentIntent(pendingIntent);

        startForeground(Constants.NOTIFICATION_ID, notificationBuilder.build());
    

    public void requestUpdate() 
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) 
            return;
        
        mFusedProviderClient.requestLocationUpdates(mLocationRequest, mMyLocationCallback,
                Looper.myLooper());
    

    public void removeUpdate() 
        mFusedProviderClient.removeLocationUpdates(mMyLocationCallback);
    

    @Override
    public void onConnected(@Nullable Bundle bundle) 
        createLocationRequest();
    

    @Override
    public void onConnectionSuspended(int i) 
        buildGoogleApiClient();
    

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) 

        buildGoogleApiClient();
    

    private void init() 
        buildGoogleApiClient();
    

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) 
        mFusedProviderClient = LocationServices.getFusedLocationProviderClient(LocationServiceDemo.this);
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED &&
                ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
                        != PackageManager.PERMISSION_GRANTED) 
            return START_STICKY;
        
        mFusedProviderClient.getLastLocation().addOnSuccessListener(location -> 
            if (location != null) 

                curLocation = location;

            
        );
        if (mGoogleApiClient.isConnected()) 
            createLocationRequest();
         else 
            buildGoogleApiClient();
        
        return START_STICKY;
    


    @Override
    public void onTaskRemoved(Intent rootIntent) 
        super.onTaskRemoved(rootIntent);
        startService();
    

    @Override
    public void onLowMemory() 
        super.onLowMemory();
        startService();
    

    @Override
    public void onDestroy() 
        super.onDestroy();
        startService();
    

    public void startService() 
        startService(new Intent(LocationServiceDemo.this, LocationServiceDemo.class));
    

    public class MyLocationCallback extends LocationCallback 
        @Override
        public void onLocationResult(LocationResult locationResult) 
            //get your location here
            if (locationResult.getLastLocation() != null) 
                for (Location location : locationResult.getLocations()) 
                    curLocation = location;
                
            
        

        @Override
        public void onLocationAvailability(LocationAvailability locationAvailability) 
            super.onLocationAvailability(locationAvailability);
        
    

    private class MyBinder extends Binder 
        LocationServiceDemo getService() 
            return LocationServiceDemo.this;
        
    

3.最终授予访问管理员权限,Android系统不会在一段时间后自动终止服务。

import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;


public class LocationHome extends AppCompatActivity 
    private static final int REQUEST_CODE = 0;
    private DevicePolicyManager mDPM;
    private ComponentName mAdminName;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
        mAdminName = new ComponentName(this, DeviceAdmin.class);

        Button button = new Button();
        button.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                //Grant Admin Permission
                if (mDPM.isAdminActive(mAdminName)) 
                    startService(new Intent(this, LocationService.class));
                 else 
                    adminPermission();
                
            
        );
    


    public void adminPermission() 
        try 
            if (!mDPM.isAdminActive(mAdminName)) 
                try 
                    Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
                    intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mAdminName);
                    intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "Click on Activate button to secure your application.");
                    startActivityForResult(intent, REQUEST_CODE);
                 catch (Exception e) 
                    e.printStackTrace();
                
            
         catch (Exception e) 
            e.printStackTrace();
        
    

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) 
        super.onActivityResult(requestCode, resultCode, data);
        //Check Permission is granted or not
        if (requestCode == REQUEST_CODE) 
            if (resultCode == RESULT_OK) 
                startService(new Intent(this, LocationService.class));
             else 
                if (!mDPM.isAdminActive(mAdminName)) 
                    adminPermission();
                
            
        
    



创建 XML 文件夹添加 device_admin.xml

<device-admin>
    <uses-policies>

    </uses-policies>
</device-admin>

最后修改您的清单并享受。

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-feature
        android:name="android.hardware.location.gps"
        android:required="false" />
<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:name=".common.MyApplication"
        android:theme="@style/AppTheme">

        <receiver
            android:name=".ui.DeviceAdmin"
            android:permission="android.permission.BIND_DEVICE_ADMIN">
            <meta-data
                android:name="android.app.device_admin"
                android:resource="@xml/device_admin" />
            <intent-filter>
                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
            </intent-filter>
        </receiver>
    </application>

【讨论】:

如何在后台启动? DeviceAdmin 中应该有什么

以上是关于如何在后台运行 android 服务 24x7 以获取用户的位置的主要内容,如果未能解决你的问题,请参考以下文章

如何让android的service一直在后台运行

如何在 Flutter 中创建服务以使应用始终在后台运行?

在 android 服务的单独线程中运行位置更新

如何创建在后台运行的Android服务

如何使用libGDX在iOS / Android后台运行应用程序

如何在 Android O 中保持服务在后台运行?