Android sticky service is beeing killed loosing all information - java

I have service which is basically timer. It receives object with location data, start time, user id etc, and every seconds it increments stop value. I'd like to have this service running non-stop until stopped in application. I've read that i need to start this service as sticky, so i did that. But I've noticed that after my main application is beeing killed by system or user, service restarts and lose all information (about current timers running etc - I have there array with list of objects).
Idea of that service was that:
user clicks button in app -> service is starting and counting time changing information in notification bar and sending broadcast to main application -> I wish to continue updating notification bar even if app is killed by user or system.
My class below:
public class TimerService extends Service{
private NotificationCompat.Builder builder;
private static JsonHistoryList activities;
private Intent intent;
private Handler handler;
private Runnable sendUpdateToUi = new Runnable() {
#Override
public void run() {
sendBroadcast();
handler.postDelayed(this, 1000);
}
};
private NotificationManager mNotificationManager;
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action.equals(ACTION_ADD)){
JsonHistory item = (JsonHistory) intent.getSerializableExtra(HISTORY_ARG);
new NetworkAsyncTask().execute(ACTION_ADD_ID, item);
}else if(action.equals(ACTION_DELETE)){
int id = intent.getIntExtra(CATEGORY_ID_ARG, -1);
if(id > 0){
new NetworkAsyncTask().execute(ACTION_DELETE_ID, id);
}
}else if(action.equals(ACTION_CLEAR)){
activities.clear();
}
}
};
public TimerService() {
handler = new Handler();
}
private void sendBroadcast() {
Long time = new Date().getTime();
for(JsonHistory item: activities){
ContentValues values = new ContentValues();
values.put(History.C_STOP, time);
String[] selectionArgs = {String.valueOf(item.id)};
getContentResolver().update(History.URI, values, History.C_ID + "=?", selectionArgs);
item.stop = time;
}
intent.putExtra(ACTIVITY_LIST_ARG, activities);
Intent bIntent = new Intent(this, MainActivity.class);
bIntent.putExtra(CATEGORY_ARG, activities);
builder.setContentText(String.format(getString(R.string.notification_bar_message), activities.size()));
Notification barNotif = builder.build();
mNotificationManager.notify(SERVICE_ID, barNotif);
sendBroadcast(intent);
}
#Override
public void onCreate() {
super.onCreate();
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_ADD);
filter.addAction(ACTION_DELETE);
filter.addAction(ACTION_CLEAR);
registerReceiver(receiver, filter);
intent = new Intent(ACTION);
handler.removeCallbacks(sendUpdateToUi);
handler.postDelayed(sendUpdateToUi, 1000);
activities = new JsonHistoryList();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if(intent != null && intent.hasExtra(TimerService.ACTIVITY_LIST_ARG)){
ArrayList<JsonHistory> temp = (ArrayList<JsonHistory>) intent.getSerializableExtra(TimerService.ACTIVITY_LIST_ARG);
if(temp != nu;; && temp.size() > 0)activities.addAll(temp);
}
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Intent bIntent = new Intent(this, MainActivity.class);
PendingIntent pbIntent = PendingIntent.getActivity(this, 0, bIntent, 0);
builder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle(getString(R.string.app_name))
.setContentText(String.format(getString(R.string.notification_bar_message), activities.size()))
.setAutoCancel(true)
.setOngoing(true)
.setContentIntent(pbIntent);
Notification barNotif = builder.build();
this.startForeground(SERVICE_ID, barNotif);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
handler.removeCallbacks(sendUpdateToUi);
stopForeground(true);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private class NetworkAsyncTask extends AsyncTask<Object, Void, String>{
Gson gson;
public NetworkAsyncTask() {
super();
gson = new Gson();
}
#Override
protected String doInBackground(Object... params) {
Integer actionId = (Integer) params[0];
String result = null;
switch (actionId){
case ACTION_ADD_ID:
break;
case ACTION_DELETE_ID:
break;
default:
result = null;
break;
}
return result;
}
}
}
Thanks for any replies

When you stop the service it is true it loses its information if the application is destroyed. That is because that instance of the service is tied to that particular instance of the application.
In my opinion, an easy fix would be to store the information you still need in some type of persistent storage (SQLite, Internal/External Memory, Shared Preferences)
Then, every time you start your service with a new application launch just be sure to reload the information you want from whichever persistent storage method you choose.
Also, assuming you don't want this service to continue if the application is killed by the user, be sure to send an intent to the service to stop it like this:
intent = new Intent(this, BackgroundLocationService.class);
stopService(intent);

Related

unable to start activity from service class

Please be informed, we are trying to start activity from service class, which is fired on clicking push notification addaction intent. The service class contains two actions, one to stop playing ringtone and another to startactivity. But unfortunately the start activity just does not boot in our service class.
Service Class page is as given below:
public class RingtonePlayingService extends Service {
private static final String TAG = RingtonePlayingService.class.getSimpleName();
private static final String URI_BASE = RingtonePlayingService.class.getName() + ".";
public static final String ACTION_DISMISS = URI_BASE + "ACTION_DISMISS";
public static final String ACTION_START = URI_BASE + "ACTION_START";
private MediaPlayer mp;
private Ringtone ringtone;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
int notificationId = intent.getIntExtra("notificationId", 0);
if (intent == null) {
Log.d(TAG, "The intent is null.");
return START_REDELIVER_INTENT;
}
String action = intent.getAction();
if (ACTION_START.equals(action)) {
String uri = String.valueOf(intent.getIntExtra("uri", 0));
Intent intents = new Intent(this, MainActivity.class);
intents.putExtra("uri", uri);
intents.putExtra("notification_id", notificationId);
intents.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
this.startActivity(intents);
}
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
mp.reset ();
}}
Firebase (from where service class's intent is fired)
Intent startIntent = new Intent(this, RingtonePlayingService.class);
startIntent.setAction(RingtonePlayingService.ACTION_START);
startIntent.putExtra("uri", uri);
startIntent.putExtra("notification_id", notification_id);
PendingIntent pt = PendingIntent.getService(this,123, startIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Action action_n = new NotificationCompat.Action.Builder(R.mipmap.ic_launcher, tag, pt).build();
The objective here is on firing of pending Intent 'pt'. We want to start the activity (that opens the app's url) as well as run the service class (which stops the service and the ringtone).
Please help us find a solution on this never ending issue.
The service class contains two actions, one to stop playing ringtone and another to startactivity. But unfortunately the start activity just does not boot in our service class.
You cannot start an activity from the background on modern versions of Android.
Firebase (from where service class's intent is fired)
That code has problems:
Your Intent is for RingtonePlayingService, which according to your first code snippet is a Service. Yet, you try using it with PendingIntent.getActivity(), rather than PendingIntent.getService().
You are using addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK), which are irrelevant for a Service.

How to know os kill my foreground service android

I made a lock screen app. I want to restart my service when the OS kills the service in Xiaomi Redmi Note 10 Pro (MIUI 12). When the service is killed, onDestroy is not call.
public class LockScreenService extends Service {
SharedPreferences prefs;
private BroadcastReceiver screenStateReceiver;
public static boolean isScreenReceiverRegistered=false;
public IBinder onBind(Intent paramIntent) {
return null;
}
public void onCreate() {
super.onCreate();
prefs = getSharedPreferences("SettingPreference", Context.MODE_PRIVATE);
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.setPriority(999);
screenStateReceiver = new ScreenStateReceiver();
registerReceiver(screenStateReceiver, filter);
isScreenReceiverRegistered = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
String channelId = createNotificationChannel(notificationManager);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId);
Notification notification = notificationBuilder.setOngoing(true)
.setSmallIcon(R.drawable.icon_notification)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.build();
startForeground(127, notification);
}
}
#RequiresApi(Build.VERSION_CODES.O)
private String createNotificationChannel(NotificationManager notificationManager){
String channelId = "my_service_channelid";
String channelName = "Lock Screen Running";
NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
// omitted the LED color
channel.setImportance(NotificationManager.IMPORTANCE_NONE);
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
notificationManager.createNotificationChannel(channel);
return channelId;
}
#Override
public int onStartCommand(final Intent intent, final int flags,
final int startId) {
return START_STICKY;
}
and on onDestroy() function I restart my service.
Manifests
<service android:name=".LockScreenService"
android:process=":ServiceProcess"
android:enabled="true"
android:exported="false"/>
try this, if you want to get it in onResume()
#Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume: GamePreferences.getPid()--------> " + GamePreferences.getPid());
Log.d(TAG, "onResume: android.os.Process.myPid()--------> " + android.os.Process.myPid());
if (GamePreferences.getPid() != 0) {
if (GamePreferences.getPid() != android.os.Process.myPid()) {
Log.d(TAG, "GamePreferences.getPid() != android.os.Process.myPid(): --------> " + android.os.Process.myPid());
//restart your service in foreground
return;
}
}
}
According to the documentation, there is no guarantee onDestroy will be called. I could not find an explicit mention to what happens when the process is killed, but it seems that you are more likely to be called onStop. So you can try to start your service with an intent from onStop.
Also, there are documented ways to prevent your process to be elected, such as: having a related Activity running or having ongoing callbacks in BroadcastReceiver or Service.
Note well that your process might get killed by the user, and refusing to comply to the user's desire to kill is invasive. Therefore the best solution should be designed around the actual reason why a user would want your process to stay alive.

Android Service stops broadcasting progress after a while

I have an Activity where the user can download a video. Upon user's click, the Download Service starts to download the content.
There is a progress bar in the Activity UI which I would like to update according to download progress in the service which broadcasts the progress periodically.
Everything works fine but after a certain time the service stops sending any broadcast progress, hence, the UI does not update anymore.
Additionally, how I can resume receiving the progress broadcast when the user goes to another Activity and comes back to this Activity? I mean, even if the above issue is solved, when the user presses back button and go to other activity and comes back to this activity, the progress gets lots. How can I check for any existing broadcast and receive it whenever the user comes to this activity.
In the ACTIVITY:
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
Log.d("DownloadService", "Progress Received In Activity");
Double progress = bundle.getDouble("PROGRESS");
updateDownloadProgressBar(progress);
}
}
};
private void startDownloadService() {
final String videoId = mItem.getAttributes().get(KEY_ASSET_ID);
Intent intent = new Intent(this, DownloadService.class);
intent.putExtra("VIDEOID", videoId);
startService(intent);
}
in the onResume():
registerReceiver(receiver, new IntentFilter(DownloadService.NOTIFICATION_SERVICE));
in the onPause():
unregisterReceiver(receiver);
In the SERVICE:
private void publishProgress(double progress) {
Log.d(TAG, "Broadcasting progress from Service");
Intent intent = new Intent(NOTIFICATION_SERVICE);
intent.putExtra("PROGRESS", progress);
sendBroadcast(intent);
}
The download and progress work fine to 38% then stop.
It seems that the service is being stopped/killed from the OS, to avoid that use foreground service so you can make sure it will not be killed from the OS.
See the sample code below:
Service
public class PendingService extends Service {
private final static String TAG = "PendingService";
public final static int NOTIFICATION_ID = 94;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startInForeground();
// Do your work here ...
return START_STICKY;
}
private void startInForeground() {
String NOTIFICATION_CHANNEL_ID = "default";
String NOTIFICATION_CHANNEL_NAME = "My Pending Service";
String NOTIFICATION_CHANNEL_DESC = "This notification holding a pending task";
Intent notificationIntent = new Intent(this, SplashActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.notification)
.setOngoing(true)
.setAutoCancel(true)
.setContentIntent(pendingIntent);
if (Build.VERSION.SDK_INT >= 26) {
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);
channel.setDescription(NOTIFICATION_CHANNEL_DESC);
channel.setSound(null, null);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
notificationManager.createNotificationChannel(channel);
}
}
Notification notification = builder.build();
startForeground(NOTIFICATION_ID, notification);
}
#Override
public void onDestroy() {
super.onDestroy();
removeNotification(NOTIFICATION_ID);
// ....
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void removeNotification(int notificationId) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
notificationManager.cancel(notificationId);
}
}
}
Utils you may need
class ServiceUtils {
/**
* #param service: Service to run
*/
fun startService(context: Context, service: Class<out Service>) {
val serviceIntent = Intent(context, service)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(serviceIntent)
} else {
context.startService(serviceIntent)
}
}
/**
* #return True: if the service is running
*/
fun isServiceRunning(context: Context, serviceClass: Class<*>): Boolean {
val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
for (service in manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.name == service.service.className) {
return true
}
}
return false
}
}

Why does foreground service stop working when device go into sleep mode

I want to create an app that is constantly checking for location change and put the current location in the firebase (e.g. an app for runners).
Unfortunately the foregroundservice is being stopped or paused every time the device go into sleep mode.
For starters I wanted to create a foreground service that is continuously writing information to the base (that would be a time stamp or a simple string) every second.
After some time it just stops writing to firebase without calling stopself().
The service is working fine on the emulator (even if put to sleep), but stops when tested on a real device – in my case Huawei, Android 8.1.0.
What should I do to force service to run in every state of the device?
My MainActivity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent intent = new Intent(this, MyService.class);
intent.putExtra("action", "start");
startForegroundService(intent);
}
else {
Intent intent = new Intent(this, MyService.class);
intent.putExtra("action", "start");
startService(intent);
}
}
#Override
protected void onDestroy() {
super.onDestroy();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent intent = new Intent(this, MyService.class);
intent.putExtra("action", "stop");
startForegroundService(intent);
}
else {
Intent intent = new Intent(this, MyService.class);
intent.putExtra("action", "stop");
startService(intent);
}
}
}
MyService:
public class MyService extends Service {
int i =0;
private String CHANNEL_ID = "2345";
#Override
public void onCreate() {
super.onCreate();
}
public MyService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(1000, createNotification());
String action = intent.getExtras().getString("action");
switch (action){
case "start":
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
myfunction();
handler.postDelayed(this, 1000);
}
};
break;
case "stop":
stopfunction();
break;
}
handler.postDelayed(runnable, 1000);
return START_NOT_STICKY;
}
private void stopfunction() {
stopSelf();
}
private void myfunction() {
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference("locations");
myRef.child("location").setValue(i);
i++;
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return null;
}
#RequiresApi(Build.VERSION_CODES.O)
private void createChannel(){
NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, getString(R.string.infoTxt),
NotificationManager.IMPORTANCE_HIGH);
channel.setShowBadge(false);
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
notificationManager.createNotificationChannel(channel);
}
private Notification createNotification(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
createChannel();
}
Intent notificationItent = new Intent(this, MainActivity.class);
notificationItent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent intent = PendingIntent.getActivity(this, 0, notificationItent, 0);
return new NotificationCompat.Builder(this, CHANNEL_ID)
.setColor(ContextCompat.getColor(this, android.R.color.background_dark))
.setContentIntent(intent)
.setSmallIcon(R.drawable.ic_launcher_background)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setOnlyAlertOnce(true)
.setContentTitle("GPS Location")
.build();
}
}
I've tried everything: service, foreground service, broadcast receiver, jobSheduler, WorkerManager – nothing helped. Then I found it’s a new HUAWEI feature called “power-intensive app monitor “. It kills every app that runs in the background for a long time unless user gives special permissions to it.
The path to do this:
Settings -> Security & privacy -> Location services -> recent location requests: YOUR APP NAME -> Battery -> uncheck Power-intensive prompt, App launch: Manage manually: check all three positions: Auto-launch, secondary launch, run in background.
I don’t know is there a way to do this programmatically. I think the best way is to create a sort of help activity and explain the user what to do if application won’t work.
Foreground services generally should be used for task which require user attention such as visual processes.
use Background service instead

Run API every second in Background after Android App is killed in Oreo version

I am trying to Build a Android Application which will run every second and when app is closed or killed then also it should run continuously in Background.
When API response condition is satisfied it should show a Local Notification..
I have used Service Class for background Task. It was working fine in all version Except the Oreo Version (8.1v)
I have check website and Example related to it, I have find out that we can't perform background task in Oreo Version after the app is closed or killed.
So I tried to use startForeground() then also it is not working,
After many tries, finally I am asking this question here.
So please help me to run a API in Background when App is closed.
MainActivty.class
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ContextCompat.startForegroundService(this, new Intent(this,MyService.class));
} else {
startService(new Intent(this,MyService.class));
}
}
MyService.class
public class MyService extends Service {
public static final int notify = 3000; //interval between two services(Here Service run every 5 Minute)
private Handler mHandler = new Handler(); //run on another Thread to avoid crash
private Timer mTimer = null; //timer handling
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public void onCreate() {
super.onCreate();
if (mTimer != null) // Cancel if already existed
mTimer.cancel();
else
mTimer = new Timer(); //recreate new
mTimer.scheduleAtFixedRate(new TimeDisplay(), 0, notify); //Schedule task
}
//class TimeDisplay for handling task
class TimeDisplay extends TimerTask {
#Override
public void run() {
mHandler.post(new Runnable() {
#Override
public void run() {
new ApiCallAsyncTask().execute(URL);
}
});
}
}
}
Notification Method which is called in ApiCallAsyncTask class
Notification notif;
#TargetApi(Build.VERSION_CODES.O)
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void notification(String Name, String time,String mId,int id){
Intent intent = new Intent(MyService.this, MainActivity.class);
String CHANNEL_ID = String.valueOf(id);
PendingIntent pendingIntent = PendingIntent.getActivity(MyService.this, 100, intent, PendingIntent.FLAG_ONE_SHOT);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, Name, NotificationManager.IMPORTANCE_DEFAULT);
notif = new Notification.Builder(MyService.this)
.setContentIntent(pendingIntent)
.setContentTitle("Reminder")
.setContentText("hello")
.setSmallIcon(R.drawable.logo)
.setOnlyAlertOnce(true)
.setColor(ContextCompat.getColor(MyService.this, R.color.colorPrimaryDark))
.setChannelId(CHANNEL_ID)
.build();
notificationManager.createNotificationChannel(mChannel);
}else {
notif = new Notification.Builder(MyService.this)
.setContentIntent(pendingIntent)
.setContentTitle("Reminder")
.setContentText("hello")
.setSmallIcon(R.drawable.logo)
.setOnlyAlertOnce(true)
.setColor(ContextCompat.getColor(MyService.this, R.color.colorPrimaryDark))
.build();
}
notif.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(id, notif);
startForeground(1, notif);
}
Thank You..
You can use combination of JobIntentService + AlarmManager(for scheduling) or JobScheduler API.
But I strongly recommend replace your approach with Firebase Cloud Messaging. So you will place business logic on server side and notify clients in special cases.

Categories