Should i use StartService or StartForegroundService for API >= 26? - java

I'm a bit confused because i read some posts where i'm supposed too use ContextCompat.StartForegroundService(); if the API is >= 26.
Now I still just use StartService and it works even though i'm supposed to get an IllegalStateException on an API >= 26 ( current api on phone is 27) according to this post.
https://medium.com/mindorks/mastering-android-service-of-2018-a4a1df5ed5a6
I know Service is an old concept. Let me assure you we will not discuss the basics and we will learn the recent changes made to the service layer in Android 8.0+, we will solve the mystery of famous IllegalStateException and RemoteServiceException. This article is not a conventional way of understanding services, hang tight till you can.
So my question is if i should change startForeGroundService or just keep startService for API >=26?
My Class that handles my Service connection:
/**This establishes the connection to the MediaPlayerService. */
public static ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MediaPlayerService.MusicBinder binder = (MediaPlayerService.MusicBinder)service;
mediaPlayerService = binder.getService();
mediaPlayerService.musicBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
mediaPlayerService.musicBound = false;
}
};
/**This is called to start the MediaPlayerService. */
private static Intent mediaPlayerServiceIntent = null;
public static void startMusicService(Context c) {
/*mediaPlayerServiceIntent binds our connection to the MediaPlayerService. */
mediaPlayerServiceIntent = new Intent(c, MediaPlayerService.class);
c.bindService(mediaPlayerServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
c.startService(mediaPlayerServiceIntent);
mServiceIsActive = true;
}
/**This is called to stop the MediaPlayerService. (onDestroy) */
public static void stopMusicService(Context c) {
if (mediaPlayerServiceIntent == null)
return;
c.unbindService(serviceConnection);
c.stopService(mediaPlayerServiceIntent);
mediaPlayerServiceIntent = null;
mediaPlayerService = null;
}
MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Main.startMusicService(getApplicationContext());
}

startService will not work for api >=26
You can change your service to foreground service with help of following code. It will show the notification.
private void runAsForeground(){
Intent notificationIntent = new Intent(this, MediaPlayerService.class);
PendingIntent pendingIntent=PendingIntent.getActivity(this, 0,
notificationIntent, Intent.FLAG_ACTIVITY_NEW_TASK);
Notification notification=new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentText(getString(R.string.isRecording))
.setContentIntent(pendingIntent).build();
startForeground(NOTIFICATION_ID, notification);
}
for more reference - https://android-developers.googleblog.com/2018/12/effective-foreground-services-on-android_11.html
https://developer.android.com/guide/components/services
another way (not recommended.. target sdk must be 26 or less)
public static void startService(Context context, Class className) {
try {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
Intent restartServiceIntent = new Intent(context, className);
restartServiceIntent.setPackage(context.getPackageName());
PendingIntent restartServicePendingIntent = PendingIntent.getService(context, 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (alarmService != null) {
alarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 500,
restartServicePendingIntent);
}
} else {
Intent i = new Intent(context, className);
context.startService(i);
}
} catch (Exception e) {
MyLog.e(TAG, "startService: ", e);
}
}
Call by
startService(context,MediaPlayerService.class);

Related

How to disable all the incoming push notification when your in the desired activity?

As the title implies, how can I prevent my app from getting notifications when I'm in the desired activity? I'm developing a chat application wherein users can get notifications when a new message has been posted, how can I prevent the notification when the user is in the chat activity?
here's FirebaseMessagingService:
public class MyFirebaseMessagingService extends FirebaseMessagingService {
#Override
public void onMessageReceived(#NonNull RemoteMessage message) {
super.onMessageReceived(message);
int requestID = (int) System.currentTimeMillis();
String title = message.getNotification().getTitle();
String body = message.getNotification().getBody();
String click_action=message.getNotification().getClickAction();
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(),"Notification");
builder.setContentTitle(title);
builder.setContentText(body);
builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI);
builder.setVibrate(new long[] { 1000, 1000, 1000, 1000, 1000 });
builder.setLights(getResources().getColor(R.color.chitchat), 3000, 3000);
builder.setSmallIcon(R.drawable.logowhite);
Intent intent = null;
//message.getData().get("type");
if (Objects.requireNonNull(message.getData().get("type")).equalsIgnoreCase("privatechat"))
{
intent = new Intent(click_action);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra("GCKey", message.getData().get("GCKey"));
intent.putExtra("GCNameKey", message.getData().get("GCNameKey"));
}
PendingIntent pendingIntent = PendingIntent.getActivity(this, requestID, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE );
builder.setAutoCancel(true);
builder.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("Notification", "Default channel", NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
}
notificationManager.notify(69, builder.build());
}
}
It should not be hard. You can just create a class where you have a static variable where you just set the activity and before notifying, check whether you want to show notification or not. This would go this way:
Make a new class with a static variable
public class NotificationHelper {
public static boolean shouldShowNotification = true;
}
In the activity you don't want the notification to show in add this code:
#Override
public void onResume(){
super.onResume();
NotificationHelper.shouldShowNotification = false;
}
#Override
public void onPause(){
super.onPause();
NotificationHelper.shouldShowNotification = true;
}
In the MyFirebaseMessagingService class, add a condition to before executing the code.
public class MyFirebaseMessagingService extends FirebaseMessagingService {
#Override
public void onMessageReceived(#NonNull RemoteMessage message) {
super.onMessageReceived(message);
if(NotificationHelper.shouldShowNotification){
// your code goes here...
}
}
}

Show current Activity by clicking Foreground Service Notification

How to show Foreground Service activity by clicking Notification? When I use my code, it starts new activity, but I need the activity, where service is working. Here is my code (Android Oreo):
public class APSService : Service
{
public static bool isRunning = false;
public override void OnCreate()
{
base.OnCreate();
}
public override void OnDestroy()
{
isRunning = false;
base.OnDestroy();
}
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
isRunning = true;
byte[] input = intent.GetByteArrayExtra("inputExtra");
Intent notificationIntent = new Intent(this, Java.Lang.Class.FromType((typeof(MainActivity))));
PendingIntent pendingIntent = PendingIntent.GetActivity(this,
0, notificationIntent, 0);
var builder = new NotificationCompat.Builder(this, MainActivity.CHANNEL_ID)
.SetContentTitle("APS Service")
.SetSmallIcon(Resource.Drawable.notifypump)
.SetContentText("Start program...")
.SetContentIntent(pendingIntent);
Notification notification = builder.Build();
StartForeground(1, notification);
//do heavy work on background thread
return StartCommandResult.NotSticky;
}
public override IBinder OnBind(Intent intent)
{
return null;
}
}
And in MainActivity in OnCreate:
protected override void OnCreate(Bundle savedInstanceState)
{
if (!APSService.isRunning)
{
createNotificationChannel();
startService();
}
else
{
NotificationChannel serviceChannel = new NotificationChannel
(
CHANNEL_ID,
"APS service Channel",
NotificationImportance.Default
);
notificationManager = (NotificationManager)GetSystemService(Java.Lang.Class.FromType((typeof(NotificationManager))));
notificationManager.CreateNotificationChannel(serviceChannel);
UpdateNotification("Loading...");
APSService.isRunning = true;
}
}
I hope you would help for solving this problem. Thanks a lot.
I write a demo about it, here is a GIF.
You can achieve the festure like following code.
[Service]
class MyForegroundService : Service
{
public const int SERVICE_RUNNING_NOTIFICATION_ID = 10000;
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
CreateNotificationChannel();
string messageBody = "service starting";
// / Create an Intent for the activity you want to start
Intent resultIntent = new Intent(this,typeof(Activity1));
// Create the TaskStackBuilder and add the intent, which inflates the back stack
TaskStackBuilder stackBuilder = TaskStackBuilder.Create(this);
stackBuilder.AddNextIntentWithParentStack(resultIntent);
// Get the PendingIntent containing the entire back stack
PendingIntent resultPendingIntent = stackBuilder.GetPendingIntent(0, PendingIntentFlags.UpdateCurrent);
var notification = new Notification.Builder(this, "10111")
.SetContentIntent(resultPendingIntent)
.SetContentTitle("Foreground")
.SetContentText(messageBody)
.SetSmallIcon(Resource.Drawable.main)
.SetOngoing(true)
.Build();
StartForeground(SERVICE_RUNNING_NOTIFICATION_ID, notification);
//do you work
return StartCommandResult.Sticky;
}
public override IBinder OnBind(Intent intent)
{
return null;
}
void CreateNotificationChannel()
{
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
{
// Notification channels are new in API 26 (and not a part of the
// support library). There is no need to create a notification
// channel on older versions of Android.
return;
}
var channelName = Resources.GetString(Resource.String.channel_name);
var channelDescription = GetString(Resource.String.channel_description);
var channel = new NotificationChannel("10111", channelName, NotificationImportance.Default)
{
Description = channelDescription
};
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.CreateNotificationChannel(channel);
}
}
Here is my demo.
https://github.com/851265601/ForegroundServiceDemo
It's not clear to me what Activity you want to open
How to show Foreground Service activity
A Foreground service runs independently from your app
You are launching the MainActivity here:
Intent notificationIntent = new Intent(this,Java.Lang.Class.FromType((typeof(MainActivity))));
can you clarify what do want to do here?
ps: I know it's not an answer, can't comment yet

Run only foreground service when app is closed to reduce memory usage

I am running a foreground service via broadcast receiver on boot completed.
It starts the services as desired and take only a a fraction of the device memory use and when I launch the app it increased the device memory usage as it should but when I close the app it still takes too much of memory even though the app has been closed and only the foreground service is running. What I want is really that it should use the same amount of memory after app has been closed as of it was using before the app was opened.
So, I did some digging through Android Profiler and what I found is that when foreground service starts after the boot it only opens Application.class, BroadcastReceiver.class, Service.class and few other background classes. And as I open the app it opens all the above classes and other activities . But when I close the app it still uses the device memory for graphic supports. I don't know how to stop that memory usage after the app has been closed.
Here are some screenshots of my Android Profiler
Before Launching the App through Foreground Notification Memory used 65MB
remember the foreground notification was started from the broadcast receiver after boot complete.
After Launching the app from Notifications Memory used 146 MB
While surfing through activities Memory used 165 MB
After app has been closed Memory used 140 MB
Now I want to know how to achieve the task of using the previous amount of memory use that was 65MB?
Here are my BroadcastReceiver and Service.class code.
Broadcast Receiver
public class BootCompletedIntentListener extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())){
Intent serviceIntent = new Intent(context,ClipMonitorService.class);
ContextCompat.startForegroundService(context,serviceIntent);
}
}
}
Service
public class ClipMonitorService extends Service {
private static final String TAG = "ClipboardManager";
private ExecutorService mThreadPool = Executors.newSingleThreadExecutor();
private ClipboardManager mClipboardManager;
private PrefManager prefManager;
#Override
public void onCreate() {
super.onCreate();
prefManager = new PrefManager(this);
}
#Override
public void onDestroy() {
super.onDestroy();
if (mClipboardManager != null) {
mClipboardManager.removePrimaryClipChangedListener(
mOnPrimaryClipChangedListener);
}
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Intent settingIntent = new Intent(this, SettingActivity.class);
PendingIntent pendingSettIntent = PendingIntent.getActivity(this, 0, settingIntent, 0);
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.custom_notification_layout);
remoteViews.setOnClickPendingIntent(R.id.btn_action, pendingSettIntent);
remoteViews.setTextViewText(R.id.notif_subtitle, "1 Clips copied Today");
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContent(remoteViews)
.setVisibility(Notification.VISIBILITY_SECRET)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setContentIntent(pendingIntent)
.setColor(getResources().getColor(R.color.colorPrimary))
.setShowWhen(false)
.build();
startForeground(1, notification);
mClipboardManager =
(ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
mClipboardManager.addPrimaryClipChangedListener(
mOnPrimaryClipChangedListener);
return START_STICKY;
}
private ClipboardManager.OnPrimaryClipChangedListener mOnPrimaryClipChangedListener =
new ClipboardManager.OnPrimaryClipChangedListener() {
#Override
public void onPrimaryClipChanged() {
Log.d(TAG, "onPrimaryClipChangeds");
try {
String textToPaste = mClipboardManager.getPrimaryClip().getItemAt(0).getText().toString();
if (textToPaste.length() > 200) {
if (prefManager.isClipNotifOns()) {
mThreadPool.execute(new MakeNotifRunnable(
textToPaste));
}
}
} catch (Exception ignored) {
}
}
};
private class MakeNotifRunnable implements Runnable {
private final CharSequence mTextToWrite;
public MakeNotifRunnable(CharSequence text) {
mTextToWrite = text;
}
#Override
public void run() {
Intent notifIntent = new Intent(getApplicationContext(), PostNewsActivity.class);
notifIntent.putExtra("post", mTextToWrite);
NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
int notificationId = 2;
String channelId = "channel1";
String channelName = "Clipboard Monitor Notification";
int importance = 0;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
importance = NotificationManager.IMPORTANCE_DEFAULT;
}
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel mChannel = new NotificationChannel(
channelId, channelName, importance);
notificationManager.createNotificationChannel(mChannel);
}
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext(), channelId)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentTitle("Verify copied content")
.setContentText(mTextToWrite)
.setAutoCancel(true)
.setOnlyAlertOnce(true);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(getApplicationContext());
stackBuilder.addNextIntent(notifIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
notificationManager.notify(notificationId, mBuilder.build());
}
}
}
Help me to reduce memory usage
I'll be thankful for your answer.
P.S: As I am new to android development I may have uploaded too much information with jargons .Pardon me for that.

How to stop NfcAdapter.ACTION_NDEF_DISCOVERED becoming true when device boots?

My application starts when the device boots (Nexus 7). When my device boots if(action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED)) becomes true. Then the code inside the if crashes because the intent was not really a ACTION_NDEF_DISCOVERED but a boot.
I can put a try catch and then it won't crash. However then NFC won't work. To get NFC to work the application must be closed and reopened.
Is there a way to check the if(action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED)) but have it ignore the booting? It is really annoying because the if is checking for NFC not boot.
#Override
public void onResume()
{
super.onResume();
// NFC code.
Intent intent = getIntent();
String action = intent.getAction();
PendingIntent pi = this.createPendingResult(0x00A, new Intent(), 0);
nfcAdapter.enableForegroundDispatch(this, pi, null, null);
try
{
// NFC transfer. Receiving message here.
if(action != null && action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED))
{
Parcelable[] parcelables = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage inNdefMessage = (NdefMessage) parcelables[0];
NdefRecord[] inNdefRecords = inNdefMessage.getRecords();
NdefRecord NdefRecord_0 = inNdefRecords[0];
String inMsg = new String(NdefRecord_0.getPayload());
Toast.makeText(getApplicationContext(), "Toasty: "+inMsg + action.toString(), Toast.LENGTH_LONG).show();
textInfo.setText(inMsg);
}
}
catch(Exception e)
{
Log.e("NFC", e.getMessage());
}
}
This is the code that checks for BOOTING.
public class BootManager extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
if(intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED))
{
Intent i = new Intent(context, Login_Activity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
}
In the Login_Activity, Could this be altering the intent?
#Override
protected void onNewIntent(Intent intent)
{
setIntent(intent);
}

Keep broadcast receiver running after application is closed

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);

Categories

Resources