Как всегда запустить сервис в фоновом режиме?
Я нахожусь в процессе создания приложения, похожего на встроенное приложение SMS.
Что мне нужно:
- сервис, который всегда работает в фоновом режиме
- каждые 5 мин. служба проверяет текущее местоположение устройства и вызывает веб-службу
- при соблюдении определенных критериев служба должна генерировать уведомление (как приложение SMS)
- при нажатии на уведомление пользователь попадает в приложение (так же, как приложение SMS)
- когда приложение установлено, служба должна быть запущена
- при перезагрузке устройства служба должна быть запущена
Что я пробовал:
- запуск обычного сервиса, который работал нормально, пока Android не убил сервис
- с помощью AlarmManager сделать 5 мин. интервальный звонок в сервис. Но я не смог сделать эту работу.
4 ответа
сервис, который всегда работает в фоновом режиме
Как вы обнаружили, это невозможно в каком-либо реальном смысле этого слова. Это тоже плохой дизайн.
каждые 5 мин. служба проверяет текущее местоположение устройства и вызывает веб-службу
использование AlarmManager
,
с помощью AlarmManager сделать 5 мин. интервальный звонок в сервис. Но я не смог сделать эту работу.
Вот пример проекта, показывающий, как использовать один, наряду с использованием WakefulIntentService
так что вы бодрствуете, пытаясь сделать весь веб-сервис.
Если у вас продолжаются проблемы с этим, откройте новый вопрос о конкретных вещах, с которыми вы сталкиваетесь AlarmManager
которые дают вам горе.
Одно из моих приложений делает что-то очень похожее. Чтобы разбудить службу после определенного периода, я рекомендую postDelayed()
Есть поле обработчика:
private final Handler handler = new Handler();
и переподготовка Runnable
private final Runnable refresher = new Runnable() {
public void run() {
// some action
}
};
Вы можете запускать свои уведомления в рабочем режиме.
По построению сервиса и после каждого запуска запуска это так:
handler.postDelayed(refresher, /* some delay in ms*/);
В onDestroy()
удалить пост
handler.removeCallbacks(refresher);
Для запуска сервиса во время загрузки вам понадобится автозапуск. Это идет в вашем манифесте
<receiver android:name="com.example.ServiceAutoStarter">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
и ServiceAutoStarter
выглядит так:
public class ServiceAutoStarter extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context, UpdateService.class));
}
}
Остановить ОС от уничтожения сервиса сложно. Также ваше приложение может иметь RuntimeException
и сбой, или ваша логика может заглохнуть.
В моем случае это помогло всегда обновлять сервис на экране с помощью BroadcastReceiver
, Таким образом, если цепочка обновлений будет остановлена, она будет воскрешена, когда пользователь использует свой телефон.
В сервисе:
private BroadcastReceiver screenOnReceiver;
К вашим услугам onCreate()
screenOnReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Some action
}
};
registerReceiver(screenOnReceiver, new IntentFilter(Intent.ACTION_SCREEN_ON));
Затем отмените регистрацию вашего сервиса на onDestroy()
с
unregisterReceiver(screenOnReceiver);
Вы можете сделать это с помощью простой реализации:
public class LocationTrace extends Service implements LocationListener{
// The minimum distance to change Updates in meters
private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters
private static final int TWO_MINUTES = 100 * 10;
// The minimum time between updates in milliseconds
private static final long MIN_TIME_BW_UPDATES = 1000 * 10; // 30 seconds
private Context context;
double latitude;
double longitude;
Location location = null;
boolean isGPSEnabled = false;
boolean isNetworkEnabled = false;
protected LocationManager locationManager;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
this.context = this;
get_current_location();
// Toast.makeText(context, "Lat"+latitude+"long"+longitude,Toast.LENGTH_SHORT).show();
return START_STICKY;
}
@Override
public void onLocationChanged(Location location) {
if((location != null) && (location.getLatitude() != 0) && (location.getLongitude() != 0)){
latitude = location.getLatitude();
longitude = location.getLongitude();
if (!Utils.getuserid(context).equalsIgnoreCase("")) {
Double[] arr = { location.getLatitude(), location.getLongitude() };
// DO ASYNCTASK
}
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
/*
* Get Current Location
*/
public Location get_current_location(){
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if(!isGPSEnabled && !isNetworkEnabled){
}else{
if (isGPSEnabled) {
if (location == null) {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
MIN_TIME_BW_UPDATES,
MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
if (locationManager != null) {
location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (location != null) {
latitude = location.getLatitude();
longitude = location.getLongitude();
// Toast.makeText(context, "Latgps"+latitude+"long"+longitude,Toast.LENGTH_SHORT).show();
}
}
}
}
if (isNetworkEnabled) {
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
MIN_TIME_BW_UPDATES,
MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
if (locationManager != null) {
if (location != null) {
latitude = location.getLatitude();
longitude = location.getLongitude();
// Toast.makeText(context, "Latgps1"+latitude+"long"+longitude,Toast.LENGTH_SHORT).show();
}
}
}
}
return location;
}
public double getLatitude() {
if(location != null){
latitude = location.getLatitude();
}
return latitude;
}
public double getLongitude() {
if(location != null){
longitude = location.getLongitude();
}
return longitude;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
if(locationManager != null){
locationManager.removeUpdates(this);
}
super.onDestroy();
}
}
Вы можете начать обслуживание следующим образом:
/*--Start Service--*/
startService(new Intent(Splash.this, LocationTrace.class));
В декларации:
<service android:name=".LocationTrace">
<intent-filter android:priority="1000">
<action android:name="android.location.PROVIDERS_CHANGED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
Этими тремя шагами вы можете каждые 5 минут разбудить большинство устройств Android:
1. установите альтернативный AlarmManager для разных API:
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(getApplicationContext(), OwnReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(getApplicationContext(), 0, i, 0);
if (Build.VERSION.SDK_INT >= 23) {
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
}
else if (Build.VERSION.SDK_INT >= 19) {
am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
} else {
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
}
2. создайте свой собственный статический BroadcastReceiver:
public static class OwnReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//do all of your jobs here
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, OwnReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
if (Build.VERSION.SDK_INT >= 23) {
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
}
else if (Build.VERSION.SDK_INT >= 19) {
am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
} else {
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
}
}
}
3. добавить <receiver>
в AndroidManifest.xml
:
<receiver android:name=".OwnReceiver" />
По моему мнению, когда вы хотите, чтобы ваша служба запускалась, это всегда означает, что она не должна останавливаться, когда приложение убито, потому что, если ваше приложение работает или находится в фоновом режиме, ваша служба будет работать. Когда вы убиваете приложение во время работы службы, запускается функция onTaskRemoved.
@Override
public void onTaskRemoved(Intent rootIntent) {
Log.d(TAG, "onTaskRemoved: removed");
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis() + 10000);
((AlarmManager) getSystemService(Context.ALARM_SERVICE)).setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), PendingIntent.getService(getApplicationContext(), 0, new Intent(getApplicationContext(), RegisterReceiverService.class), 0));
super.onTaskRemoved(rootIntent);
}
Таким образом, как только вы убьете приложение, служба запустится через 10 секунд. Примечание: если вы хотите использовать
AlarmManager.ELAPSED_REALTIME_WAKEUP
минимальное время, по истечении которого вы сможете перезапустить службу, составляет 15 минут.
Помните, что вам также необходимо запустить службу при перезагрузке с помощью BroadcastReceiver.