I have my application running a service for shake detect, however in my MainActivity, I have button for log out user, in which I must log out and terminate the service that detects the shake event.
my method for log out in my MainActivity is:
public void signOutAndFinish(){
//Stop Shakeservice
Intent intent = new Intent(this, ShakeService.class);
stopService(intent);
//Go to login activity
Intent iLoginView = new Intent(this, LoginActivity.class);
startActivity(iLoginView);
}
however if I shake my device after logging out, the service recognizes the shake, it is as if it will not kill it immediately:
Intent intent = new Intent(this, ShakeService.class);
stopService(intent);
The code in the method onDestroy is:
#Override
public void onDestroy() {
super.onDestroy();
}
How can I terminate the service so that when I log out the service dies?
Thanks for your help!
You can send a broadcast back your activity in the onDestroy() method of your service and then do the logout.
Here is some sample code of the above idea:
This for your service:
#Override
public void onDestroy() {
super.onDestroy();
Intent intent = new Intent();
intent.setAction("com.example.broadcast.MY_NOTIFICATION");
intent.putExtra("data","Notice for logout!");
sendBroadcast(intent);
}
And this is for your activity:
private BroadcastReceiver br = new MyBroadcastReceiver();
#Override
public void onCreate() {
IntentFilter filter = new IntentFilter("com.example.broadcast.MY_NOTIFICATION");
registerReceiver(br, filter);
}
#Override
public void onDestroy() {
unregisterReceiver(br);
}
// An inner class at your activity
public class MyBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "MyBroadcastReceiver";
#Override
public void onReceive(Context context, Intent intent) {
YourActivity.this.finish();
// or do anything you require to finish the logout...
}
}
however if I shake my device after logging out, the service recognizes the shake
Then presumably you did not clean up your Sensor stuff in the service's onDestroy() method.
How can I terminate the service so that when I log out the service dies?
You are doing that now. However, if you set up something in the service, such as listening to events from SensorManager, you need to clean that up, typically in onDestroy() of the service.
At the moment, in each one of my activities I have this method:
private void registerReceiverClose(){
Activity activity = this;
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("CLOSE_ALL");
broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
activity.finish();
}
};
registerReceiver(broadcastReceiver, intentFilter);
}
and this one as well:
#Override
protected void onDestroy() {
unregisterReceiver(broadcastReceiver);
super.onDestroy();
}
They're triggered by the following logout button:
Button logout = findViewById(R.id.logout_button);
logout.setOnClickListener(click -> {
Intent intent = new Intent("CLOSE_ALL");
this.sendBroadcast(intent);
});
One thing that I'm sure is not closing in the right way, is that I have this code:
private static final ScheduledExecutorService pollingScheduledExecutor = Executors.newSingleThreadScheduledExecutor();
private static final Object lock = new Object();
private static ScheduledFuture<?> currentRunningTask;
public void longPoll() {
synchronized (lock) {
if (currentRunningTask != null) {
currentRunningTask.cancel(true);
}
try {
currentRunningTask = pollingScheduledExecutor.scheduleAtFixedRate(this, 0, 3, TimeUnit.SECONDS);
} catch (Exception ignored) {
}
}
}
public void request() {
Thread requestThread = new Thread(this);
requestThread.start();
}
which continues to issue requests even after I think I should be logged out, which causes errors on the server.
How can I make sure all the threads stop gracefully and the application closes down in the right way?
You could wrapthe polling code inside of a Service. This service can then be stopped using
Intent intent = new Intent(MainActivity.this, MyService.class);
stopService(intent);
Inside of the service, you can override onDestroy() to clean resources up.
Background info: I'm trying to create a service. I create it in my main activity. I know the service is running because the notification is showing. So the service doesn't stop at any point.
What i want to happen: when the screen is locked/turned off i want a certain activity to be displayed for when the screen is turned back on.
What is happening: first, When the screen is turned back on the main activity shows and then the activity i want shows over that. I dont want the main activity to show at all when the screen is turned on .
Then i changed a few things: and now the desired activity flickers on the main screen and then vanishes. It seems very unreliable and only comes a few times.
That should be enough info, here is the code so far (specifically onReceive())
public class OverlayService extends Service{
//IN THIS CLASS CHECK WHETHER LOCK BUTTON WAS PRESSED OR IF PHONE WHEN TO SLEEP.. UPON AWAKENING START THE OVERLAY ACTIVITY...
private BroadcastReceiver mReceiver;
private boolean isShowing = false;
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
private WindowManager windowManager;
WindowManager.LayoutParams params;
#Override
public void onCreate() {
super.onCreate();
windowManager = (WindowManager)getSystemService(WINDOW_SERVICE);
//Register receiver for determining screen off and if user is present
mReceiver = new OverlayStateReceiver();
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);
filter.addAction(Intent.ACTION_SCREEN_ON);
registerReceiver(mReceiver, filter);
makeNotification();
}
void makeNotification() {
Notification notification = new NotificationCompat.Builder(this)
//TODO change icon
.setSmallIcon(R.drawable.test)
.setContentTitle("App")
.setContentText("service is running...")
.setPriority(Notification.PRIORITY_LOW)
.build();
startForeground(1, notification);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
public class OverlayStateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Log.i("app", "load view");
showOverlayActivity(context);
}
}
}
private void showOverlayActivity(Context context) {
Intent intent = new Intent(context, OverlayActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
#Override
public void onDestroy() {
//unregister receiver when the service is destroy
if (mReceiver != null) {
unregisterReceiver(mReceiver);
}
//TODO remove view if it is showing and the service is destroy
super.onDestroy();
}
}
Please see edits before answering!
I have an app which contains a BackgroundService class:
public class BackgroundService extends Service {
#Override
public void onCreate() {
super.onCreate();
IntentFilter filter = new IntentFilter();
filter.addAction("com.spotify.music.playbackstatechanged");
filter.addAction("com.spotify.music.metadatachanged");
filter.addAction("com.spotify.music.queuechanged");
registerReceiver(receiver, filter);
Log.e("Playing:", "APP IS PLAYING");
Notification notification = new Notification();
startForeground(1, notification);
}
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
long timeSentInMs = intent.getLongExtra("timeSent", 0L);
String action = intent.getAction();
if (action.equals(BroadcastTypes.METADATA_CHANGED)) {
String trackId = intent.getStringExtra("id");
String artistName = intent.getStringExtra("artist");
String albumName = intent.getStringExtra("album");
String trackName = intent.getStringExtra("track");
int trackLengthInSec = intent.getIntExtra("length", 0);
// Do something with extracted information...
} else if (action.equals(BroadcastTypes.PLAYBACK_STATE_CHANGED)) {
boolean playing = intent.getBooleanExtra("playing", false);
Log.e("Playing:","TRUE");
}
}
};
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
static final class BroadcastTypes {
static final String SPOTIFY_PACKAGE = "com.spotify.music";
static final String PLAYBACK_STATE_CHANGED = SPOTIFY_PACKAGE + ".playbackstatechanged";
static final String METADATA_CHANGED = SPOTIFY_PACKAGE + ".metadatachanged";
}
}
and this is declared in my manifest:
<service
android:name=".BackgroundService"
android:enabled="true" >
<intent-filter>
<action android:name="com.spotify.music.playbackstatechanged" />
<action android:name="com.spotify.music.metadatachanged" />
<action android:name="com.spotify.music.queuechanged" />
</intent-filter>
</service>
So essentially my objective is to have my BackgroundService initialized when my app is opened, and to have it continue to run in the Background doing whatever I need it to do. As of now, I am using logs to determine whether my "setup" is working, but when I run my app, I am unable to see an logs even after I tested all actions that should have triggered my BroadCastReceiver. Furthermore, my persistent notification should have changed had my service been running, but it does not...
Edit::
So, I added logs to my BackgroundService's onCreate() and onReceive() methods, however, neither seem to be appearing. Im wondering, do I need to do something in my launcher activity to initialize the service? Furthermore, no notification is shown so I assume the Service is not being started for some reason...
Latest Edit:
So I added the following code to my Main activity to see if it would make a difference:
startService(new Intent(this,BackgroundService.class));
And after debugging my app, I began to see the following error:
java.lang.RuntimeException: Unable to create service com.aurum.mutify.BackgroundService: java.lang.SecurityException: Isolated process not allowed to call registerReceiver
pointing to my BroadCast Receiver class.
Intent services are designed for short tasks. And your intent handling method is empty.
If you need long running task in the background use standard service and call start foreground. This will minimize chance of system destroying your service.
To learn more go here
EDIT
Try overriding onStartCommand method. this method is called when service is started and usually you do all stuff here. Remember that there are 3 options to return.
Edit 2:
try something like this
in on create
PendingIntent pi;
BroadcastReceiver br;
Intent myIntent;
#Override
public void onCreate()
{
super.onCreate();
myIntent = new Intent("something")
if(Build.Version.SDK_INT >= 16) //The flag we used here was only added at API 16
myIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
//use myIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); if you want to add more than one flag to this intent;
pi = PendingIntent.getBroadcast(context, 1, myIntent, 0);
br = new BroadcastReceiver ()
{
public void onReceive (Context context, Intent i) {
new thread(new Runnable()
{
public void run()
{
//do something
}
}).start();
}
};
And then in on start command
this.registerReceiver(br, new IntentFilter("something"));
I need to keep broadcast receiver running all the time after app has been started.
Here is the code that registers this receiver in the application
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
BroadcastReceiver mReceiver = new ScreenEventsReceiver();
registerReceiver(mReceiver, filter);
And code for receiver
public class ScreenEventsReceiver extends BroadcastReceiver {
public static boolean wasScreenOn = true;
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
wasScreenOn = false;
Log.d("ScreenEventReceiver", "ON");
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
wasScreenOn = true;
Log.d("ScreenEventReceiver", "ON");
}
}
}
You can use a service
In main app start/stop the service
Intent service = new Intent(context, MyService.class);
context.startService(service);
...
Intent service = new Intent(context, MyService.class);
context.stopService(service);
service
public class MyService extends Service
{
private static BroadcastReceiver m_ScreenOffReceiver;
#Override
public IBinder onBind(Intent arg0)
{
return null;
}
#Override
public void onCreate()
{
registerScreenOffReceiver();
}
#Override
public void onDestroy()
{
unregisterReceiver(m_ScreenOffReceiver);
m_ScreenOffReceiver = null;
}
private void registerScreenOffReceiver()
{
m_ScreenOffReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
Log.d(TAG, "ACTION_SCREEN_OFF");
// do something, e.g. send Intent to main app
}
};
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
registerReceiver(m_ScreenOffReceiver, filter);
}
}
Accepted answer is not an actual answer i think. I will explain what the issue. I think you are testing your app in the Huawie, Oppo, Vivo, Xiomi,asus....... or some devices. With that devices if we close the application they will also close our broadcast receivers. So thats the problem.(To check that use a with pixel nexus emulator). I will explain how to resolve this.``
we would add our app to the protected app list. OS only allow to them to continue broadcast receiver activities.(Copy this array declaration to your code)
private static final Intent[] POWERMANAGER_INTENTS = {
new Intent().setComponent(new ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity")),
new Intent().setComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity")),
new Intent().setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity")),
new Intent().setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.appcontrol.activity.StartupAppControlActivity")),
new Intent().setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity")),
new Intent().setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.startupapp.StartupAppListActivity")),
new Intent().setComponent(new ComponentName("com.oppo.safe", "com.oppo.safe.permission.startup.StartupAppListActivity")),
new Intent().setComponent(new ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity")),
new Intent().setComponent(new ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.BgStartUpManager")),
new Intent().setComponent(new ComponentName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.BgStartUpManagerActivity")),
new Intent().setComponent(new ComponentName("com.samsung.android.lool", "com.samsung.android.sm.ui.battery.BatteryActivity")),
new Intent().setComponent(new ComponentName("com.htc.pitroad", "com.htc.pitroad.landingpage.activity.LandingPageActivity")),
new Intent().setComponent(new ComponentName("com.asus.mobilemanager", "com.asus.mobilemanager.MainActivity"))};
Put these code to your onCreate Method. Here i used shared preference for check it only first time of the app open.
`
final SharedPreferences.Editor pref = getSharedPreferences("allow_notify", MODE_PRIVATE).edit(); pref.apply(); final SharedPreferences sp = getSharedPreferences("allow_notify", MODE_PRIVATE)`;
if(!sp.getBoolean("protected",false)) {
for (final Intent intent : POWERMANAGER_INTENTS)
if (getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Alert Title").setMessage("Alert Body")
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
startActivity(intent);
sp.edit().putBoolean("protected",true).apply();
}
})
.setCancelable(false)
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
})
.create().show();
break;
Be careful if you are using Android 4.4.x as there is a bug which kills background services when closing the app. I was testing my app in Android 4.4.2 and I had the same problem. Here there is a detailed explanation:
http://www.androidpolice.com/2014/03/07/bug-watch-stopping-apps-on-android-4-4-2-can-silently-kill-related-background-services-a-fix-is-on-the-way/
You cannot receive some broadcast events through components declared in manifest.
These events are
ACTION_BATTERY_CHANGED
ACTION_CONFIGURATION_CHANGED
ACTION_SCREEN_OFF (You are playing with this event)
ACTION_SCREEN_ON (You are playing with this event)
ACTION_TIME_TICK
Reference https://developer.android.com/reference/android/content/Intent.html#ACTION_SCREEN_ON
So in your specific events,
you will have to create a Service & you will have to register your event explicitly in service onCreate() with with Context.registerReceiver().
For other events,
entry in manifest is sufficient.
If you declare BroadcastReceiver in the Manifest, it will always be active and be called even if the application is closed/stopped
You could start a service that is running in the foreground. That's the only way to ensure (mostly) that your app will get the events. There is still a chance that your foreground service could get killed in times of crazy memory pressure from the OS (so it's not foolproof). If you start a service in the foreground, the user will see a persistent notification to know that it is always running, though.
So the moral of the story is, do you really need to monitor the screen off/on events at all times? The reason that they force you to register a receiver not in the manifest is that they don't want people to always be monitoring these events and slowing down the device. What are you trying to accomplish?
The best way I found is the Foreground Services. I registered my BroadcastReceiver from my Service only under the onStartCommand() as I want my service needs to run always, I returned START_STICKY
This way, my broadcast receiver survives even after terminating the app from stack.
Used below code in my service
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("rht", "Received start id " + startId + ": " + intent);
String input = intent.getStringExtra("inputExtra");
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
}
This is how I started my service
Intent serviceIntent = new Intent(this, SpeechServiceForeground.class);
ContextCompat.startForegroundService(this, serviceIntent);