I'm writing a service that sends a message to a remote server every interval.
I'm using Android 4.4 (KitKat).
The service also has an activity that allows some minimal configuration such as the remote server's URL.
I managed to make the service work, start from boot and restart if killed, but I have some issue with the latter.
If I open the app for the first time, all works well. Killing the app results in a broadcast intent and the service revives (without the activity of course). Now, if I open the app a second time (after the kill and revive), the next time I kill the app, the service does not restart.
My broadcast receiver:
public class dynDNSBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "dynDNSBroadcastReceiver";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG,"Broadcast received, starting dynDNS service");
context.startService(new Intent(context, dyndns.class));
}
}
My service:
public class dyndns extends Service {
private static String TAG = "dynDNS service";
public SharedPreferences myPrefs;
private Handler mainHandler;
private Timer timer;
#Override
public IBinder onBind(Intent arg0) {
Log.i(TAG, "Service onBind");
return null;
}
#Override
public void onCreate() {
super.onCreate();
startForeground(1, new Notification());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "dynDNS service started");
mainHandler = new Handler(getApplicationContext().getMainLooper());
timer = new Timer();
timer.schedule(new MyTimerTask(), 10000, 10000);
return START_STICKY;
}
#Override
public void onDestroy() {
timer.cancel();
super.onDestroy();
Log.d(TAG, "dynDNS service destroyed");
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("restartService");
broadcastIntent.setClass(this, dynDNSBroadcastReceiver.class);
this.sendBroadcast(broadcastIntent);
}
Relevant code from my activity:
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
assert manager != null;
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
Log.d(TAG, serviceClass.getName());
Log.d (TAG, "Service status: Running");
return true;
}
}
Log.d (TAG, "Service status: Not running");
return false;
}
#Override
protected void onDestroy() {
stopService(mServiceIntent);
super.onDestroy();
}
Relevant code form the manifest:
<service
android:name="com.dyndns.dyndns"
android:enabled="true" android:process=":externalProcess">
<intent-filter>
<action android:name="com.dyndns.dyndns" />
</intent-filter>
</service>
<receiver android:name="dynDNSBroadcastReceiver" android:enabled="true" android:exported="true">
<intent-filter>
<action android:name="restartService"/>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
Related
I have created an Enterprise mobility managament (EMM)app. When i launched it a few years ago my customer base probably had Android < 8 devices. I've recently had to update my app and the customer base are now using Android 11 devices.
The problem:
I have a BG service that constantly runs. It counts down from 500, then when it hits 0, it fires off a web service that updates states in the EMM portal. The portal is what the customers use to see their devices out in the field.
In the ondestroy method of my BG service, i send a broadcast that uses a receiver to relaunch the destroyed service, thus keeping it alive.
My customers need this service alive as it sends info like data usage and battery level info which could be safety critical in their industry.
What i have tried:
I understand that Android Doze puts app to sleep, i have tried using my solution to send a command to the device that whitelists it from Doze, this does not work.
I've tried using JobIntentService instead but this did not work either.
NB. my app is a device admin app, so does not need to be in the foregroud and actually shouldn't be. Device Admins are also exemp from Doze apparently.
The error:
Caused by: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=xxxxx/.ConnectivityService }: app is in background
What is the best way to keep this service running in the background on Android 11?
here is my code:
<service
android:name="xxx.ConnectivityService"
android:enabled="true"
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
.
public class ConnectivityServiceRestarterBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.e(ConnectivityServiceRestarterBroadcastReceiver.class.getSimpleName(), "Service Stops! ");
context.startService(new Intent(context, ConnectivityService.class));
//ConnectivityService.enqueueWork(context, new Intent());
}
}
.
public class ConnectivityService extends Service {
private static final String TAG = ConnectivityService.class.getSimpleName();
public int counter=0;
AppObj appobj;
public ConnectivityService(Context applicationContext) {
super();
Log.e(TAG, "inside ConnectivityService!");
}
public ConnectivityService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
Log.e(TAG, "inside onStartCommand");
appobj = (AppObj)getApplication();
startTimer();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "ondestroy!");
Intent broadcastIntent = new Intent("xxx.ActivityRecognition.RestartConnectivityservice");
sendBroadcast(broadcastIntent);
stoptimertask();
}
private Timer timer;
private TimerTask timerTask;
long oldTime=0;
public void startTimer() {
Log.e(TAG, "inside startTimer");
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, to wake up every 1 second
timer.schedule(timerTask, 1000, 1000); //
}
/**
* it sets the timer to print the counter every x seconds
*/
public void initializeTimerTask() {
Log.e(TAG, "inside initializeTimerTask");
timerTask = new TimerTask() {
public void run() {
Log.e(TAG, "in timer ++++ "+ (counter++));
if(counter == 3600){
counter = 0;
appobj.webService.sendPulseToServer();
Intent myIntent = new Intent(getApplicationContext(), TrafficMonitorService.class);
myIntent.setAction("xxx.TrafficMonitorService");
getApplicationContext().startService(myIntent);
}
}
};
}
/**
* not needed
*/
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
.
<receiver
android:name="xxx.ConnectivityServiceRestarterBroadcastReceiver"
android:enabled="true"
android:exported="true"
android:label="ConnectivityServiceRestartServiceWhenStopped">
<intent-filter>
<action android:name="xxx.ActivityRecognition.RestartConnectivityservice"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
I have created a background service for registering SCREEN_ON and SCREEN_OFF intent to catch an event using broadcast receiver but some time due to unknown reason my service does not pass intent to my main application. For testing I have generated log file using ten second timer that it is my background service working or not, which I have attached below. I got a issue that my background service pause some time and start it automatically.
It is my background service
public class MyBackgroundService extends Service {
BroadcastReceiver receiver;
private static final String LOG_TAG = "MyBackgroundService";
#Override
public void onCreate() {
super.onCreate();
Timber.i("Foreground Service OnCreate");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Timber.i("Start Foreground Service");
receiver = new ScreenStateReceiver();
registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_ON));
registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
if (intent.getAction().equals(Constants.STARTFOREGROUND_ACTION)) {
Intent notificationIntent = new Intent(this, HomeActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("text")
.setTicker("text")
.setContentText("text")
.setSmallIcon(R.drawable.logo_icon)
.setContentIntent(pendingIntent)
.setOngoing(true).build();
startForeground(Constants.NOTIFICATION_ID.FOREGROUND_SERVICE,
notification);
}
startTimer();
return START_STICKY;
}
#Override
public boolean onUnbind(Intent intent) {
Timber.i("Foreground Service onUnbind");
return super.onUnbind(intent);
}
#Override
public void onTaskRemoved(Intent rootIntent) {
Timber.i("Foreground Service onTaskRemoved");
super.onTaskRemoved(rootIntent);
}
#Override
public void onDestroy() {
stopForeground(true);
Timber.i("Foreground Service onDestroy");
stoptimertask();
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
// Used only in case of bound services.
return null;
}
private Timer timer;
private TimerTask timerTask;
public void startTimer() {
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, to wake up every 1 second
timer.schedule(timerTask, 10000, 10000); //
}
/**
* it sets the timer to print the counter every x seconds
*/
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
Timber.i("Timer");
}
};
}
/**
* not needed
*/
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
}
It is my broadcast Receiver
public class ScreenStateReceiver extends BroadcastReceiver {
public ScreenStateReceiver(){
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action.equals(Intent.ACTION_SCREEN_OFF)){
Timber.i("Screen OFF");
}
else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
Timber.i("Screen ON");
}
}
}
Menifest,xml file declaration
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<service android:name=".service.MyBackgroundService"/>
<receiver android:name=".receiver.ScreenStateReceiver" android:enabled="true" android:exported="true">
<intent-filter >
<action android:name="android.intent.action.SCREEN_OFF" />
<action android:name="android.intent.action.SCREEN_ON" />
</intent-filter>
</receiver>
I am starting Service using intent
Intent service = new Intent(getContext().getApplicationContext(), MyBackgroundService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
service.setAction(Constants.STARTFOREGROUND_ACTION);
getContext().getApplicationContext().startForegroundService(service);
}
else{
service.setAction(Constants.MAIN_ACTION);
getContext().getApplicationContext().startService(service);
}
Here this picture of my log file
LOG FILE
using this log file you can see background service is stop and start automatically.
please help me resolve this issue.
There is a restriction for service in Oreo onward, We can start a service by calling startService() only when the application is in foreground. When you exit from the app (app is in background), then it has a window of several minute, in that app can run the service. At the end of window, app considered to be idle,that time system stop the background service. In order to achieve run a service even in background, either we have to implement foreground service or we have to use job scheduler etc.
i want when boot run app server allow for receive message any time
without showing application
this code problem when boot show Notification twice only
but i want receive message any time
Is this "android:exported" important, What used
code AndroidManifest
<service
android:name=".appService"
android:exported="true"
android:enabled="true">
</service>
<receiver
android:name=".ServiceStarterBoot"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</receiver>
code BroadcastReceiver
public class ServiceStarterBoot extends BroadcastReceiver {
private Context context ;
#Override
public void onReceive(Context context1, Intent intent) {
this.context = context1;
if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
Intent serviceLauncher = new Intent(context, appService.class);
context.startService(serviceLauncher);
}}
code Service
public class appService extends Service {
#Override
public void onCreate() {
super.onCreate();
lood();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
//return mBinder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent, flags, startId);
}
private void lood(){
SystemClock.sleep(3000);
Runnable runnable = new Runnable() {
public void run() {
boolean p= true ;
while(p) {
SystemClock.sleep(5000);
showNotification();
}
}
};
Thread mythread = new Thread(runnable);
mythread.start();
}
please help
thanks
you have to make your service sticky:
Service.START_STICKY
then it is infinite and restarts itself when killed by OS for any reason.
It even starts itself when booting OS and app is NOT opened by user.
Best regards.
pseudo code:
onStartCommand(..){
...
//e.g. wifilisterner
Wifilistener WF = new Wifilistener(Interface yourInterfaceCallBackMethod);
}
something like this.
When I run my app in an emulator and kill the process, my service gets started and runs in the background (Toast: "Service Called") BUT it does not get called at all on a real device and no logcat runs because the broadcast receiver or my service does not get called:
MainFest:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- Service -->
<service android:name=".MyService">
<intent-filter>
<action android:name="com.mypackage.myapp.MyService" />
</intent-filter>
</service>
<receiver
android:name=".BootReceiver"
android:enabled="true"
android:exported="true"
android:label="BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- Service -->
MainActivity:
AsyncTask.execute(new Runnable() {
#Override
public void run() {
//TODO your background code
Intent i = new Intent(MainActivity.this, PushNotification.class);
startActivity(i);
}
});
PushNotification.class:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Context context = this;
if (!isMyServiceRunning()){
Intent serviceIntent = new Intent(context, MyService.class);
context.startService(serviceIntent);
finish();
}
else
{
finish();
}
}
private boolean isMyServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (MyService.class.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
BootReceiver:
#Override
public void onReceive(Context context, Intent intent) {
if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
Intent serviceIntent = new Intent(context, MyService.class);
context.startService(serviceIntent);
}
}
}
MyService:
public class MyService extends Service {
Handler handler;
#Override
public int onStartCommand(Intent intent, int flags, int startId){
// START YOUR TASKS
Toast.makeText(MyService.this, "Service Called", Toast.LENGTH_SHORT).show();
Context context = this;
Intent in = new Intent(context, FragmentMain.class);
in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
in.putExtra("valuerunInBG", "1");
context.startActivity(in);;
//loop();
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
// STOP YOUR TASKS
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
Modify your manifest as given below
<receiver
android:name=".BootReceiver"
android:enabled="true"
android:exported="true"
android:label="BootReceiver">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
I made a small change in it just given the priority by which it will start as early as possible
also change the code given below
#Override
public int onStartCommand(Intent intent, int flags, int startId){
// START YOUR TASKS
Toast.makeText(MyService.this, "Service Called", Toast.LENGTH_SHORT).show();
Context context = this;
Intent in = new Intent(context, FragmentMain.class);
in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
in.putExtra("valuerunInBG", "1");
context.startActivity(in);;
//loop();
return START_STICKY; //THIS WILL RESTART YOUR SERVICE IF IT GETS STOPPED BY ANDROID OS DUE TO LOW MEMORY
}
android.intent.action.BOOT_COMPLETED Takes time after your device starts, try waiting 3-4 minutes
Start your service again in your service class's onDestroy() method. This way when your application will be closed, then onDestroy() will be called and that will start your service again.
Some time Toast does not work inside of Service so please use Log to know whether your code is running or not.
i want to make my app to be run in background and listens for
contact,sms deletion events.
for that i created a service in my app but i dnt how to start without activity
my code is like this
public class DeleteService extends Service {
ContentResolver cr;
MyContentObserver observer=new MyContentObserver();
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return mBinder;
}
#Override
public void onCreate() {
cpath=ContactsContract.Contacts.CONTENT_URI;
// some action
}
#Override
public void onDestroy() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Launch a background thread to do processing.
super.onStartCommand(intent, flags, startId);
cpath=ContactsContract.Contacts.CONTENT_URI;
cr=getContentResolver();
cur=cr.query(cpath, null, null, null, null);
this.getApplicationContext().getContentResolver().registerContentObserver(cpath, true, observer);
return Service.START_STICKY;
}
private class MyContentObserver extends ContentObserver {
public MyContentObserver() {
super(null);
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
nfm=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
int NOTIFICATION_ID = 1;
Intent intent1 = new Intent();
PendingIntent pi = PendingIntent.getActivity(DeleteService.this, 1, intent1, 0);
nf=new Notification(R.drawable.ic_launcher,"Contact Database changed",System.currentTimeMillis());
nf.setLatestEventInfo(getApplicationContext(), "Delete Event", "contact name", pi);
nf.flags = nf.flags |
Notification.FLAG_ONGOING_EVENT;
}
#Override
public boolean deliverSelfNotifications()
{
super.deliverSelfNotifications();
return true;
}
}
public class LocalBinder extends Binder {
DeleteService getService() {
return DeleteService.this;
}
}
}
register ACTION_SCREEN_ON or ACTION_USER_PRESENT broadcast recivers for your Appliction in Service and start Service when screen is on or user is present. you can register ACTION_SCREEN_OFF broadcast reciver for stoping Service when phone screen is off to avoid battery drain by your app.as:
In manifest.xml:
<receiver android:name="com.my.AppStart">
<intent-filter>
<action android:name="android.intent.action.SCREEN_ON" />
<action android:name="android.intent.action.SCREEN_OFF" />
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
BroadcastReceiver :
public class AppStart extends BroadcastReceiver {
public static final String present = "android.intent.action.USER_PRESENT";
public static final String screenon = "android.intent.action.SCREEN_ON";
public static final String screenoff = "android.intent.action.SCREEN_OFF";
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(present) || intent.getAction().equals(screenon) )
{
Intent i=new Intent(context,DeleteService.class);
context.startService(i);
}
if (intent.getAction().equals(screenoff))
{
//STOP YOUR SERVICE HERE
}
}
}
A service can only by started by an Activity, or a BroadCast receiver, or a service which is already started. It can't be stand-alone(It can't start by itself). So, you would need one of the two components to start it. you can make an activity which starts the service which is the preferred way. But if you don't want to provide a user interface, implement a broadcast receiver which fires up when the phone is switched on and the boot up is completed, Inside that br, start your service. This will also help you run the service as soon as a phone starts.
for example in your manifest:
<receiver android:name="com.my.MyBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
and in the br:
public class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent i=new Intent(context,DeleteService.class);
context.startService(i);
}
}
In your activity .. put this code in oncreate
Intent svc=new Intent(youractivity.this,DeleteService.class);
startService(svc);