I am making an android app that can send notifications even if the app is closed. So I added a NotificationService to call when the app is destroyed. But I get an error: System services not available to Activities before onCreate() when I force the app to stop.
This is the MainActivity.java:
public class MainActivity extends AppCompatActivity {
private Button button1;
private Button button2;
private Button button3;
private Button button4;
private static final String CHANNEL_ID = "telus";
private static final String CHANNEL_NAME = "telus app";
private static final String CHANNEL_DESC = "telus app notification";
private int count = 1;
Timer timer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button1=(Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openActivity_web();
}
});
button2=(Button) findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openActivationPage();
}
});
button3=(Button) findViewById(R.id.button3);
button3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openMysql();
}
});
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription(CHANNEL_DESC);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
findViewById(R.id.button4).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
displayNotification();
}
});
class sendnotif extends TimerTask {
public void run() {
displayNotification();
}
}
Timer timer = new Timer();
timer.schedule(new sendnotif(), 0, 5000);
}
public void displayNotification(){
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_alarm)
.setContentTitle("Titre de la notification")
.setContentText("C'est le text de la notification")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
;
NotificationManagerCompat mNotifMgr = NotificationManagerCompat.from(this);
mNotifMgr.notify( 1, mBuilder.build());
}
public void openActivity_web(){
Intent intent = new Intent(this, WebPage.class);
startActivity(intent);
}
public void openActivationPage(){
Intent intent = new Intent(this, ActivationPage.class);
startActivity(intent);
}
public void openMysql(){
Intent intent = new Intent(this, mysql_display.class);
startActivity(intent);
}
protected void onDestroy(){
super.onDestroy();
startService(new Intent(this, NotificationService.class));
}
}
and this is the NotificationService.java:
public class NotificationService extends Service {
Timer timer;
TimerTask timerTask;
String TAG = "Timers";
int Your_X_SECS = 5;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand");
super.onStartCommand(intent, flags, startId);
startTimer();
return START_STICKY;
}
#Override
public void onCreate() {
Log.e(TAG, "onCreate");
}
#Override
public void onDestroy() {
Log.e(TAG, "onDestroy");
stoptimertask();
super.onDestroy();
}
//we are going to use a handler to be able to run in our TimerTask
final Handler handler = new Handler();
public void startTimer() {
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, after the first 5000ms the TimerTask will run every 10000ms
timer.schedule(timerTask, 5000, Your_X_SECS * 1000); //
//timer.schedule(timerTask, 5000,1000); //
}
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
//use a handler to run a toast that shows the current timestamp
handler.post(new Runnable() {
public void run() {
MainActivity notif = new MainActivity();
notif.displayNotification();
}
});
}
};
}
}
This is the error log when I try to manually stop the app:
2019 - 07 - 16 20: 10: 23.079 23334 - 23334 / com.example.testapp E / AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.testapp, PID: 23334
java.lang.IllegalStateException: System services not available to Activities before onCreate()
at android.app.Activity.getSystemService(Activity.java: 5581)
at androidx.core.app.NotificationManagerCompat. < init > (NotificationManagerCompat.java: 158)
at androidx.core.app.NotificationManagerCompat.from(NotificationManagerCompat.java: 153)
at com.example.testapp.MainActivity.displayNotification(MainActivity.java: 103)
at com.example.testapp.NotificationService$1$1.run(NotificationService.java: 89)
at android.os.Handler.handleCallback(Handler.java: 751)
at android.os.Handler.dispatchMessage(Handler.java: 95)
at android.os.Looper.loop(Looper.java: 154)
at android.app.ActivityThread.main(ActivityThread.java: 6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java: 866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java: 756)
Thank you for your help.
Related
Purpose of program: I'm trying to make an app that will count how many times the user checked their phone by issuing a broadcast for Intent.ACTION_SCREEN_ON. it then increments a counter and updates the activity with the new counter.
The problem: This all works just fine but as soon as I swipe away the application from the apps tray, the counter goes back to zero.
obviously what is supposed to happen is the counter would continue.
I tried saving the counter value in the service onDestroy and then calling it again onCreate but onDestroy is never called.
Note that in the onCreate() for the activity it sends a broadcast to the service asking for the most recent value of counter and then updates it in the view. I couldn't find a better way to keep them in sync.
CounterService.java
public class CounterService extends Service {
public static boolean RERUN = true;
private int counter = 0;
private SharedPreferences SP;
private BroadcastReceiver mScreenStateBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
counter++;
System.out.println("************************************* \n \n " + counter);
}
sendCounterBroadcast();
}
};
public void sendCounterBroadcast() {
Intent i = new Intent();
i.setAction("com.inc.count");
i.putExtra("counterValue", counter);
sendBroadcast(i);
}
#Override
public void onCreate() {
super.onCreate();
System.out.println("********************** CounterService.onCreate()");
// get counter value from SP -- this is useful for when the service gets recreated
SP = getSharedPreferences("Counter Service Data", MODE_PRIVATE);
counter = SP.getInt("counter", 0);
// wait for screen to be turned on or for the activity to ask you for the counter number
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction("send.counter.to.phonecounteractivity");
registerReceiver(mScreenStateBroadcastReceiver, intentFilter);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
System.out.println("***************************************** CounterService.OnDestroy()");
unregisterReceiver(mScreenStateBroadcastReceiver);
// Save counter value for when we restart service
SP = getSharedPreferences("Counter Service Data", MODE_PRIVATE);
SharedPreferences.Editor SPE = SP.edit();
if (RERUN) {
SPE.putInt("counter", counter);
System.out.println("******************************** RESTARTING SERVICE ");
startService(new Intent(getApplicationContext(), CounterService.class));
} else
SPE.putInt("counter", 0);
SPE.apply();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
PhoneCheckerCounter.Java
public class PhoneCheckerCounter extends AppCompatActivity {
private BroadcastReceiver changeCount;
private IntentFilter filter;
private int counter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_phone_checker_counter);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
switcherOnClick();
assignValuesOnCreate();
System.out.println("**************************** onCreate()");
changeCounterText();
}
public void switcherOnClick() {
final Switch sCounter = findViewById(R.id.switchCounter);
sCounter.setOnClickListener(new View.OnClickListener() {
Intent intent = new Intent(getApplicationContext(), CounterService.class);
#Override
public void onClick(View v) {
if (sCounter.isChecked()) {
startService(intent);
CounterService.RERUN = true;
v.getContext().registerReceiver(changeCount, filter);
Toast.makeText(getApplicationContext(), "Counting has begun", Toast.LENGTH_SHORT).show();
} else {
TextView n = findViewById(R.id.counter);
n.setText("0");
CounterService.RERUN = false;
v.getContext().unregisterReceiver(changeCount);
stopService(intent);
Toast.makeText(getApplicationContext(), "The application stopped counting", Toast.LENGTH_SHORT).show();
}
}
});
}
public void changeCounterText() {
changeCount = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
TextView n = findViewById(R.id.counter);
counter = intent.getIntExtra("counterValue", 0);
System.out.println("************************ RECEIVED!!!! value of: " + counter);
n.setText("" + counter);
}
};
filter = new IntentFilter();
filter.addAction("com.inc.count");
this.registerReceiver(changeCount, filter);
}
#Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(changeCount);
assignValuesOnDestroy();
System.out.println("**************************** onDestroy()");
}
public void assignValuesOnCreate() {
Switch s = findViewById(R.id.switchCounter);
if (getSwitchValueFromSP() == 1) s.setChecked(true);
else s.setChecked(false);
Intent f = new Intent();
f.setAction("send.counter.to.phonecounteractivity");
sendBroadcast(f);
}
public void assignValuesOnDestroy() {
SharedPreferences SP = getSharedPreferences("data", MODE_PRIVATE);
SharedPreferences.Editor edit = SP.edit();
Switch s = findViewById(R.id.switchCounter);
if (s.isChecked()) edit.putInt("switch", 1);
else edit.putInt("switch", 0);
edit.apply();
}
public int getSwitchValueFromSP() {
SharedPreferences SP = getSharedPreferences("data", MODE_PRIVATE);
int isOn = SP.getInt("switch", 0);
return isOn;
}
}
Sample of the activity
I need to create a service that allow my application to work also when I close it, I’ve tried with STICKY_SERVICE but it doesn’t work... if anyone can decribe me how I can do this please answer this question.
It works with android 7.1 and it doesn’t with other versions
Here is my code...
public class SensorService extends Service {
public int counter=0;
public SensorService(Context applicationContext) {
super();
Log.i("HERE", "here I am!");
}
public SensorService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startTimer();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i("EXIT", "ondestroy!");
stoptimertask();
Intent broadcastIntent = new Intent("RestartSensor");
sendBroadcast(broadcastIntent);
}
private Timer timer;
private TimerTask timerTask;
long oldTime=0;
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, 1000, 1000); //
}
/**
* it sets the timer to print the counter every x seconds
*/
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
Log.i("in timer", "in timer ++++ "+ (counter++));
}
};
}
/**
* 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;
}
}
This is my service Restarter...
public class SensorRestarterBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i(SensorRestarterBroadcastReceiver.class.getSimpleName(), "Service Stops");
context.startService(new Intent(context, SensorService.class));
}
}
And the mainClass
public class MainActivity extends AppCompatActivity {
Intent mServiceIntent;
private SensorService mSensorService;
Context ctx;
public Context getCtx() {
return ctx;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ctx = this;
setContentView(R.layout.activity_main);
mSensorService = new SensorService(getCtx());
mServiceIntent = new Intent(getCtx(), mSensorService.getClass());
if (!isMyServiceRunning(mSensorService.getClass())) {
startService(mServiceIntent);
}
}
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
Log.i ("isMyServiceRunning?", true+"");
return true;
}
}
Log.i ("isMyServiceRunning?", false+"");
return false;
}
#Override
protected void onDestroy() {
stopService(mServiceIntent);
Log.i("MAINACT", "onDestroy!");
super.onDestroy();
}
}
This restartthe service correctly but after 3/4 seconds it die.
I've added this to my manifest
<service
android:name=".SensorService"
android:enabled="true" >
</service>
<receiver
android:name=".SensorRestarterBroadcastReceiver"
android:enabled="true"
android:exported="true"
android:label="RestartServiceWhenStopped">
<intent-filter>
<action android:name="RestartSensor"/>
</intent-filter>
</receiver>
Add the following code to your sensor service and edit as per your need. You need to bind the sticky service to a notification to keep the service alive and start in the foreground.
#Override
public void onCreate() {
super.onCreate();
Intent notifIntent = new Intent(this, SensorService.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, notifIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.smslogo_100x100)
.setColor(ContextCompat.getColor(this, R.color.colorAccent))
.setContentTitle(getResources().getString(R.string.app_name))
.setContentText("Running")
.setContentIntent(pi)
.build();
startForeground(101010, notification);
}
This will rectify the issue you are facing and the service will run forever.
Here's code for a timer that plays a sound once it reaches 0 (timer works fine). The problem is the sound persists even through onPause() in MainActivity.java called.
I implemented onDestroy() in SimpleIntentService.java to stop the sound, but apparently it's never called even with finish() in the calling Activity. How am I supposed to make the sound stop when the app is paused?
Here's my MainActivity.java
public class MainActivity extends Activity {
private BroadcastReceiver broadcastReceiver;
NumberPicker picker;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
picker = (NumberPicker) findViewById(minutePicker);
Log.i("TurnToTech", "Project Name - SimpleBackgroundService");
picker.setMinValue(0);
picker.setMaxValue(20);
broadcastReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context arg0, Intent intent) {
String text = intent.getStringExtra(SimpleIntentService.PARAM_OUT_MSG);
Toast.makeText(getApplicationContext(),
text, Toast.LENGTH_SHORT).show();
}
};
}
Intent msgIntent;
public void startTimer(View view) {
setContentView(R.layout.activity_main);
msgIntent = new Intent(this, SimpleIntentService.class);
msgIntent.putExtra(SimpleIntentService.PARAM_IN_MSG, "Alarm: ");
msgIntent.putExtra("time", picker.getValue());
startService(msgIntent);
}
public void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter(SimpleIntentService.ACTION_RESP);
filter.addCategory(Intent.CATEGORY_DEFAULT);
registerReceiver(broadcastReceiver,filter);
}
public void onPause() {
finish();
unregisterReceiver(broadcastReceiver);
super.onPause();
}
}
And the SimpleIntentService.java
public class SimpleIntentService extends IntentService {
public static final String PARAM_IN_MSG = "in_msg";
public static final String PARAM_OUT_MSG = "out_msg";
int time;
public static final String ACTION_RESP = "org.turntotech.intent.action.MESSAGE_PROCESSED";
public SimpleIntentService() {
super("SimpleIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
System.out.println("SimpleIntentService Called");
String msg = intent.getStringExtra(PARAM_IN_MSG);
int time = intent.getIntExtra("time", 0);
// Timer implementation
if (time == 0 ){
playSound();
}
while(time > 0){
SystemClock.sleep(5000); // 5 seconds
time -= 5;
String resultTxt = msg + time + " seconds remaining";
Intent broadcastIntent = new Intent();
broadcastIntent.setAction(ACTION_RESP);
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra(PARAM_OUT_MSG, resultTxt);
broadcastIntent.putExtra("time", time);
sendBroadcast(broadcastIntent);
if (time == 0) {
playSound();
}
}
}
Uri alert;
public void playSound(){
alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), alert);
r.play();
}
public void onDestroy() {
Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), alert);
r.stop();
super.onDestroy();
}
}
In your IntentService you're not really stopping the same alarm in your onDestroy function. Because each time you're getting a new instance of it.
So I would like to suggest to keep a public static variable of Ringtone so that it can be accessed from everywhere. Declare them in your MainActivity.
public static Ringtone r;
public static Uri alert;
Initialize them in the onCreate function of your MainActivity.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// ... Other statements
// Initialize ringtone here
initializeRingtone();
}
private void initializeRingtone() {
alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
r = RingtoneManager.getRingtone(getApplicationContext(), alert);
}
Now the onPause() function of your MainActivity should look like this
public void onPause() {
unregisterReceiver(broadcastReceiver);
r.stop();
super.onPause();
}
And if you want to play the sound after you resume the application from background and then the timer runs out, you might consider doing something like this in the onResume function of your MainActivity
public void onResume() {
super.onResume();
registerReceiver(broadcastReceiver);
initializeRingtone(); // Initialize it again.
}
And the playSound() function in the IntentService might look like this.
public void playSound(){
// Initialize the alert and ringtone again.
MainActivity.alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
MainActivity.r = RingtoneManager.getRingtone(getApplicationContext(), alert);
MainActivity.r.play();
}
public void onDestroy() {
MainActivity.r.stop();
super.onDestroy();
}
Hope that helps!
As soon as my application starts I get a crash and I can't figure out why. I have a Service that is used as a timer to count the number of seconds since the timer started, this then gets passed back to my MainActivity.
MainActivity.java:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "BroadcastTest";
private Intent intent;
private Chronometer timer;
private Button start;
private Button stop;
private EditText hourlyRate;
private TextView money;
private long timeWhenStopped = 0;
private double moneyEarned;
private double secondsRate;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
timer = (Chronometer) findViewById(R.id.chronometer);
start = (Button) findViewById(R.id.start);
stop = (Button) findViewById(R.id.stop);
hourlyRate = (EditText) findViewById(R.id.hourlyRate);
money = (TextView) findViewById(R.id.money);
intent = new Intent(this, Timer.class);
start.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startService(new Intent(MainActivity.this, Timer.class));
timer.setBase(SystemClock.elapsedRealtime() + timeWhenStopped);
timer.start();
}
});
stop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
timeWhenStopped = timer.getBase() - SystemClock.elapsedRealtime();
timer.stop();
}
});
timer.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener()
{
#Override
public void onChronometerTick(Chronometer chronometer){
long timeElapsed = SystemClock.elapsedRealtime() - timer.getBase();
int seconds = (int) timeElapsed / 1000;
double hrlyRate = Double.parseDouble(hourlyRate.getText().toString());
secondsRate = (hrlyRate / 60) / 60;
moneyEarned = secondsRate * seconds;
double displayMoney = Math.round(moneyEarned * 100.0)/100.0;
Log.d("hours",Long.toString(seconds));
money.setText("$"+Double.toString(displayMoney));
}
});
}
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
updateUI(intent);
}
};
#Override
public void onResume() {
super.onResume();
startService(intent);
registerReceiver(broadcastReceiver, new IntentFilter(Timer.BROADCAST_ACTION));
}
#Override
public void onPause() {
super.onPause();
unregisterReceiver(broadcastReceiver);
stopService(intent);
}
private void updateUI(Intent intent) {
String counter = intent.getStringExtra("counter");
Log.d("SERVICE_SECONDS", counter);
}
}
Timer.java:
public class Timer extends Service {
private static final String TAG = "BroadcastService";
public static final String BROADCAST_ACTION = "b.calvin.com.dirtymoney";
private final Handler handler = new Handler();
Intent intent;
int counter = 0;
private Runnable sendUpdatesToUI = new Runnable() {
public void run() {
countSeconds();
handler.postDelayed(this, 1000); // 1 second
}
};
#Override
public void onCreate()
{
super.onCreate();
intent = new Intent(BROADCAST_ACTION);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
handler.removeCallbacks(sendUpdatesToUI);
handler.postDelayed(sendUpdatesToUI, 0); // 1 second
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void countSeconds() {
Log.d(TAG, Integer.toString(++counter));
sendBroadcast(intent);
}
}
Error:
FATAL EXCEPTION: main
Process: b.calvin.com.dirtymoney, PID: 3348
java.lang.RuntimeException: Error receiving broadcast Intent { act=b.calvin.com.dirtymoney flg=0x10 } in b.calvin.com.dirtymoney.MainActivity$4#bf337ff
at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:891)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.NullPointerException: println needs a message
at android.util.Log.println_native(Native Method)
at android.util.Log.d(Log.java:139)
at b.calvin.com.dirtymoney.MainActivity.updateUI(MainActivity.java:113)
at b.calvin.com.dirtymoney.MainActivity.access$600(MainActivity.java:20)
at b.calvin.com.dirtymoney.MainActivity$4.onReceive(MainActivity.java:93)
at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:881)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Seems like you forgot to put your counter variable into the Intent, so you're passing null to Log.d() as the message, resulting in a NullPointerException.
Something like this should work:
private void countSeconds() {
Log.d(TAG, Integer.toString(++counter));
Intent counterIntent = new Intent(BROADCAST_ACTION);
counterIntent.putExtra("counter", counter);
sendBroadcast(counterIntent);
}
Also, counter is an int, so you should call getIntExtra() instead of getStringExtra():
private void updateUI(Intent intent) {
int counter = intent.getIntExtra("counter");
Log.d("SERVICE_SECONDS", Integer.toString(counter));
}
I have a service that runs in the background and every 15 minutes, it will show a notification or dialog. But how can I get it to start and stop OnClick of a FAB?
The button shows a Snack Bar OnClick, currently. I want to add If/Else code to start and stop the service. How can I do this?
Here is the service:
public class SafeService extends Service {
private Handler mHandler = new Handler();
private Timer mTimer = null;
public static final int NOTIFICATION_ID = 1;
public static final long NOTIFY_INTERVAL = 900 * 1000; // 15 Minutes
/*^TODO - TEST NOTIFY_INTERVAL FOR ACCURACY^*/
/*^Those are for the timer and handler so the code
can recognise it^ The last one gives how long the timer runs */
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
// Cancels the timer if it already existed.
if (mTimer != null) {
mTimer.cancel();
} else {
// recreate new
mTimer = new Timer();
}
// schedule task
mTimer.scheduleAtFixedRate(new TimeDisplayTimerTask(), 0, NOTIFY_INTERVAL);
}
class TimeDisplayTimerTask extends TimerTask {
#Override
public void run() {
// run on another thread
mHandler.post(new Runnable() {
#Override
public void run() {
// Lollipop or Above
if (Build.VERSION.SDK_INT >= 21) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(SafeService.this);
new NotificationCompat.Builder(SafeService.this);
builder.setSmallIcon(R.drawable.smallplaceholder);
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
builder.setContentTitle("Safeguard");
builder.setContentText("Be careful, Trainer! Remember to look up and stay aware of your surroundings!!");
builder.setStyle(new NotificationCompat.BigTextStyle().bigText("Be careful, Trainer! Remember to look up and stay aware of your surroundings!!"));
builder.setPriority(Notification.PRIORITY_HIGH);
builder.setVibrate(new long[] { 1000, 1000, 1000, 1000, 1000 });
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID, builder.build());
//Below Lollipop
} else {
new MaterialDialog.Builder(SafeService.this)
.title(R.string.warning_title)
.content(R.string.warning)
.positiveText(R.string.button_ok)
.show();
}
}
});
}
};
}
Here is the button I want to start and stop the service:
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Safeguard is now on!", Snackbar.LENGTH_LONG).show();
//Start service after showing SnackBar
}
});
You can use a Singleton instance which will bind/unbind from your service, this way, the service will not be unbound if your activity is destroyed by Android :
ServiceSingleton.java :
public class ServiceSingleton {
private String TAG = ServiceSingleton.class.getSimpleName();
private static ServiceSingleton mInstance;
private Context mContext;
private boolean mBound = false;
private SafeService mService;
private ServiceConnection mServiceConnection = null;
public static ServiceSingleton getInstance(Context context) {
if (mInstance == null)
mInstance = new ServiceSingleton(context);
return mInstance;
}
private ServiceSingleton(Context context) {
this.mContext = context.getApplicationContext();
}
public boolean startNotification() {
if (!mBound) {
mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
Log.i(TAG, "onServiceConnected");
mService = ((SafeService.LocalBinder) service).getService();
mService.startNotification();
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
Intent intent = new Intent(this, SafeService.class);
mBound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
if (!mBound) {
Log.e(TAG, "Error cant bind to service !");
}
} else {
if (mService != null) {
mService.startNotification();
}
}
return mBound;
}
public void stopNotification() {
if (mBound && mService != null) {
mService.stopNotification();
}
}
public boolean isNotificationStarted() {
if (mBound && mService != null) {
return mService.isNotificationStarted();
}
return false;
}
public void close() {
try {
if (mBound) {
if (mService!=null){
mService.stopNotification();
}
mContext.unbindService(mServiceConnection);
mBound = false;
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
public boolean isBound() {
return mBound;
}
}
In your onCreate()
mSingleton = ServiceSingleton.getInstance();
For your click listener :
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (mSingleton.isNotificationStarted()){
mSingleton.startNotification();
}
else {
mSingleton.stopNotification();
}
}
});
stopNotification() wont unbind service if you want to reuse it, if you want to shut it down call close()