In my Android app, I record a lot of things like GPS, barometric pressure, Bluetooth sensors. There is a "super" service which is started in the foreground:
Intent intent = new Intent(context, DataCollectorService.class);
ContextCompat.startForegroundService(context, intent);
The service itself has to start numerous child services like GPS-recording, Bluetooth sensor recording and the like. And the service creates a notification:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// . . .
Notification notification = new NotificationCompat.Builder(this, channelId)
// . . .
.build();
startForeground(NOTIFICATION_ID, notification);
mHandler.postDelayed(this::startChildServices, 100);
return START_STICKY;
}
private void startChildServices() {
// . . .
if (gpsEnabled) {
Intent intent = new Intent(this, GpsTrackingService.class);
bindService(intent, mGpsServiceConnection, Context.BIND_AUTO_CREATE);
startService(intent);
}
if (isPressureEnabled) {
Intent intent = new Intent(this, PressureService.class);
bindService(intent, pressureConnection, Context.BIND_AUTO_CREATE);
startService(intent);
}
// . . .
}
All works well, but a very small percentage of the users encounter crashes like this:
android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()
Actually, I start the "super" service in the foreground, as you can see.
Do I have to start the child services in the foreground as well?
Obviously, I don't want to have 3-7 notification icons in the users status bar.
Related
I feel like I am doing something stupid here. I have set up a service which listens to changes in a collection in my firebase database, and when there has been a change, the app is meant to open, except the activity doesn't open. Both the log message and the toast appear when the data is changed in the collection, but the activity doesn't open. The code from the onStartCommand is below.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String input = intent.getStringExtra("inputExtra");
context = getApplicationContext();
Intent notificationIntent = new Intent(context, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Example Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
reference.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot value, #Nullable FirebaseFirestoreException error) {
for (DocumentChange documentChange : value.getDocumentChanges()) {
if (documentChange.getType() == DocumentChange.Type.MODIFIED) {
Log.d(TAG, "onComplete: reference modified");
Toast.makeText(context, "message received", Toast.LENGTH_SHORT).show();
Intent intent1 = new Intent(context, Open.class);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent1);
}
}
}
});
return START_STICKY;
}
Any help would be greatly appreciated.
Android 10 (API level 29) and higher place restrictions on when apps can start activities when the app is running in the background. These restrictions help minimize interruptions for the user and keep the user more in control of what's shown on their screen.
For the purposes of starting activities, an app running a foreground service is still considered to be "in the background"
Alternatives to display activity
Apps that are in the background should display time-sensitive notifications to provide urgent information to the user instead of directly starting an activity.
Exceptions to the restrictions:
There are some exceptions in which app can display activity directly, some of those are:
The app has a visible window, such as an activity in the foreground.
The app has an activity in the back stack of the foreground task.
The app has an activity in the back stack of an existing task on the
Recents screen.
For more detailed articles, read this
I have a service start in Activity A with
private void startService() {
Intent pushIntent = new Intent(this, MyService.class);
pushIntent.putExtra(MyService.TYPE_SCREEN, 1);
startService(pushIntent);
}
in my Service I get data from onStartCommand
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
typeScreen = intent.getExtras().getInt(TYPE_SCREEN);
return Service.START_STICKY;
}
I want when change from activity A to activity B, I will update type of screen from 1 to 2 in Service.
How I can do it?
Android only create one instance of a service.
Request that a given application service be started. The Intent can
either contain the complete class name of a specific service
implementation to start, or an abstract definition through the action
and other fields of the kind of service to start. If this service is
not already running, it will be instantiated and started (creating a
process for it if needed); if it is running then it remains running.
So in your case, the service is already running, then you just have to send the intent with the screen 2 extra. It will only call the onStartCommand Override method.
Intent pushIntent = new Intent(this, MyService.class);
pushIntent.putExtra(MyService.TYPE_SCREEN, 2);
startService(pushIntent);
I am targeting Oreo. As you know, oreo introduced limits on background task execution time. Workarounds are - according to google - to put the background task in the foreground. This is what I was trying to do, yet once the foreground service is running, it gets destroyed after some time.
First the phone switches off it's screen, then once I activate it again, the background task continues. Sometimes onDestroy on the foreground service is called without the task being completed.
My goal is to have all tasks being set by enqueueWork to be executed without ondestroy being called and without phone sleep mode to interrupt it.
ForeGroundService
public class ForeGroundService extends JobIntentService {
static final int JOB_ID = 1000;
static final int ONGOING_NOTIFICATION_ID = 33;
static void enqueueWork(Context context, Intent work) {
enqueueWork(context, ForeGroundService.class, JOB_ID, work);
}
Notification.Builder notification;
NotificationManager mNotificationManager;
#RequiresApi(api = Build.VERSION_CODES.O)
void einleitung(String Titel, String Text)
{
Intent notificationIntent = new Intent(this, ForeGroundService.class);
PendingIntent pendingIntent =
PendingIntent.getActivity(this, 0, notificationIntent, 0);
mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(Titel,
Text,
NotificationManager.IMPORTANCE_HIGH);
channel.setSound(null,null);
mNotificationManager.createNotificationChannel(channel);
}
notification =
new Notification.Builder(this,Titel)
.setContentTitle(Titel)
.setContentText(Text)
.setSmallIcon(R.drawable.kleinesicon)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.setContentIntent(pendingIntent)
.setTicker("setTicker");
mNotificationManager.notify(ONGOING_NOTIFICATION_ID, notification.build());
startForeground(ONGOING_NOTIFICATION_ID, notification.build());
}
#RequiresApi(api = Build.VERSION_CODES.O)
void vordergrund(String Titel, String Text)
{
notification.setContentTitle(Titel);
notification.setContentText(Text);
mNotificationManager.notify(ONGOING_NOTIFICATION_ID, notification.build());
}
PowerManager.WakeLock wakeLock;
#RequiresApi(api = Build.VERSION_CODES.O)
#Override
protected void onHandleWork(Intent intent) {
if (beginn) {
einleitung("Test", "Test");
beginn = false;
}
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"MyWakelockTag");
wakeLock.acquire();
//Do Work
}
#Override
public void onDestroy() {
super.onDestroy();
Intent local = new Intent();
local.setAction("de.test.action");
this.sendBroadcast(local);
stopForeground(true);
//toast("Fertig");
if (wakeLock != null)
wakeLock.release();
}
final Handler mHandler = new Handler();
}
MainActivity
public class MainActivity extends AppCompatActivity {
private int JI = 1000;
private BroadcastReceiver updateUIReciver;
#RequiresApi(api = Build.VERSION_CODES.O)
void somefunction(someparameters)
{
Intent mServiceIntent = new Intent();
mServiceIntent.putExtra...
ForeGroundService.enqueueWork(getBaseContext(),ForeGroundService.class,JI,mServiceIntent);
}
#Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(updateUIReciver);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
IntentFilter filter = new IntentFilter();
filter.addAction("de.test.action");
updateUIReciver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
ForeGroundService.shouldContinue = false;
}
};
registerReceiver(updateUIReciver,filter);
btnB.setOnClickListener(new View.OnClickListener() {
#RequiresApi(api = Build.VERSION_CODES.O)
public void onClick(View v) {
if (startcondition)
{
Intent startIntent = new Intent(MainActivity.this, MyService.class);
startIntent.setAction(Constants.ACTION.START_ACTION);
startService(startIntent);
Intent serviceIntent = new Intent(MainActivity.this,ForeGroundService.class);
startForegroundService(serviceIntent);
somefunction(someparameters);
}
else
{
Intent stopIntent = new Intent(MainActivity.this, MyService.class);
stopIntent.setAction(Constants.ACTION.STOP_ACTION);
startService(stopIntent);
}
}
});
}
}
EDIT: I made it work with sandhya sasane's solution and
public int onStartCommand(Intent intent, int flags, int startId)
{
if (beginn) {
executorService = Executors.newFixedThreadPool(1);
beginn = false;
}
final Intent i2 = intent;
executorService.execute(new Runnable(){
#Override
public void run(){
abarbeiten(i2);
}
});
return START_STICKY;
}
Important is the 1 in newFixedThreadPool(1); to only have one thread run at once
I am targeting Oreo. As you know, oreo introduced limits on background task execution time.
Yes, it does. I can understand you, as google has made the things very odd and complex first..., then again complicated... then again... then again... And now developers like me and you, and your question and problem, denotes the outcome / result / proof of that.
Workarounds are - according to google ...
Please save time and yourself too... Google documentation is worst.. i have given -10 out of 10 for their documentation.
to put the background task in the foreground.
You have a wrong perception of what foreground concept is..!! Read complete answer word by word carefully, Your problem will get solved..!!
This is what I was trying to do, yet once the foreground service is running, it gets destroyed after some time...
Now very simply... Your Concept and implementation, both are wrong..., So Try with a new sample project and guidelines provided here along with sample working and tested code across 4.0 to latest android P
.
First the phone switches off it's screen, then once I activate it again, the background task continues. Sometimes onDestroy on the foreground service is called without the task being completed.
It does not relate to foreground service, in any way.... forget this.
My goal is to have all tasks being set by enqueueWork to be executed without ondestroy being called and without phone sleep mode to interrupt it.
Forget this too... Lets first see what a foreground service is and how it is created...
What is foreground service
A service which remains active (It does not mean... continuously
running like never ending do-while loop)
Remain active until next boot / reboot
Even if user removes app from recents, it remains
But It does not remain active post next boot
It needs to be restarted by user by opening app again or via a broadcast receiver of ON_BOOT_COMPLETE or by a AlarmManager or By a JobScedular
When to use
As per my view users do not like a permanent notification showing message ^This is running in foreground and may discharge your battery soon^ , Again user would not be able to swipe it away and can only force stop or uninstall app to stop it. So it is as per my implementations point of view , ^Developers must use this for implementing runtime receivers as post - oreo devices do not welcomes static receivers implemented by extending Broadcastreceiver and placing its intent entry in manifest.xml file... Even if developer tries to do this that receiver will never get called on post - oreo devices ..., Yes it will get called below oreo devices. So implement just a ON_BOOT_COMPLETE receiver and rest all in a service.
How to implement a foreground service
Right click on project structure and make a service named RunnerService and then generate all mandatory methods. it does not require you to type all code manually.. Generate it as said. Sample foreground service :
public class RunnerService extends Service
{
NotificationManager mNotifyManager;
NotificationCompat.Builder mBuilder;
NotificationChannel notificationChannel;
String NOTIFICATION_CHANNEL_ID = "1";
public RunnerService() { }
#Override
public void onCreate()
{
super.onCreate();
Log.d("RUNNER : ", "PROGRAMMED.... \n");
Bitmap IconLg = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground);
mNotifyManager = (NotificationManager) getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(this, null);
mBuilder.setContentTitle("App Name")
.setContentText("Foreground service...")
.setTicker("Foreground service...")
.setSmallIcon(R.drawable.ic_menu_slideshow)
.setLargeIcon(IconLg)
.setPriority(Notification.PRIORITY_HIGH)
.setVibrate(new long[] {100})
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setOngoing(true)
.setAutoCancel(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "My Notifications", NotificationManager.IMPORTANCE_HIGH);
// Configure the notification channel.
notificationChannel.setDescription("Channel description");
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.setVibrationPattern(new long[]{100});
notificationChannel.enableVibration(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
mNotifyManager.createNotificationChannel(notificationChannel);
mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
startForeground(1, mBuilder.build());
}
else
{
mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
mNotifyManager.notify(1, mBuilder.build());
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
Log.d("RUNNER : ", "\n IT IS ACTIVE UNTIL NEXT BOOT....");
return START_STICKY;
}
#Override
public void onDestroy()
{
Log.d("RUNNER : ", "\n IT WILL BE AGAIN ACTIVE BY ANDROID OS AUTOMATICALLY, DO NOT WORRY AND DONT CODE TO START IT AGAIN !!....");
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent)
{
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("NOT_YET_IMPLEMENTED_BY_DEVELOPER");
}
}
How to start it
It depends on which android you are targeting below oreo or post oreo ... I will prefer to on all like below :
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
this.startForegroundService(new Intent(this, RunnerService.class));
}
else
{
this.startService(new Intent(this, RunnerService.class));
}
Either from MainActivity or any ON_BOOT_RECEIVER, or from wherever you want, just start it as said here...
How to test is in foreground
By removing it from recents... It will call onDestroy but it will be never destroyed you will not be able to swipe away notification. This means a success.
How to test it quick
With a sample new project with a MainActivity just calling service in said manner.
What next..?
Yes you can ask your next tasks here only..., I will keep updating and guiding... I hope you have kept enqueueWork concept and all your concepts aside and do not thinking on it...
Lets go step by step and let me know the updates....
UPDATE 2
You should try it on emulator only... If success then try it on actual devices... Here is a problem again...
There are many mobile phone manufacturers in the world now, which takes
stock android from google as it is open source and modifies it to disable all services on BOOT. It only keeps Google , WhatsApp, FaceBook , Twitter and major market leaders... As if they do not allow them no one will purchase their devices ...
Examples :
Vivo = FunTouchOs
Oppo = ColorOs
There is a huge list....
Do not check on this for BOOT_COMPLETE..., IT will not work as they are modified the android..
But i want to test it on actual device
Then test it on such device which os is purely from google and having android os.
Then what should i do for other os modified from android
There are tricks ..., But lets go step by step.... I will let you know , once you success in this..!!
UPDATE : 3
As it is not clear what is the requirement i am making some assumptions and writing answer :
What you can do to implement foreground execution is :
Implement foreground service as i depicted
Use local broadcastmanager to broadcast events of your own.
in onCreate of a foreground service register runtime receiver to receive that broadcasts
On receiving broadcasts call to the methods of user defined class with context of foreground service. And perform all tasks from there.
Unregister receiver from onDestroy of foreground service.
What you can do to implement background execution is :
If you are having repeating tasks and wants to execute it in background even if the app is removed from recents ... Then :
Use Firebase Job Dispatcher which uses GooglePLAYServices
If you use forever then that job will be triggered automatically even if system is rebooted and even if app is not in foreground or background or in recents...
As of now i do not see any need of JobIntentService and therefore its static enqueueWork method; More resolution and details are needed for solving your problem.
I'm trying to make an Android App that records the user's activity(like where he touches or drags).
Because of the recent change in Android security and permissions, we can't make an app that draws over an another app and records its movements.
So our team decided to solve the problem this way
since the adb shell's permission is root, we can use logcat and the grep tool to parse the logs and find what we want.
create a service that constantly spins up logcat and saves into a file.
create another service that reads the file logcat created, parse, and show the info.
There is currently a problem in our team.
How can we make a service that constantly reads a file and spit out the results?
After that we can do the other jobs more easily.
You can create a service as mentioned in below step to keep the service running all the time
1) In the service onStartCommand method return START_STICKY.
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
2)Start the service in the background using startService(MyService) so that it always stays active regardless of the number of bound clients.
Intent intent = new Intent(this, PowerMeterService.class);
startService(intent);
3)Create the binder.
public class MyBinder extends Binder {
public MyService getService() {
return MyService.this;
}
}
4)Define a service connection.
private ServiceConnection m_serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
m_service = ((MyService.MyBinder)service).getService();
}
public void onServiceDisconnected(ComponentName className) {
m_service = null;
}
};
5)Bind to the service using bindService.
Intent intent = new Intent(this, MyService.class);
bindService(intent, m_serviceConnection, BIND_AUTO_CREATE);
6)For your service you may want a notification to launch the appropriate activity once it has been closed.
private void addNotification() {
// create the notification
Notification.Builder m_notificationBuilder = new Notification.Builder(this)
.setContentTitle(getText(R.string.service_name))
.setContentText(getResources().getText(R.string.service_status_monitor))
.setSmallIcon(R.drawable.notification_small_icon);
// create the pending intent and add to the notification
Intent intent = new Intent(this, MyService.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
m_notificationBuilder.setContentIntent(pendingIntent);
// send the notification
m_notificationManager.notify(NOTIFICATION_ID, m_notificationBuilder.build());
}
7)You need to modify the manifest to launch the activity in single top mode.
android:launchMode="singleTop"
8)Note that if the system needs the resources and your service is not very active it may be killed. If this is unacceptable bring the service to the foreground using startForeground.
startForeground(NOTIFICATION_ID, m_notificationBuilder.build());
I have a background service in which I want to show a notification which allows the user to stop it.
In the android SDK docs it says an activity is used to normally launch an Activity. So I am wondering if I need to create an activity to stop the service or can I directly stop the service when user selects the notification,
So how would the intend call back the service to stop it..
Thanks,
So I am wondering if I need to create an activity to stop the service or can I directly stop the service when user selects the notification,
You cannot directly stop the service from a Notification. You can start the service, using an Intent that has an action string or extra or something that the service sees in onStartCommand() and triggers it to call stopSelf().
The question is already old, but since there is still no solution with code, I simply share my code as an example for solving the problem:
You cannot directly stop the service from a Notification. You can
start the service, using an Intent that has an action string or extra
or something that the service sees in onStartCommand() and triggers it
to call stopSelf().
That's the right solution so let's jump in code (this code is all in your ExampleService class):
#RequiresApi(Build.VERSION_CODES.O)
private void startForegroundService() {
// create PendingIntend to open MainActivity (this is when the notification gets clicked) //
Intent tabIntent = new Intent(this, MainActivity.class);
tabIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent tabPendingIntent = PendingIntent.getActivity(this, 0, tabIntent, 0);
// create PendingIntend to open ExampleService (this is when the notification BUTTON gets clicked) //
Intent closeIntent = new Intent(this, ExampleService.class);
closeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
closeIntent.putExtra("destroyCode", 666); // this is the important line //
PendingIntent closePendingIntent = PendingIntent.getService(this, 0, closeIntent, 0);
createNotificationChannel(); // this is only the default code to create notification channel. I just outsourced? it //
Now the Intent has additional data (the "destroy code" -> 666). Notice that we have created 2 pendingIntents: closePendingIntent (stop Service) and tabPendingIntent (start Activity)
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
// get extras to know if Intent has destroyCode (666)
Bundle extras = intent.getExtras();
if (extras == null) {
// extras is null which means there is no destroyCode (666)
exampleMethod();
} else {
// Intent has destroyCode (666) -> Intent comes from notification -> stop the service and close notification
stopSelf();
}
return START_STICKY;
}
Now we have the code to check if there is a destroyCode or not. The last step is to create a notification with a button:
// set attributes for notification //
final NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "channelID_2");
Notification notification = builder.setOngoing(true)
.setSmallIcon(R.drawable.example)
.setContentTitle(getText(R.string.notificationTitle))
.setContentText(getText(R.string.notificationText))
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContentIntent(tabPendingIntent) //this is when notification is clicked which only opens ExampleActivity
.addAction(R.drawable.example, getString(R.string.notificationButtonText), closePendingIntent) // here is our closePendingIntent with the destroyCode .addAction is "the onClickListener for the notification button"//
.build();
startForeground(2, notification);
In onCreate you start your service
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
startForegroundService();
else
startForeground(1, new Notification());
// Toast Message that service has started
Toast.makeText(this, R.string.serviceStarted, Toast.LENGTH_SHORT).show();
That's it
You can't start an Acitivty from a Service just like that. What you can do is create a callback to an Activity in the Service and let the callback start new activities. But having a notification means you don't have to go through the Service. When the notification is clicked, you can start an activity that's specified in the Intent you supply to the notification. It's really very simple.
Do read the reference docs on notifications for examples.