应用关闭后保持位置服务运行

10

我有一个服务,在用户更改其位置时发送通知。这个服务运作良好,但当用户关闭应用程序时,问题就出现了,因为服务也会关闭。

我该如何让服务在应用程序关闭后仍然保持活动状态?

我的服务是:

public class LocationService extends Service implements LocationListener {
    public final static int MINUTE = 1000 * 60;


    boolean isGPSEnabled = false;
    boolean isNetworkEnabled = false;
    boolean canGetLocation = false;

    Location location; // location
    double latitude = 0; // latitude
    double longitude = 0; // longitude
    String provider;

    // The minimum distance to change Updates in meters
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10;

    // The minimum time between updates in milliseconds
    private static final long MIN_TIME_BW_UPDATES = 1 * MINUTE;

    // Declaring a Location Manager
    protected LocationManager locationManager;



    // Binder given to clients
    private final IBinder mBinder = new LocalBinder();

    /**
     * Class used for the client Binder. Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
     */
    public class LocalBinder extends Binder {
        public LocationService getService() {
            // Return this instance of LocalService so clients can call public
            // methods
            return LocationService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    public Location getLocation() {
        try {
            locationManager = (LocationManager) getBaseContext().getSystemService(LOCATION_SERVICE);

            // getting GPS status
            isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);

            // getting network status
            isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

            if (!isGPSEnabled && !isNetworkEnabled) {
                // no network provider is enabled. DEFAULT COORDINATES


            } else {
                this.canGetLocation = true;
                if (isNetworkEnabled) {
                    locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES,
                            this);
                    Log.d("Network", "Network Enabled");
                    if (locationManager != null) {
                        location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                        if (location != null) {
                            latitude = location.getLatitude();
                            longitude = location.getLongitude();
                        }
                    }
                }
                // if GPS Enabled get lat/long using GPS Services
                if (isGPSEnabled) {
                    if (location == null) {
                        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES,
                                this);
                        Log.d("GPS", "GPS Enabled");
                        if (locationManager != null) {
                            location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
                            if (location != null) {
                                latitude = location.getLatitude();
                                longitude = location.getLongitude();
                            }
                        }
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        Log.i("LOCATION", "Latitude: " + latitude + "- Longitude: " + longitude);


        return location;
    }

    @Override
    public void onLocationChanged(Location arg0) {

        NotificationManager mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
        Intent intent = null;

        intent = new Intent(this, CompleteSurveyActivity.class);

        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_launcher).setAutoCancel(true)
                .setContentIntent(contentIntent).setContentTitle(this.getString(R.string.app_name)).setContentText("text");

        // mBuilder.setContentIntent(contentIntent);
        mNotificationManager.notify((int) System.currentTimeMillis() % Integer.MAX_VALUE, mBuilder.build());

        double longitude = location.getLongitude();
        double latitude = location.getLatitude();

        Log.i("LOCATION", "Latitude: " + latitude + "- Longitude: " + longitude);

    }

    @Override
    public void onProviderDisabled(String arg0) {
    }

    @Override
    public void onProviderEnabled(String arg0) {
    }

    @Override
    public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
    }

}

我从这里打电话:

public class MyActivity extends Activity {

    LocationService mService;
    boolean mBound = false;


    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            // We've bound to LocalService, cast the IBinder and get
            // LocalService instance
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;

        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_activity);

        exampleButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                initService();
            }
        });

    }

    public void initService() {
        if (mBound)
            mService.getLocation();
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to LocalService
        Intent intent = new Intent(this, LocationService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}

Manifest.xml

  <service android:name=".LocationService" android:enabled="true"></service>

你能提供我们无限服务的新代码吗?@sany - Mina Farid
3个回答

11

与@sven-menschner所说的相反,我认为一个无限制的服务正是您需要的,因为绑定服务受到绑定/解除绑定机制的限制,这可能会导致您的服务被杀死。这就是我会做的事情:

在您的清单文件中,定义您的服务:

<service
  android:name=".YourService"
  android:enabled="true"
  android:exported="true"
  android:description="@string/my_service_desc"
  android:label="@string/my_infinite_service">
  <intent-filter>
    <action android:name="com.yourproject.name.LONGRUNSERVICE" />
  </intent-filter>
</service>
注意:已经有一些实现的操作列表,但你可以为启动服务定义自己的操作。只需创建一个单例类并定义字符串,将它们分配一个唯一的String即可。 "enabled"设置为true只是为了实例化服务,而导出设置为true只是在您需要其他应用程序发送意图到您的Service的情况下。如果不需要,可以安全地将最后一个设置为false。

接下来的步骤是从活动中启动您的服务。这可以很容易地完成:

public class MainActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Intent servIntent = new Intent("com.yourproject.name.LONGRUNSERVICE");
    startService(servIntent);

    ...
  }
}

最后一步是定义您的Service初始化。关注onBind()方法。由于您不希望它被绑定,只需返回null。代码如下:
public class MyService extends Service {
  @Override
  public IBinder onBind(Intent intent) {
    // This won't be a bound service, so simply return null
    return null;
  }

  @Override
  public void onCreate() {
    // This will be called when your Service is created for the first time
    // Just do any operations you need in this method.
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    return super.onStartCommand(intent, flags, startId);
  }
}

现在,即使您关闭主Activity,您的服务也将运行。只剩最后一步:为了不让您的Service被结束,将其作为前台服务运行(请在您的Service中执行此操作)。这基本上会在状态栏中创建一个通知图标。这并不意味着您的主Activity也在运行(这就是为什么您不想要绑定服务),因为活动和服务具有不同的生命周期。为了帮助该服务长时间运行,请尝试尽可能保持堆栈较低,以避免Android系统杀死它。

还有一个声明:您无法通过杀死DVM来测试服务是否仍在运行。如果您杀死DVM,则会杀死一切,包括服务。


哦不!我想说的是正确的,但写错了。当然,启动(或未绑定)服务是正确的选择。只是在第一句话中弄错了顺序... 但从我的回答中可以理解我的本意 ;) - Sven Menschner
我使用前台服务和闹钟管理器来设置间隔并获取最新位置。问题: 当我关闭或杀死应用程序时,定位服务会停止运行。经过两天的努力,终于找到了问题所在——我的应用程序针对的是SDK 30或Android 11。解决方法:需要在AndroidManifest文件中添加ACCESS_BACKGROUND_LOCATION权限,并需要进入应用程序设置 -> 权限 -> 位置 ->“始终允许”。希望能帮助那些面临相同问题的人。^^ - Jacky Teaw

2
有两种Android服务:启动和绑定。你需要使用第一种(启动服务)。 文档展示了如何使用它,下面有一个很好的生命周期图表。
不要使用bindService()一步启动并绑定服务,而是需要先调用startService()。但从Oreo开始,startService()不能帮助您启动服务,您需要使用startForegroundService()。然后,即使应用程序关闭,服务仍将运行,直到您停止它。
如果希望Android OS在杀死服务后再次选择您的服务,则启动Sticky Service

0

API> 29 已经更改了,请添加以下权限以获取用户位置:

android:name="android.permission.ACCESS_FINE_LOCATION" 
android:name="android.permission.ACCESS_COARSE_LOCATION"   
android:name="android.permission.ACCESS_BACKGROUND_LOCATION" 

ACCESS_BACKGROUND_LOCATION 应该在请求ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION之后进行。

然后,您可以使用任何方式获取用户的位置,例如:

const val LOCATION_UPDATE_INTERVAL = 5000L
const val FASTEST_LOCATION_INTERVAL = 2000L
lateinit var fusedLocationProviderClient: FusedLocationProviderClient

fusedLocationProviderClient = FusedLocationProviderClient(this)
val request = LocationRequest().apply {
                interval = LOCATION_UPDATE_INTERVAL
                fastestInterval = FASTEST_LOCATION_INTERVAL
                priority = PRIORITY_HIGH_ACCURACY
            }

fusedLocationProviderClient.requestLocationUpdates(
                request,
                locationCallback,
                Looper.getMainLooper()
            )

val locationCallback = object : LocationCallback() {
    override fun onLocationResult(result: LocationResult?) {
        super.onLocationResult(result)
        if(isTracking.value!!) {
            result?.locations?.let { locations ->
                for(location in locations) {
                    Logd.d("NEW LOCATION: ${location.latitude}, 
                  ${location.longitude}")
                }
            }
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接