我创建了一个前台服务,每 1 分钟获取一次位置,但不知何故,该服务在一分钟后就不再获取位置了。
我正在奥利奥前台服务之前创建一项服务,在服务中的奥利奥前台服务之后我正在尝试注册位置更新,但问题是在杀死应用程序后仅一分钟我就得到了位置更新,之后我无法获得其他服务比前台服务通知。
MainActivity.java
package com.practice.satya.foreground;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService();
}
public void startService() {
if(Build.VERSION.SDK_INT >25){
startForegroundService(new Intent(this, LocationFetchInForeground.class));
}else{
startService(new Intent(this, LocationFetchInForeground.class));
}
}
}
LocationUpdatesReceiver.java
package com.practice.satya.foreground;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.support.v4.app.NotificationCompat;
public class LocationUpdatesReceiver extends BroadcastReceiver {
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onReceive(Context context, Intent intent) {
showNotification(context,"Location Changed","Updated location",intent);
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public void showNotification(Context context, String title, String body, Intent intent) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
int notificationId = 93;
String channelId = "testing";
String channelName = "testing chanel";
int importance = NotificationManager.IMPORTANCE_HIGH;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel mChannel = new NotificationChannel(
channelId, channelName, importance);
notificationManager.createNotificationChannel(mChannel);
}
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context, channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(title)
.setContentText(body);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addNextIntent(intent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
notificationManager.notify(notificationId, mBuilder.build());
}
}
LocationFetchInForeground.java
package com.practice.satya.foreground;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.app.TaskStackBuilder;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
import android.location.Location;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.RequiresApi;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.Toast;
public class LocationFetchInForeground extends Service {
private SensorManager sensorManager;
private Sensor sensor;
private TriggerEventListener triggerEventListener;
/** indicates how to behave if the service is killed */
int mStartMode;
/** interface for clients that bind */
IBinder mBinder;
/** indicates whether onRebind should be used */
boolean mAllowRebind;
private static final String TAG = "MyLocationService";
private LocationManager mLocationManager = null;
private static final int LOCATION_INTERVAL = 1000;
private static final float LOCATION_DISTANCE = 0f;
private class LocationListener implements android.location.LocationListener {
Location mLastLocation;
public LocationListener(String provider) {
Log.e(TAG, "LocationListener " + provider);
mLastLocation = new Location(provider);
}
@Override
public void onLocationChanged(Location location) {
Log.e(TAG, "onLocationChanged: " + location);
Toast.makeText(getApplicationContext(),"onLocationUpdated", Toast.LENGTH_LONG).show();
Intent intent=new Intent();
showNotification(getApplicationContext(),"Location Changed","Updated location",intent);
mLastLocation.set(location);
}
@Override
public void onProviderDisabled(String provider) {
Log.e(TAG, "onProviderDisabled: " + provider);
}
@Override
public void onProviderEnabled(String provider) {
Log.e(TAG, "onProviderEnabled: " + provider);
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
Log.e(TAG, "onStatusChanged: " + provider);
}
}
/*
LocationListener[] mLocationListeners = new LocationListener[]{
new LocationListener(LocationManager.GPS_PROVIDER),
new LocationListener(LocationManager.NETWORK_PROVIDER)
};
*/
LocationListener[] mLocationListeners = new LocationListener[]{
new LocationListener(LocationManager.GPS_PROVIDER)
};
/** Called when the service is being created. */
@Override
public void onCreate() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//For creating the Foreground Service
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? getNotificationChannel(notificationManager) : "";
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId);
Notification notification = notificationBuilder.setOngoing(true)
.setContentTitle("Location service")
.setContentText("This service is used to fetch location in background")
.setSmallIcon(R.mipmap.ic_launcher)
// .setPriority(PRIORITY_MIN)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.build();
startForeground(1, notification);
}
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
triggerEventListener = new TriggerEventListener() {
@Override
public void onTrigger(TriggerEvent event) {
// Do work
Toast.makeText(getApplicationContext(), "Location Updated", Toast.LENGTH_LONG).show();
Intent intent = new Intent();
showNotification(getApplicationContext(), "Motion Changed", "Updated location", intent);
}
};
if (sensor != null) {
sensorManager.requestTriggerSensor(triggerEventListener, sensor);
}
Log.e(TAG, "onCreate");
initializeLocationManager();
}
/** The service is starting, due to a call to startService() */
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Let it continue running until it is stopped.
Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
try {
// Intent intenter = new Intent(this,LocationUpdatesReceiver.class);
// PendingIntent proximityIntent = PendingIntent.getBroadcast(this, 0, intenter, PendingIntent.FLAG_UPDATE_CURRENT);
// mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
// LOCATION_INTERVAL,
// LOCATION_DISTANCE,
// proximityIntent);
Intent intenter2 = new Intent(this,LocationUpdatesReceiver.class);
PendingIntent proximityIntent2 = PendingIntent.getBroadcast(this, 0, intenter2, PendingIntent.FLAG_UPDATE_CURRENT);
mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
LOCATION_INTERVAL,
LOCATION_DISTANCE,
proximityIntent2);
//
// mLocationManager.requestLocationUpdates(
// LocationManager.GPS_PROVIDER,
// LOCATION_INTERVAL,
// LOCATION_DISTANCE,
// mLocationListeners[0]
// );
} catch (java.lang.SecurityException ex) {
Log.i(TAG, "fail to request location update, ignore", ex);
} catch (IllegalArgumentException ex) {
Log.d(TAG, "network provider does not exist, " + ex.getMessage());
}
return START_STICKY;
}
/** A client is binding to the service with bindService() */
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/** Called when all clients have unbound with unbindService() */
@Override
public boolean onUnbind(Intent intent) {
return mAllowRebind;
}
/** Called when a client is binding to the service with bindService()*/
@Override
public void onRebind(Intent intent) {
}
/** Called when The service is no longer used and is being destroyed */
@Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();
Log.e(TAG, "onDestroy");
super.onDestroy();
if (mLocationManager != null) {
for (int i = 0; i < mLocationListeners.length; i++) {
try {
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
mLocationManager.removeUpdates(mLocationListeners[i]);
} catch (Exception ex) {
Log.i(TAG, "fail to remove location listener, ignore", ex);
}
}
}
}
private void initializeLocationManager() {
Log.e(TAG, "initializeLocationManager - LOCATION_INTERVAL: "+ LOCATION_INTERVAL + " LOCATION_DISTANCE: " + LOCATION_DISTANCE);
if (mLocationManager == null) {
mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
}
}
@RequiresApi(Build.VERSION_CODES.O)
private String getNotificationChannel(NotificationManager notificationManager){
String channelId = "ForegroundLocationFetch";
String channelName = getResources().getString(R.string.app_name);
NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
channel.setImportance(NotificationManager.IMPORTANCE_NONE);
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
notificationManager.createNotificationChannel(channel);
return channelId;
}
public void showNotification(Context context, String title, String body, Intent intent) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
int notificationId = 234;
String channelId = "testing";
String channelName = "testing chanel";
int importance = NotificationManager.IMPORTANCE_HIGH;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel mChannel = new NotificationChannel(
channelId, channelName, importance);
notificationManager.createNotificationChannel(mChannel);
}
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context, channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(title)
.setContentText(body);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addNextIntent(intent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
notificationManager.notify(notificationId, mBuilder.build());
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.practice.satya.foreground">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-feature android:name="android.hardware.location.network"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".LocationFetchInForeground" />
<receiver android:name=".LocationUpdatesReceiver"/>
</application>
</manifest>
如果您的代码工作正常,有时 Android 会因为省电而限制位置更新,因此请确保您的手机已充电 超过 30%,您的位置处于“高精度”状态:
package de.conlance.kotlinclean.stackoverflow
import android.annotation.SuppressLint
import android.app.Notification.EXTRA_NOTIFICATION_ID
import android.content.Intent
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import android.app.*
import android.content.Context
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Build
import android.os.Bundle
import de.conlance.kotlinclean.R
class ForGroundService : Service() {
companion object {
val CHANNEL_ID = "1956"
val notificationId = 252
val ACTION_SNOOZE = "de.conlance.kotlinclean.stopservice"
var cnt = 0
}
var locationListener: LocationListener? = null
var locationManager: LocationManager? = null
override fun onBind(intent: Intent?): IBinder? = null
override fun onCreate() {
// Start up the thread running the service
createCh()
startForeground(notificationId, NotificationCompat.Builder(this, CHANNEL_ID).build())
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
// If we get killed, after returning from here, restart
//Main Thread
showNotification("Service is Running")
doWork()
return START_NOT_STICKY
}
fun doWork() {
getLocation() {
showNotification(it)
}
}
override fun onDestroy() {
Log.v("ForService", "Service Is Dead")
RemoveUpdates()
showNotification("Service is Destroyed restart app .")
stopForeground(true)
}
//Create Notification Channel ID .
@SuppressLint("InlinedApi")
private fun showNotification(msg: String) {
val stopintent = Intent(this, MyBroadcastReceiver::class.java).apply {
ACTION_SNOOZE
putExtra(EXTRA_NOTIFICATION_ID, 0)
}
val stoppendingintent: PendingIntent =
PendingIntent.getBroadcast(this, 0, stopintent, 0)
val builder = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("My notification")
.setContentText(msg)
.setStyle(
NotificationCompat.BigTextStyle()
.bigText(msg)
)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.addAction(
R.mipmap.ic_launcher_round, "StopService",
stoppendingintent
)
with(NotificationManagerCompat.from(this)) {
// notificationId is a unique int for each notification that you must define
notify(notificationId, builder.build())
}
}
fun Context.getLocation(onResult: (String) -> Unit = {}) {
val context = this
locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager?
locationListener = object : LocationListener {
override fun onLocationChanged(location: Location?) {
location?.let {
onResult("cntrefresh {${++cnt}} lat-> ${it.latitude} , lng ${it.longitude} ")
//send Location To Activity and then send it to Parse Location Function......
}
}
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {
// Timber.tag(MyTag).v("on Status Changed $provider $status")
}
override fun onProviderEnabled(provider: String?) {
// Timber.tag(MyTag).v("on Provider Enabled $provider")
}
override fun onProviderDisabled(provider: String?) {
// Timber.tag(MyTag).v("on Provider Disabled $provider")
}
}// location Listner
try {
locationManager?.let {
if (it.isProviderEnabled(LocationManager.GPS_PROVIDER) && it.isProviderEnabled(LocationManager.NETWORK_PROVIDER))
it.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000, 0f, locationListener)
else {
//fail.value = Failure.GPSError
onResult("Gps Error")
}
}
} catch (ex: SecurityException) {
// fail.value = Failure.SecurityError
onResult("Permission")
}
}
private fun RemoveUpdates() {
locationListener?.let {
locationManager?.removeUpdates(it)
}
}
fun createCh() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create the NotificationChannel
val name = "randname"
val descriptionText = "dumdum"
val importance = NotificationManager.IMPORTANCE_DEFAULT
val mChannel = NotificationChannel(CHANNEL_ID, name, importance)
mChannel.description = descriptionText
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(mChannel)
}
}
}
根据我使用 Expo Go 的 React Native 项目的经验,我注意到了类似的问题。使用 Android 手机时,无论我构建应用程序还是使用 Expo Go 启动它,我只会在主动移动时收到位置更新。此行为不会发生在 iOS 上。
似乎在Android上,除非您在运动,否则更新位置的后台任务不会触发。它会在更新位置之前等待移动。
希望这有帮助!