Android Service member variable goes null after running for some time - java

I'm trying to create a foreground service to communicate with a bluetooth device and I've done so using this structure:
Using a service, which also registers a BroadcastReceiver as a private member variable (to handle Gatt events), then the service is started using the startService command. Then, I bind to that service inside my current Activity. Inside the onStartCommand, I assign a BluetoothDevice object (my_device) which was passed as an extra in the onStartCommand intent to a member variable in the service. However, after the service has run and the member variable is assigned, somehow it loses that member variable instance and it becomes null again when I try to do anything in the BroadcastReceiver that handles BLE Gatt Events.
public final class My_BLEService extends Service {
private My_BLEDevice my_device = null;
private BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Problem: my_device is null inside this function
}
};
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Create the Foreground notification
if (intent.getAction().equals(Constants.ACTION.STARTFOREGROUND_ACTION)) {
// Get BluetoothDevice object
if(this.my_device == null) {
// Initialize the my_device object
BluetoothDevice bleDevice = intent.getParcelableExtra(My_AppManager.RFSTAR);
this.my_device = new my_BLEDevice(bleDevice);
}
// Connect to the bluetooth device first thing:
if(!this.isConnected) {
this.connect();
}
// Start up the broadcast receiver here
if(!this.receiverRegistered) {
registerReceiver(this.gattUpdateReceiver, this.bleIntentFilter());
this.receiverRegistered = true;
}
Intent notificationIntent = new Intent(My_BLEService.this, MonitoringActivity.class);
// FIXME: Necessary? - notificationIntent.putExtra(My_AppManager.RFSTAR, bleDevice);
notificationIntent.setAction(Constants.ACTION.MAIN_ACTION);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Bitmap icon = BitmapFactory.decodeResource(getResources(),
R.drawable.logo_md);
// TODO: Add close button
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("BLE Monitor")
.setTicker("BLE Monitor")
.setContentText("BLE")
// TODO; This isn't appearing correctly the first time
.setSmallIcon(R.drawable.logo_md)
//.setLargeIcon(
// Bitmap.createScaledBitmap(icon, 128, 128, false))
.setContentIntent(pendingIntent)
// Note: this doesn't seem to do anything (setting it to false doesn't change it either)
.setOngoing(true).build();
startForeground(Constants.NOTIFICATION_ID.FOREGROUND_SERVICE, notification);
} else if (intent.getAction().equals(Constants.ACTION.STOPFOREGROUND_ACTION)) {
if(this.receiverRegistered) {
unregisterReceiver(this.gattUpdateReceiver);
this.receiverRegistered = false;
}
if(this.isConnected) {
this.disconnect();
}
stopForeground(true);
stopSelf();
}
return START_STICKY;
}

Alright, I created the problem myself. The problem was, in my service I was resetting the member variable my_device in the onBind method:
public IBinder onBind(Intent intent) {
this.my_device = intent.getParcelableExtra(Kidpod_AppManager.RFSTAR);
return this.kBinder;
}
with an extra which was not set when the bindService method was called in the activity, so it was setting it to null.

Related

unable to start activity from service class

Please be informed, we are trying to start activity from service class, which is fired on clicking push notification addaction intent. The service class contains two actions, one to stop playing ringtone and another to startactivity. But unfortunately the start activity just does not boot in our service class.
Service Class page is as given below:
public class RingtonePlayingService extends Service {
private static final String TAG = RingtonePlayingService.class.getSimpleName();
private static final String URI_BASE = RingtonePlayingService.class.getName() + ".";
public static final String ACTION_DISMISS = URI_BASE + "ACTION_DISMISS";
public static final String ACTION_START = URI_BASE + "ACTION_START";
private MediaPlayer mp;
private Ringtone ringtone;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
int notificationId = intent.getIntExtra("notificationId", 0);
if (intent == null) {
Log.d(TAG, "The intent is null.");
return START_REDELIVER_INTENT;
}
String action = intent.getAction();
if (ACTION_START.equals(action)) {
String uri = String.valueOf(intent.getIntExtra("uri", 0));
Intent intents = new Intent(this, MainActivity.class);
intents.putExtra("uri", uri);
intents.putExtra("notification_id", notificationId);
intents.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
this.startActivity(intents);
}
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
mp.reset ();
}}
Firebase (from where service class's intent is fired)
Intent startIntent = new Intent(this, RingtonePlayingService.class);
startIntent.setAction(RingtonePlayingService.ACTION_START);
startIntent.putExtra("uri", uri);
startIntent.putExtra("notification_id", notification_id);
PendingIntent pt = PendingIntent.getService(this,123, startIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Action action_n = new NotificationCompat.Action.Builder(R.mipmap.ic_launcher, tag, pt).build();
The objective here is on firing of pending Intent 'pt'. We want to start the activity (that opens the app's url) as well as run the service class (which stops the service and the ringtone).
Please help us find a solution on this never ending issue.
The service class contains two actions, one to stop playing ringtone and another to startactivity. But unfortunately the start activity just does not boot in our service class.
You cannot start an activity from the background on modern versions of Android.
Firebase (from where service class's intent is fired)
That code has problems:
Your Intent is for RingtonePlayingService, which according to your first code snippet is a Service. Yet, you try using it with PendingIntent.getActivity(), rather than PendingIntent.getService().
You are using addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK), which are irrelevant for a Service.

How to know os kill my foreground service android

I made a lock screen app. I want to restart my service when the OS kills the service in Xiaomi Redmi Note 10 Pro (MIUI 12). When the service is killed, onDestroy is not call.
public class LockScreenService extends Service {
SharedPreferences prefs;
private BroadcastReceiver screenStateReceiver;
public static boolean isScreenReceiverRegistered=false;
public IBinder onBind(Intent paramIntent) {
return null;
}
public void onCreate() {
super.onCreate();
prefs = getSharedPreferences("SettingPreference", Context.MODE_PRIVATE);
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.setPriority(999);
screenStateReceiver = new ScreenStateReceiver();
registerReceiver(screenStateReceiver, filter);
isScreenReceiverRegistered = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
String channelId = createNotificationChannel(notificationManager);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId);
Notification notification = notificationBuilder.setOngoing(true)
.setSmallIcon(R.drawable.icon_notification)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.build();
startForeground(127, notification);
}
}
#RequiresApi(Build.VERSION_CODES.O)
private String createNotificationChannel(NotificationManager notificationManager){
String channelId = "my_service_channelid";
String channelName = "Lock Screen Running";
NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
// omitted the LED color
channel.setImportance(NotificationManager.IMPORTANCE_NONE);
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
notificationManager.createNotificationChannel(channel);
return channelId;
}
#Override
public int onStartCommand(final Intent intent, final int flags,
final int startId) {
return START_STICKY;
}
and on onDestroy() function I restart my service.
Manifests
<service android:name=".LockScreenService"
android:process=":ServiceProcess"
android:enabled="true"
android:exported="false"/>
try this, if you want to get it in onResume()
#Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume: GamePreferences.getPid()--------> " + GamePreferences.getPid());
Log.d(TAG, "onResume: android.os.Process.myPid()--------> " + android.os.Process.myPid());
if (GamePreferences.getPid() != 0) {
if (GamePreferences.getPid() != android.os.Process.myPid()) {
Log.d(TAG, "GamePreferences.getPid() != android.os.Process.myPid(): --------> " + android.os.Process.myPid());
//restart your service in foreground
return;
}
}
}
According to the documentation, there is no guarantee onDestroy will be called. I could not find an explicit mention to what happens when the process is killed, but it seems that you are more likely to be called onStop. So you can try to start your service with an intent from onStop.
Also, there are documented ways to prevent your process to be elected, such as: having a related Activity running or having ongoing callbacks in BroadcastReceiver or Service.
Note well that your process might get killed by the user, and refusing to comply to the user's desire to kill is invasive. Therefore the best solution should be designed around the actual reason why a user would want your process to stay alive.

Why does foreground service stop working when device go into sleep mode

I want to create an app that is constantly checking for location change and put the current location in the firebase (e.g. an app for runners).
Unfortunately the foregroundservice is being stopped or paused every time the device go into sleep mode.
For starters I wanted to create a foreground service that is continuously writing information to the base (that would be a time stamp or a simple string) every second.
After some time it just stops writing to firebase without calling stopself().
The service is working fine on the emulator (even if put to sleep), but stops when tested on a real device – in my case Huawei, Android 8.1.0.
What should I do to force service to run in every state of the device?
My MainActivity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent intent = new Intent(this, MyService.class);
intent.putExtra("action", "start");
startForegroundService(intent);
}
else {
Intent intent = new Intent(this, MyService.class);
intent.putExtra("action", "start");
startService(intent);
}
}
#Override
protected void onDestroy() {
super.onDestroy();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent intent = new Intent(this, MyService.class);
intent.putExtra("action", "stop");
startForegroundService(intent);
}
else {
Intent intent = new Intent(this, MyService.class);
intent.putExtra("action", "stop");
startService(intent);
}
}
}
MyService:
public class MyService extends Service {
int i =0;
private String CHANNEL_ID = "2345";
#Override
public void onCreate() {
super.onCreate();
}
public MyService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(1000, createNotification());
String action = intent.getExtras().getString("action");
switch (action){
case "start":
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
myfunction();
handler.postDelayed(this, 1000);
}
};
break;
case "stop":
stopfunction();
break;
}
handler.postDelayed(runnable, 1000);
return START_NOT_STICKY;
}
private void stopfunction() {
stopSelf();
}
private void myfunction() {
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference("locations");
myRef.child("location").setValue(i);
i++;
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return null;
}
#RequiresApi(Build.VERSION_CODES.O)
private void createChannel(){
NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, getString(R.string.infoTxt),
NotificationManager.IMPORTANCE_HIGH);
channel.setShowBadge(false);
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
notificationManager.createNotificationChannel(channel);
}
private Notification createNotification(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
createChannel();
}
Intent notificationItent = new Intent(this, MainActivity.class);
notificationItent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent intent = PendingIntent.getActivity(this, 0, notificationItent, 0);
return new NotificationCompat.Builder(this, CHANNEL_ID)
.setColor(ContextCompat.getColor(this, android.R.color.background_dark))
.setContentIntent(intent)
.setSmallIcon(R.drawable.ic_launcher_background)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setOnlyAlertOnce(true)
.setContentTitle("GPS Location")
.build();
}
}
I've tried everything: service, foreground service, broadcast receiver, jobSheduler, WorkerManager – nothing helped. Then I found it’s a new HUAWEI feature called “power-intensive app monitor “. It kills every app that runs in the background for a long time unless user gives special permissions to it.
The path to do this:
Settings -> Security & privacy -> Location services -> recent location requests: YOUR APP NAME -> Battery -> uncheck Power-intensive prompt, App launch: Manage manually: check all three positions: Auto-launch, secondary launch, run in background.
I don’t know is there a way to do this programmatically. I think the best way is to create a sort of help activity and explain the user what to do if application won’t work.
Foreground services generally should be used for task which require user attention such as visual processes.
use Background service instead

EventListener for Power Button press in a Service

I want to listen the power key event in the service.
How can in do that ?
Currently I am working with an app, where I need to listen the power button for some events, from a service which is running in a background, even when the app is killed or stopped.
Somehow I can manage to get it.
But when I kill/stop the app, the service is getting stopped.
How can i overcome this ?
Currently the code i am using this :
Service Class:
public class SampleService extends Service
{
SettingContentObserver mSettingsContentObserver;
AudioManager mAudioManager;
private ComponentName mRemoteControlResponder;
private Intent intent;
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
Log.v("StartServiceAtBoot", "StartAtBootService -- onStartCommand()");
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
#Override
public void onStart(Intent intent, int startId) {
boolean screenOn = intent.getBooleanExtra("screen_state", false);
if (!screenOn) {
Toast.makeText(getApplicationContext(), "On", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(), "Off", Toast.LENGTH_SHORT).show();
}
}
public void onCreate()
{
mSettingsContentObserver = new SettingContentObserver(this,new Handler());
getApplicationContext().getContentResolver().registerContentObserver
(android.provider.Settings.System.CONTENT_URI, true, mSettingsContentObserver );
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
mRemoteControlResponder = new ComponentName(getPackageName(),
StartAtBootServiceReceiver.class.getName());
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
BroadcastReceiver mReceiver = new StartAtBootServiceReceiver();
registerReceiver(mReceiver, filter);
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
public void onDestroy()
{
getApplicationContext().getContentResolver().unregisterContentObserver(mSettingsContentObserver);
}
}
BroadcastReceiver Class:
public class StartAtBootServiceReceiver extends BroadcastReceiver
{
static boolean wasScreenOn;
private boolean screenOff;
public void onReceive(Context context, Intent intent)
{
if(intent.getAction().equals(Intent.ACTION_SCREEN_OFF))
{
wasScreenOn = false;
Toast.makeText(context, "Power Off", Toast.LENGTH_SHORT).show();
}
else if(intent.getAction().equals(Intent.ACTION_SCREEN_ON))
{
wasScreenOn = true;
}
Intent i = new Intent(context, SampleService.class);
i.putExtra("screen_state", screenOff);
i.setAction("com.example.antitheft.SampleService");
context.startService(i);
//
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
Intent i1 = new Intent();
i1.setAction("com.example.sampleonkeylistener.MainActivity");
context.startService(i1);
}
}
}
given above is the sample code and i have created AndroidManifest.xml files also with user's permission but i cannot get the app continue service if it is killed or stopped.
Thanks in Advance.
#Override
public void onDestroy() {
super.onDestroy();
startService(new Intent(this, SampleService.class));
}
This is one way to ensure that service will never stop even user want to destroy it.
This is one Just ONE of ways to achieve what you are trying to achieve.
Secondly, you can try and run service in "foreground" by using startForeground().
Also, make sure that in you return "START_STICKY" (which you are doing in the sample code that you shared and I trust that you are also doing in App's code too :) ) in Services's onStartCommand().
This will ensure that If this service's process is killed while it is started (after returning from onStartCommand(Intent, int, int)), then leave it in the started state but don't retain this delivered intent. Later the system will try to re-create the service.
And you may find some additional pointers/hints to make sure your service is not stopped at below link.
How can we prevent a Service from being killed by OS?
Just pick and choose the approach that best suits YOUR Need/implementation.

Get Activity's widget from service

The main activity has an AlarmManager which calls a Service every X minutes. I need a way that the methods inside the service's class can update a TextView in the main activity, but I dont know how to get the TextView's object in the service. Is there any way?
Here is part of the code:
Intent myIntent = new Intent(MainActivity.this, MyAlarmService.class);
pintent = PendingIntent.getService(MainActivity.this, 0, myIntent, 0);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 60000, pintent);
In your alarm service you have a onReceive method
public void onReceive(Context context, Intent arg1) {
String data = "haha";
if (data.isEmpty() == false && data.contentEquals("") == false) {
nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
CharSequence from = "sing";
CharSequence message = data;
//get the activity
PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
new Intent(), 0);
Notification notif = new Notification(R.drawable.icon,
data, System.currentTimeMillis());
notif.defaults |= Notification.DEFAULT_SOUND;
notif.setLatestEventInfo(context, from, message, contentIntent);
nm.notify(1, notif);
}
Method call:
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, ReminderReceiverActivity.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,
intent, PendingIntent.FLAG_CANCEL_CURRENT);
am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
100000, pendingIntent);
}
I had the same need, in an accelerometer-reading service and an app that would start/stop the service and display the current average accelerometer magnitude.
I used Service Binding to allow the Service to send a Message to the Activity, containing the accelerometer value.
See http://developer.android.com/guide/components/bound-services.html#Messenger for background on Binding. For a good example, see the API Samples' MessengerServiceActivities and MessengerService classes.
I did the following. I've left out the mundane details (such as Synchronization to avoid races) for clarity. Notice that I use bind() as well as StartService(). The bind() is for sending messages between the Activity and Service; the StartService() is so the Service keeps running after the Activity exits.
Basically, the Activity and Service exchange Messengers that will allow each to send Messages to the other. The Activity sends custom Messages to the Service in order to Subscribe to Service Messages or Unsubscribe from Service Messsages. When the Service wants to send data to the Activity, it sends a custom Message to the Activity's Messenger. The Activity, on receiving such a message, displays the new value to the user.
The answer that suggested using Notifications uses a simple way to get data onto the screen. This more complex Message-passing answer is needed if you want to display data in your Activity (vs. the Notification bar).
I apologize for the length of the example code below, but there are a lot of necessary details to convey.
In my Activity, named AccelServiceControl:
private FromServiceHandler handler; // My custom class for Handling Messages from my Service.
private Messenger fromService; // For receiving messages from our Service
...
public void onCreate(Bundle savedInstanceState) {
...
handler = new FromServiceHandler(this);
fromService = new Messenger(handler);
...
}
protected void onResume() {
...
// While we're in the foreground, we want to be bound to our service.
// onServiceConnected() will return the IBinder we'll use to communicate back and forth.
bindService(new Intent(this, AccelService.class), this, Context.BIND_AUTO_CREATE);
}
protected void onPause() {
...
// Note: By itself, this doesn't stop the Service from sending messages to us.
// We need to send a custom Unsubscribe Message to stop getting messages from the Service.
unbindService(this);
}
public void onClick(View v) {
// Send a custom intent to start or stop our Service.
if (buttonStart == v) {
startService(new Intent(AccelService.ACTION_START));
} else if (buttonStop == v) {
startService(new Intent(AccelService.ACTION_STOP));
}
...
}
public void onServiceConnected(ComponentName name, IBinder service) {
...
// Ask our Service to send us updates.
toService = new Messenger(service);
Message msg = Message.obtain(null, FromClientHandler.MSG_SUBSCRIBE); // our custom Subscribe message
msg.replyTo = fromService;
try {
toService.send(msg);
} catch (RemoteException e) {
// Failed because the Service has died.
// We handle this in onServiceDisconnected().
}
}
In the Activity's custom Message Handler:
....
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ACCEL_UPDATE:
...
Bundle bundle = msg.getData();
double accelValue = bundle.getDouble(ACCEL_UPDATE_VALUE);
...then display the new accelValue in, for example, a TextView.
...
}
}
In the Service, named AccelService:
...
private Messenger fromClient; // For receiving messages from our Client(s).
private FromClientHandler handler; // needed just for unlinking at in onDestroy().
// Since we have only one Client, we store only one Activity's Messenger
private Messenger subscribedMessenger;
public void onCreate() {
...
handler = new FromClientHandler(this);
fromClient = new Messenger(handler);
}
public void onDestroy() {
// Unlink ourselves from our Handler, so the Garbage Collector can get rid of us. That's a topic in itself.
handler.unlink();
....
}
public int onStartCommand(Intent intent, int flags, int startId) {
...
int returnValue = START_NOT_STICKY;
String action = intent.getAction();
if (ACTION_START.equals(action)) {
doActionStart();
returnValue = START_STICKY;
} else if (ACTION_STOP.equals(action)) {
...
// Our Service is done
stopSelf();
}
...
return returnValue;
}
public IBinder onBind(Intent intent) {
// Hand back a way to send messages to us.
return fromClient.getBinder();
}
...when we want to send data to the Activity:
Message msg = Message.obtain(null, FromServiceHandler.MSG_ACCEL_UPDATE);
Bundle bundle = new Bundle();
bundle.putDouble(FromServiceHandler.ACCEL_UPDATE_VALUE, avgAccel);
msg.setData(bundle);
try {
subscribedMessenger.send(msg);
} catch (RemoteException e) {
// Failed because the Client has unbound.
subscribedMessenger = null;
}

Categories