Android service closed even with startForeground() - java

So I need a service for my app, but I need it to keep running all the time until the user press the end Button in the app. It goes like this : The user press start, my service start (starting a notification btw), then 2-3h or more later the user has to click on the notification which get him back to the app where he can press the "End" button and end the service. I call this a Start-End cycle for simplicity purposes.
My user will use many Start-End cycles a day, but the problem is my notification goes away after some time when the phone his lock.
I've already been able to put the notification and service thanks to Stackoverflow so I hope you have a solution for this too :)!
Here is my main activity :
public class MainActivity extends AppCompatActivity{
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkEnabled();
mTimeListenerD.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) { //Button to set the Start time
setDebutTime();
checkEnabled();
new Thread(new Runnable() {
#Override
public void run() {
Intent start=new Intent(MainActivity.this,TimeService.class);
start.putExtra("StopSig",false);
start.putExtra("Temps_travail",mTempsTravail); //Envoi Long
start.putExtra("mSmallest",mSmallest); //Envoi Long
start.putExtra("file_src",src); //Envoi String
start.putExtra("file_dst",dst); //Envoi String
start.putExtra("Debut",sDebut); //Envoi String
start.putExtra("Fin",sFin); //Envoi String
start.putExtra("Name",sName);//Envoi String
start.putExtra("String_travail",sTempsTravail); //Envoi String
start.putExtra("Smallest",sSmallest);//Envoi String*/
startService(start);
}
}).start();
}
});
mTimeListenerF.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) { //Button to set the End Time
setFinTime();
checkEnabled();
new Thread(new Runnable() {
#Override
public void run() {
Intent end = new Intent(MainActivity.this,TimeService.class);
end.putExtra("StopSig",true);
end.putExtra("TimeF",mTimeF);
end.putExtra("Temps_travail",mTempsTravail); //Envoi Long
end.putExtra("mSmallest",mSmallest); //Envoi Long
end.putExtra("file_src",src); //Envoi String
end.putExtra("file_dst",dst); //Envoi String
end.putExtra("Debut",sDebut); //Envoi String
end.putExtra("Fin",sFin); //Envoi String
end.putExtra("Name",sName);//Envoi String
end.putExtra("String_travail",sTempsTravail); //Envoi String
end.putExtra("Smallest",sSmallest);
// startService(end);
stopService(end);
}
}).start();
}
});
mMail.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
sendMail();
}
}); //Send data by mail
Here is the service i use, with the method that show the notification:
public class TimeService extends Service {
#Override
public void onCreate() {
Log.i(TAG, "Service onCreate");
mContext=TimeService.this;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Service onStartCommand");
Toast.makeText(this,"TimeServiceStarted",Toast.LENGTH_LONG).show();
stopSig=intent.getBooleanExtra("StopSig",false); //Receiving Boolean
setDebutTime();
setNotification();
//Stop service once it finishes its task
//stopSelf();
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent arg0) {
Log.i(TAG, "Service onBind");
TSTempsTravail=arg0.getLongExtra("Temps_travail",TSTempsTravail); //Receiving Long
TSSmallest=arg0.getLongExtra("mSmallest",TSSmallest);//Receiving Long
sName=arg0.getStringExtra("Name");//Receiving String
sDebut= arg0.getStringExtra("Debut");//Receiving String
sFin= arg0.getStringExtra("Fin");//Receiving String
sTempsTravail= arg0.getStringExtra("String_travail");//Receiving String
sSmallest=arg0.getStringExtra("Smallest");//Receiving String
return null;
}
#Override
public void onDestroy() {
stopSig=true;
setFinTime();
// getName();
getHeureSupp(TimeD,TimeF);
Clock();
writeToFile(sDebut,sFin,sName,sTempsTravail);
try {
copyFile(src,dst);
} catch (IOException e) {
e.printStackTrace();
}
//sendMail();
Log.i(TAG, "Service onDestroy");
//Toast.makeText(this,"Service stopped",Toast.LENGTH_LONG).show();
}
public void setNotification() {
Intent notificationIntent = new Intent(this, MainActivity.class);
/*notificationIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);*/
PendingIntent pendingIntent = PendingIntent.getActivity(this, 15, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Bitmap icon = BitmapFactory.decodeResource(getResources(),
R.drawable.logo);
NotificationCompat.Builder notification = (NotificationCompat.Builder) new NotificationCompat.Builder(this)
.setContentTitle("myTitle")
.setTicker("myTicker")
.setContentText("myText")
.setSmallIcon(R.drawable.logo_small)
.setLargeIcon(Bitmap.createScaledBitmap(icon, 128, 128, false))
.setContentIntent(pendingIntent)
.setOngoing(true);
startForeground(15, notification.build());
}
And finally here is the Manifest if that can be useful in anyway :
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.benjii.myapp">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<application
android:allowBackup="true"
android:icon="#drawable/logo_small"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".TimeService"
android:icon="#drawable/logo_small"
android:label="#string/service">
</service>
</application>
</manifest>

Application components (services, activities, etc) always run in main thread, no matter what thread they are started from. Consider starting thread in your Service instead, or use an IntentService.

Related

how to unlock phone from service [duplicate]

I am doing an application which Locks the screen on shake. Now it is locking and from there it going to a broadcast receiver from there if the screen is off its entering into a service which has to turn the screen on.
Below is the broadcast receiver:
public class ScreenReceiver extends BroadcastReceiver {
public static boolean wasScreenOn = true;
#Override
public void onReceive(Context context, Intent intent) {
System.out.println("Entered Broadcaste Reciever");
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
// DO WHATEVER YOU NEED TO DO HERE
System.out.println("SCREEN_OFF"+wasScreenOn);
wasScreenOn = false;
Intent i = new Intent(context, UpdateService.class);
i.putExtra("screen_state", wasScreenOn);
context.startService(i);
System.out.println("jrkejhr keh");
}
else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
// AND DO WHATEVER YOU NEED TO DO HERE
wasScreenOn = true;
System.out.println("SCREEN_ON"+wasScreenOn);
}
}
And its entering to a service where i had written the intent action to go home is...
ShakeListener mShaker;
int amountOfTime = 0;
Context context1;
#Override
public void onCreate() {
super.onCreate();
// REGISTER RECEIVER THAT HANDLES SCREEN ON AND SCREEN OFF LOGIC
System.out.println("Enterd Service");
final Vibrator vibe = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
mShaker = new ShakeListener(this);
mShaker.setOnShakeListener(new ShakeListener.OnShakeListener () {
public void onShake() {
vibe.vibrate(100);
Intent goHome = new Intent();
goHome.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
goHome.setAction("android.intent.action.MAIN");
goHome.addCategory("android.intent.category.HOME");
startActivity(goHome);
}
});
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
It is entering into the service. But home screen is not displaying. When the service is invoked the the screen is locked.
Edit:
As some folks needs help in Unlocking device after locking programmatically,
I came through post Android screen lock/ unlock programatically, please have look, may help you.
Original Answer was:
You need to get Admin permission and you can lock phone screen
please check below simple tutorial to achive this one
Lock Phone Screen Programmtically
also here is the code example..
LockScreenActivity.java
public class LockScreenActivity extends Activity implements OnClickListener {
private Button lock;
private Button disable;
private Button enable;
static final int RESULT_ENABLE = 1;
DevicePolicyManager deviceManger;
ActivityManager activityManager;
ComponentName compName;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
deviceManger = (DevicePolicyManager)getSystemService(
Context.DEVICE_POLICY_SERVICE);
activityManager = (ActivityManager)getSystemService(
Context.ACTIVITY_SERVICE);
compName = new ComponentName(this, MyAdmin.class);
setContentView(R.layout.main);
lock =(Button)findViewById(R.id.lock);
lock.setOnClickListener(this);
disable = (Button)findViewById(R.id.btnDisable);
enable =(Button)findViewById(R.id.btnEnable);
disable.setOnClickListener(this);
enable.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if(v == lock){
boolean active = deviceManger.isAdminActive(compName);
if (active) {
deviceManger.lockNow();
}
}
if(v == enable){
Intent intent = new Intent(DevicePolicyManager
.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
compName);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,
"Additional text explaining why this needs to be added.");
startActivityForResult(intent, RESULT_ENABLE);
}
if(v == disable){
deviceManger.removeActiveAdmin(compName);
updateButtonStates();
}
}
private void updateButtonStates() {
boolean active = deviceManger.isAdminActive(compName);
if (active) {
enable.setEnabled(false);
disable.setEnabled(true);
} else {
enable.setEnabled(true);
disable.setEnabled(false);
}
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case RESULT_ENABLE:
if (resultCode == Activity.RESULT_OK) {
Log.i("DeviceAdminSample", "Admin enabled!");
} else {
Log.i("DeviceAdminSample", "Admin enable FAILED!");
}
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
}
MyAdmin.java
public class MyAdmin extends DeviceAdminReceiver{
static SharedPreferences getSamplePreferences(Context context) {
return context.getSharedPreferences(
DeviceAdminReceiver.class.getName(), 0);
}
static String PREF_PASSWORD_QUALITY = "password_quality";
static String PREF_PASSWORD_LENGTH = "password_length";
static String PREF_MAX_FAILED_PW = "max_failed_pw";
void showToast(Context context, CharSequence msg) {
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
#Override
public void onEnabled(Context context, Intent intent) {
showToast(context, "Sample Device Admin: enabled");
}
#Override
public CharSequence onDisableRequested(Context context, Intent intent) {
return "This is an optional message to warn the user about disabling.";
}
#Override
public void onDisabled(Context context, Intent intent) {
showToast(context, "Sample Device Admin: disabled");
}
#Override
public void onPasswordChanged(Context context, Intent intent) {
showToast(context, "Sample Device Admin: pw changed");
}
#Override
public void onPasswordFailed(Context context, Intent intent) {
showToast(context, "Sample Device Admin: pw failed");
}
#Override
public void onPasswordSucceeded(Context context, Intent intent) {
showToast(context, "Sample Device Admin: pw succeeded");
}
}
The androidmanifest.xml and policies.xml files on the sample page are invisible in my browser due to it trying to format the XML files as HTML. I'm only posting this for reference for the convenience of others, this is sourced from the sample page.
Thanks all for this helpful question!
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.kns"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="8" />
<application android:icon="#drawable/icon" android:label="#string/app_name">
<activity android:name=".LockScreenActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".MyAdmin"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data android:name="android.app.device_admin"
android:resource="#xml/policies" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
</application>
</manifest>
policies.xml
<?xml version="1.0" encoding="utf-8"?>
<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
<uses-policies>
<limit-password />
<watch-login />
<reset-password />
<force-lock />
<wipe-data />
</uses-policies>
</device-admin>
Use Activity.getWindow() to get the window of your activity; use Window.addFlags() to add whichever of the following flags in WindowManager.LayoutParams that you desire:
FLAG_DISMISS_KEYGUARD
FLAG_SHOW_WHEN_LOCKED
FLAG_TURN_SCREEN_ON

BroadcastReceiver for SCREEN_ON and SCREEN_OFF even after quitting the app

I'm trying to make an app that monitors the users phone usage by tracking time of screen lock and unlock. I tried to setup a BroadcastReceiver which works fine when the app is running the background. But won't work when I close the app. Is there a solution for this.
The code I'm using now is as follows :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, ScreenListenerService.class);
startService(intent);
}
}
ScreenListenerService class is as follows..
public class ScreenListenerService extends Service {
private BroadcastReceiver mScreenStateBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
// Save something to the server
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
// Save something to the server
}
}
};
#Override
public void onCreate() {
super.onCreate();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(mScreenStateBroadcastReceiver, intentFilter);
}
#Override
public void onDestroy() {
unregisterReceiver(mScreenStateBroadcastReceiver);
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
My AndroidManifest file is as follows :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.abbinvarghese.calculu">
<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">
<service android:name=".ScreenListenerService" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
To overcome the imposed limitations of 8.0 you could run a foreground service. Just like a service but a notification is posted to the foreground.
Then the service code would be like this (remember to unregister the receiver onDestory):
BroadcastReceiver screenReceiver;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startRunningInForeground();
detectingDeterminateOfServiceCall(intent.getExtras());
registerBroadcastReceivers();
return START_STICKY;
}
private void startRunningInForeground() {
//if more than or equal to 26
if (Build.VERSION.SDK_INT >= 26) {
//if more than 26
if(Build.VERSION.SDK_INT > 26){
String CHANNEL_ONE_ID = "sensor.example. geyerk1.inspect.screenservice";
String CHANNEL_ONE_NAME = "Screen service";
NotificationChannel notificationChannel = null;
notificationChannel = new NotificationChannel(CHANNEL_ONE_ID,
CHANNEL_ONE_NAME, NotificationManager.IMPORTANCE_MIN);
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.setShowBadge(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (manager != null) {
manager.createNotificationChannel(notificationChannel);
}
Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.background_running);
Notification notification = new Notification.Builder(getApplicationContext())
.setChannelId(CHANNEL_ONE_ID)
.setContentTitle("Recording data")
.setContentText("ActivityLog is logging data")
.setSmallIcon(R.drawable.background_running)
.setLargeIcon(icon)
.build();
Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
notification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0);
startForeground(101, notification);
}
//if version 26
else{
startForeground(101, updateNotification());
}
}
//if less than version 26
else{
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("Activity logger")
.setContentText("data recording on going")
.setSmallIcon(R.drawable.background_running)
.setOngoing(true).build();
startForeground(101, notification);
}
}
private Notification updateNotification() {
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, MainActivity.class), 0);
return new NotificationCompat.Builder(this)
.setContentTitle("Activity log")
.setTicker("Ticker")
.setContentText("recording of data is on going")
.setSmallIcon(R.drawable.activity_log_icon)
.setContentIntent(pendingIntent)
.setOngoing(true).build();
}
private void detectingDeterminateOfServiceCall(Bundle b) {
if(b != null){
Log.i("screenService", "bundle not null");
if(b.getBoolean("phone restarted")){
storeInternally("Phone restarted");
}
}else{
Log.i("screenService", " bundle equals null");
}
documentServiceStart();
}
private void documentServiceStart() {
Log.i("screenService", "started running");
}
private void registerBroadcastReceivers() {
screenReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
switch (Objects.requireNonNull(intent.getAction())){
case Intent.ACTION_SCREEN_ON:
//or do something else
storeInternally("Screen on");
break;
case Intent.ACTION_SCREEN_OFF:
//or do something else
storeInternally("Screen off");
break;
}
}
};
IntentFilter screenFilter = new IntentFilter();
screenFilter.addAction(Intent.ACTION_SCREEN_ON);
screenFilter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(screenReceiver, screenFilter);
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(screenReceiver);
}
and call it from the main activity:
private void startServiceRunning() {
if(!isMyServiceRunning(Background.class)){
if(Build.VERSION.SDK_INT >25){
startForegroundService(new Intent(this, Background.class));
}else{
startService(new Intent(this, Background.class));
}
}
}
As Background Execution Limit imposes on Android 8.0 (API level 26) so now it's not possible to listen SCREEN_OFF and SCREEN_ON action in background by running the service.
I have found a work around for same with the help of JobScheduler which works fine for listen broadcast in background without running any service.
Please check on this: Screen OFF/ON broadcast listener without service on Android Oreo
Instead of creating a new service for broadcast receiver, you can directly create a broadcast receiver class that will listen to system broadcasts even when the app is not running.
Create a new class which extends BroadcastReceiver.
public class YourReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Do your stuff
}
}
And register it in manifest.
<receiver
android:name=".YourReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.ACTION_SCREEN_ON" />
<action android:name="android.intent.action. ACTION_SCREEN_OFF" />
<category android:name="android.intent.category.DEFAUL" />
</intent-filter>
</receiver>
Read about Manifest-declared receivers here.
Above solution won't work, here is the reason why. Problem is that your service is getting killed when the app is killed, so your receiver instance is removed from memory. Here is a little trick to re-start the service in background. Add the following code to your service.
#Override
public void onTaskRemoved(Intent rootIntent){
Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
restartServiceIntent.setPackage(getPackageName());
PendingIntent restartServicePendingIntent = PendingIntent.getService(getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000,
restartServicePendingIntent);
super.onTaskRemoved(rootIntent);
}
Although this is not the right way to do it. Also in Android 26+ you won't be able to do this and you'd go for foreground service. https://developer.android.com/about/versions/oreo/background

Is sendBroadcast() the best way to keep a WakefulBroadcastReceiver alive in Android?

I try do do some background calculation tasks in an Android application.
My Main class :
public class MainActivity extends AppCompatActivity {
private CalculationReceiver calculationReceiver = new CalculationReceiver();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
final Context mContext = this;
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
calculationReceiver.doAddition(mContext, 2, 2);
}
});
}
}
My service :
public class CalculationService extends IntentService {
public CalculationService() {
super("Calculation Service");
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
int nb1 = intent.getIntExtra(NUMBER_1,0);
int nb2 = intent.getIntExtra(NUMBER_2,0);
doAddition(nb1,nb2);
CalculationReceiver.completeWakefulIntent(intent);
}
public void doAddition(int number1, int number2){
int result = number1+number2;
System.out.println("Result : " + result);
}
}
My receiver :
public class CalculationReceiver extends WakefulBroadcastReceiver {
public static final String NUMBER_1 = "NUMBER_1";
public static final String NUMBER_2 = "NUMBER_2";
#Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, CalculationService.class);
int receiverNumber1 = intent.getIntExtra(NUMBER_1,0);
int receiverNumber2 = intent.getIntExtra(NUMBER_2,0);
service.putExtra(NUMBER_1,receiverNumber1);
service.putExtra(NUMBER_2,receiverNumber2);
startWakefulService(context, service);
}
public void doAddition (Context context, int number1, int number2){
Intent intent = new Intent(context, CalculationReceiver.class);
intent.putExtra(NUMBER_1,number1);
intent.putExtra(NUMBER_2,number2);
context.sendBroadcast(intent);
}
}
My Manifest :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.testservices">
<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission>
<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=".ReductionService"
android:enabled="true" />
<receiver android:name=".ReductionReceiver"/>
<service android:name=".CalculationService"
android:enabled="true" />
<receiver android:name=".CalculationReceiver"/>
</application>
</manifest>
The calculations of the application are more complex than these additions, and can take several minutes (in average 15 minutes) to be done.
According to the Google documentation (https://developer.android.com/training/scheduling/wakelock.html), I decided to implement this architecture to make sure that the calculation is done to the end.
The idea is that the user starts his calculation and then waits for the application to give the result. In the meantime, he can launch other apps or lock his phone, the calculation must not stop.
This approach seems to work.
What bothers me here is the call to service in the receiver:
context.sendBroadcast (intent);
Is there a more "clean" way to start the service?
What strikes me is that it does not seem very "clean", especially the passage of several times the same parameter (number1 and number2)
Thanks
According to the Google documentation (https://developer.android.com/training/scheduling/wakelock.html), I decided to implement this architecture to make sure that the calculation is done to the end.
That is not how the documentation shows using WakefulBroadcastReceiver. Plus, WakefulBroadcastReceiver was deprecated in version 26.0.0 of the support libraries.
The idea is that the user starts his calculation and then waits for the application to give the result.
My interpretation of this is that the user is requesting, through your activity's UI, to start the calculation. This means that at this point in time, the screen is on and you have an activity in the foreground.
Is there a more "clean" way to start the service?
Call startService().
Step #1: Delete your use of the deprecated WakefulBroadcastReceiver
Step #2: Have your activity call startService() to start the service
Step #3: Have your service acquire a partial WakeLock through the PowerManager system service, in the service's onCreate() method
Step #4: Have your service release that WakeLock in the service's onDestroy() method
Step #5: Modify the service to be a foreground service, calling startForeground() in onCreate() with a suitable Notification to allow the user to control the behavior of the service
Note that:
If you skip Step #5, your service will stop running after ~1 minute on Android 8.0+.
This will still not work on Android 6.0+ if the device enters into Doze mode. That should not happen for ~1 hour, but you need to make sure that your calculations are done by then.
Consider offloading the calculation work to a server, rather than burning up the user's CPU for an extended period of time (through your calculation work plus the wakelock)
It was in this way that I resolved it, following the advice of #CommonsWare
Main Activity :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
final Context mContext = this;
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent service = new Intent(mContext, CalculationService.class);
service.putExtra(NUMBER_1,2);
service.putExtra(NUMBER_2,2);
startService(service);
}
});
}
}
Calculation Service :
public class CalculationService extends IntentService {
public static final String NUMBER_1 = "NUMBER_1";
public static final String NUMBER_2 = "NUMBER_2";
private static final int FOREGROUND_ID = 42;
PowerManager.WakeLock wakeLock;
public CalculationService() {
super("Calculation Service");
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
int nb1 = intent.getIntExtra(NUMBER_1,0);
int nb2 = intent.getIntExtra(NUMBER_2,0);
doAddition(nb1,nb2);
}
public void doAddition(int number1, int number2){
int result = number1+number2;
System.out.println("Result : " + result);
}
#Override
public void onCreate() {
super.onCreate();
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"CalculationServiceWakelockTag");
wakeLock.acquire();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Calculation App")
.setContentText("Calculation in progress")
.setContentIntent(pendingIntent).build();
startForeground(FOREGROUND_ID, notification);
}
#Override
public void onDestroy() {
super.onDestroy();
wakeLock.release();
}
}
This is working great :)
But can I call
startForegroundService(service)
instead of
startService(service);
or not ? And why ?

Broadcast receiver register in on create of class extend from application

I'm working on app which test state on server every 15 min and push notification , i used Alarm Manager , broadcast receiver & Intent Service .
every thing worked fine and i get this state from server perfectly when app is running or in background , until i removed it from recent apps, every thing stops and can't get that state from server.
I searched ... and get nothing , but my friend tell me that I must register my broadcast receiver in on create of class extend from application.
I don't know how to do this .. so I need help please
Main Activity Class
public class MainActivity extends AppCompatActivity {
static TextView TvText;
Button Btn11, Btn22;
AlarmManager alarm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
alarm = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
TvText = (TextView) findViewById(R.id.tv_Text);
Btn11 = (Button) findViewById(R.id.btn_11);
Btn22 = (Button) findViewById(R.id.btn_22);
Btn22.setEnabled(false);
}
public void Btn11OC(View view) {
scheduleAlarm();
Btn11.setEnabled(false);
Btn22.setEnabled(true);
}
public void Btn22OC(View view) {
if (alarm!= null) {
cancelAlarm();
}
Btn11.setEnabled(true);
Btn22.setEnabled(false);
}
// Setup a recurring alarm every half hour
public void scheduleAlarm() {
// Construct an intent that will execute the AlarmReceiver
Intent intent = new Intent(getApplicationContext(), broadtest.class);
intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
// Create a PendingIntent to be triggered when the alarm goes off
final PendingIntent pIntent = PendingIntent.getBroadcast(this, broadtest.REQUEST_CODE,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Setup periodic alarm every 5 seconds
alarm.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(),
900000L, pIntent);
}
public void cancelAlarm() {
Intent intent = new Intent(getApplicationContext(), broadtest.class);
intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
final PendingIntent pIntent = PendingIntent.getBroadcast(this, broadtest.REQUEST_CODE,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarm.cancel(pIntent);
}
}
Broad Cast Receiver
public class broadtest extends WakefulBroadcastReceiver {
public static final int REQUEST_CODE = 12345;
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, MyService.class);
context.startService(i);
}
}
AppController Class
public class AppController extends Application {
#Override
public void onCreate() {
super.onCreate();
}
}
MyService class
public class MyService extends IntentService {
static int NOTIFICATION_ID = 0;
public MyService() {
super("MyService");
}
#Override
protected void onHandleIntent(Intent intent) {
String url = "http://test.com/testts.php";
// Tag used to cancel the request
String tag_string_req = "string_req";
StringRequest strReq = new StringRequest(Request.Method.GET,
url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.d("Volley Log", response);
Toast.makeText(MyService.this, response, Toast.LENGTH_SHORT).show();
if (response.equals("0")){
sendNotification("Titel Test 1111", "Body Test 1111");
}else if (response.equals("1")){
sendNotification("Titel Test 2222", "Body Test 2222");
}else {
sendNotification("Titel Test 3333", "Body Test 3333");
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(MyService.this, error.toString(), Toast.LENGTH_SHORT).show();
VolleyLog.d("Volley Log", "Error: " + error.getMessage());
}
});
// Adding request to request queue
int socketTimeout = 30000;//30 seconds - change to what you want
RetryPolicy policy = new DefaultRetryPolicy(socketTimeout,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
strReq.setRetryPolicy(policy);
AppController.getInstance().addToRequestQueue(strReq, tag_string_req);
// Setup periodic alarm every 5 seconds
}
private void sendNotification(String title, String messageBody) {
long[] pattern = {500,500,500,500,500,500,500,500,500};
Uri alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(title)
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(alarmSound)
.setLights(Color.BLUE, 500, 500)
.setVibrate(pattern);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (NOTIFICATION_ID > 1073741824) {
NOTIFICATION_ID = 0;
}
notificationManager.notify(NOTIFICATION_ID++, notificationBuilder.build());
}
}
Manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.gih.testmass">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:name=".AppController"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
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>
<receiver
android:name=".broadtest"
android:process=":remote">
</receiver>
<service
android:name=".MyService"
android:exported="false">
</service>
</application>
You need to start the service as a foreground service. When you clear app from recents it kills the service
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.drawable.ic_notification)
.setLargeIcon(BitmapFactory.decodeResource(getApplicationContext().getResources(),
R.mipmap.ic_launcher))
.setContentTitle("WhatsApp Reminder Service.")
.setContentText("Touch to configure.");
Intent startIntent = new Intent(getApplicationContext(), MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 965778, startIntent, 0);
builder.setContentIntent(pendingIntent);
startForeground(965778, builder.build());
return START_REDELIVER_INTENT;
}
It is necessary to build a notification when you use foreground service.
Hope it helps.
I see you have used IntentService
see answer to this question
Using startForeground() with an Intent Service
To run a process on a device when users are not particularly interacting with your application.
The steps will involve:
1.Updating your android manifest xml
2.Setting up broadcast receivers to listen to relevant events
3.Set up a background service for context when your application isn’t running
Android menifest.xml
<?xml version="1.0" encoding="utf-8"?>
<uses-permission ... />
<application
android:name=".MyApplication"
... >
<receiver android:name=".receivers.PeriodicTaskReceiver">
<intent-filter>
<action android:name="com.example.app.PERIODIC_TASK_HEART_BEAT" />
</intent-filter>
</receiver>
<service android:name=".services.BackgroundService" />
...
</application>
Now for the Broadcast receiver
public class PeriodicTaskReceiver extends BroadcastReceiver {
private static final String TAG = "PeriodicTaskReceiver";
private static final String INTENT_ACTION = "com.example.app.PERIODIC_TASK_HEART_BEAT";
#Override
public void onReceive(Context context, Intent intent) {
if (!Strings.isNullOrEmpty(intent.getAction())) {
MyApplication myApplication = (MyApplication) context.getApplicationContext();
SharedPreferences sharedPreferences = myApplication.getSharedPreferences();
if (intent.getAction().equals("android.intent.action.BATTERY_LOW")) {
sharedPreferences.edit().putBoolean(Constants.BACKGROUND_SERVICE_BATTERY_CONTROL, false).apply();
stopPeriodicTaskHeartBeat(context);
} else if (intent.getAction().equals("android.intent.action.BATTERY_OKAY")) {
sharedPreferences.edit().putBoolean(Constants.BACKGROUND_SERVICE_BATTERY_CONTROL, true).apply();
restartPeriodicTaskHeartBeat(context, myApplication);
} else if (intent.getAction().equals(INTENT_ACTION)) {
doPeriodicTask(context, myApplication);
}
}
}
private void doPeriodicTask(Context context, MyApplication myApplication) {
// Periodic task(s) go here ...
}
public void restartPeriodicTaskHeartBeat(Context context, MyApplication myApplication) {
SharedPreferences sharedPreferences = myApplication.getSharedPreferences();
boolean isBatteryOk = sharedPreferences.getBoolean(Constants.BACKGROUND_SERVICE_BATTERY_CONTROL, true);
Intent alarmIntent = new Intent(context, PeriodicTaskReceiver.class);
boolean isAlarmUp = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntent.FLAG_NO_CREATE) != null;
if (isBatteryOk && !isAlarmUp) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmIntent.setAction(INTENT_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, 0);
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), AlarmManager.INTERVAL_FIFTEEN_MINUTES, pendingIntent);
}
}
public void stopPeriodicTaskHeartBeat(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent alarmIntent = new Intent(context, PeriodicTaskReceiver.class);
alarmIntent.setAction(INTENT_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, 0);
alarmManager.cancel(pendingIntent);
}
}
here com.example.app.PERIODIC_TASK_HEART_BEAT is application’s own broadcast, created and sent from our restartPeriodicTaskHeartBeat method.
your Alarmmanager should have this line
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), AlarmManager.INTERVAL_FIFTEEN_MINUTES, pendingIntent);
Now for your background service class:
public class BackgroundService extends Service {
private static final String TAG = "BackgroundService";
PeriodicTaskReceiver mPeriodicTaskReceiver = new PeriodicTaskReceiver();
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
MyApplication myApplication = (MyApplication) getApplicationContext();
SharedPreferences sharedPreferences = myApplication.getSharedPreferences();
IntentFilter batteryStatusIntentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatusIntent = registerReceiver(null, batteryStatusIntentFilter);
if (batteryStatusIntent != null) {
int level = batteryStatusIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryStatusIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float batteryPercentage = level / (float) scale;
float lowBatteryPercentageLevel = 0.14f;
try {
int lowBatteryLevel = Resources.getSystem().getInteger(Resources.getSystem().getIdentifier("config_lowBatteryWarningLevel", "integer", "android"));
lowBatteryPercentageLevel = lowBatteryLevel / (float) scale;
} catch (Resources.NotFoundException e) {
Log.e(TAG, "Missing low battery threshold resource");
}
sharedPreferences.edit().putBoolean(Constants.BACKGROUND_SERVICE_BATTERY_CONTROL, batteryPercentage >= lowBatteryPercentageLevel).apply();
} else {
sharedPreferences.edit().putBoolean(Constants.BACKGROUND_SERVICE_BATTERY_CONTROL, true).apply();
}
mPeriodicTaskReceiver.restartPeriodicTaskHeartBeat(BackgroundService.this);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
startSelf();
}
}
Here the Backgroundservice tries to find the device’s low battery threshold and set our battery control flag appropriately before it attempts to restart the Broadcast receiver.
And START_STICKY will try to re-create your service after it is killed and call onStartCommand() again with a null intent.
Finally for your Application class start the Background service:
public class MyApplication extends Application {
private static final String TAG = "MyApplication";
#Override
public void onCreate() {
super.onCreate();
// Initialize the singletons so their instances
// are bound to the application process.
...
Intent startServiceIntent = new Intent(context, BackgroundService.class);
startService(startServiceIntent);
}
}
For detail implementation see this:https://technology.jana.com/2014/10/28/periodic-background-tasks-in-android/
be sure to make you service like this in your Mainifest
<service
android:name=".service.youservice"
android:exported="true"
android:process=":ServiceProcess" />
then your service will run on other process named ServiceProcess
if you want make your service never die :
onStartCommand() return START_STICKY
onDestroy() -> call startself
if nothing works use startForeground() service..

Simple service won't bind : bindService return false, service connection never triggered

This is my first time using Service in Android and my service won't bind.
The Service code :
package mackosoft.almightymessage;
public final class MainService extends Service {
private final static String TAG = "Main Service";
private final IBinder binder = new MainServiceBinder();
#Override
public final void onCreate() {
super.onCreate();
Log.d(TAG, "Service created");
Toast.makeText(this, "Service created", Toast.LENGTH_SHORT).show();
}
#Override
public final int onStartCommand(final Intent intent, final int flags, final int startId) {
return START_STICKY;
}
#Override
public final void onDestroy() {
super.onDestroy();
Log.d(TAG, "Service created");
Toast.makeText(this, "Service destroyed", Toast.LENGTH_SHORT).show();
}
#Override
public final IBinder onBind(final Intent intent) {
Toast.makeText(this, "Service binded", Toast.LENGTH_SHORT).show();
return this.binder;
}
public final class MainServiceBinder extends Binder {
final MainService getService() {
return MainService.this;
}
}
}
Manifest file :
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="mackosoft.almightymessage" >
<application
android:allowBackup="true"
android:icon="#drawable/ic_logo"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden"
android:icon="#drawable/ic_logo"
android:label="#string/app_name"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".MainService"
android:enabled="true" >
</service>
</Application
</manifest>
And finally the part in MainActivity where I try to bind the service :
public final class MainActivity extends AppCompatActivity {
#Override
protected final void onStart() {
super.onStart();
final Intent intent = new Intent(MainActivity.this, MainService.MainServiceBinder.class);
final boolean status = bindService(intent, this.connection, Context.BIND_AUTO_CREATE);
Log.d(TAG, "Service binded ? " + status);
}
private final ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.d(TAG, "Service connected !");
service = ((MainService.MainServiceBinder) iBinder).getService(); // service is a private member of MainActivity
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
Log.d(TAG, "Service disconnected !");
}
};
}
Running the application, Logcat shows :
mackosoft.almightymessage D/MainActivity﹕ Service binded ? false
And Service Connection is NEVER triggered !
I tried multiple solution :
Moving the binding call to a Button.onClick() and using getApplicationContext()
Using directly getApplicationContext()
In the Manifest, changed -> android:name="mackosoft.almightymessage.MainService"
Changed also the Intent-> final Intent intent = new Intent(this, MainService.MainServiceBinder.class);
Also tried to add this.startService(intent) and with this.getApplicationContext()
All of the above fail and no error in the logs !
I have no luck at all.
Test device : Samsung Galaxy Note 3 running Cyanogen Mode 12.1 (Android 5.1)
Android Studio 1.2.1.1 (stable)
Thanks for the help !!
final Intent intent = new Intent(MainActivity.this, MainService.MainServiceBinder.class);
Should be
final Intent intent = new Intent(MainActivity.this, MainService.class);
See the example provided in the docs for further info

Categories