I'm using a foreground service on Android that plays audio. I also use microphone input through NDK from Android Oboe library on a fragment, unrelated to the service. However, after I close the app, the microphone is inacessible to other apps, even if the service is killed (the service notification goes away).
Here's how I'm starting the service:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//Start in Foreground
ContextCompat.startForegroundService(this, intent);
if (serviceConnection != null) {
//Then bind service
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
} else {
startService(intent);
if (serviceConnection != null) {
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
The service is just public class MainService extends Service
and I added this on AndroidManifest
<service android:name=".services.MainService" android:stopWithTask="true"/>
so it stops when I close the app.
However, the oboe code is not in the service. I suspect that C++ keeps a thread open on the background. Somehow, this thread lives even after the app closes. Is it possible?
I added this:
override fun onDestroy() {
Log.d(TAG, "-----fxfragment destroyed!")
NativeInterface.destroyAudioEngine()
super.onDestroy()
}
to delete the audio engine on the C++ side. It works on some phones, the microphone is acessible. But on some, it does not.
Creating engine in onResume and destroying in onPause() so the stream retains exclusive
mode only while in focus. This allows other apps to reclaim exclusive stream mode. Therefore, I would recommend you to add onResume() and onPause()
#Override
protected void onResume() {
super.onResume();
PlaybackEngine.create(this);
setupLatencyUpdater();
// Return the spinner states to their default value
mChannelCountSpinner.setSelection(CHANNEL_COUNT_DEFAULT_OPTION_INDEX);
mPlaybackDeviceSpinner.setSelection(SPINNER_DEFAULT_OPTION_INDEX);
mBufferSizeSpinner.setSelection(SPINNER_DEFAULT_OPTION_INDEX);
mAudioApiSpinner.setSelection(SPINNER_DEFAULT_OPTION_INDEX);
}
and in the onPause()
#Override
protected void onPause() {
if (mLatencyUpdater != null) mLatencyUpdater.cancel();
PlaybackEngine.delete();
super.onPause();
}
You can also go through this github link for further details
this is bug in library as mention here library issues link
Related
From the stackoverflow and many blogs, i surely understand that foreground service never run without notification in API>25. But still i confuse that Is notification mandory while app is running on screen or visible.
For eg. no need of notification when user stand within app. So is this possible to remove notification while app running ?
In service class
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
......
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
.setContentTitle(getString(R.string.app_name))
.setContentText(text)
.setAutoCancel(true);
Notification notification = builder.build();
startForeground(1, notification);
}
return START_NOT_STICKY;
}
In activity
Intent myService = new Intent(this, MyService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(myService);
} else {
startService(myService);
}
It's not possible to remove the notification while the foreground service is running, but it is possible to change your foreground service back into a "regular" service. This removes the need for a notification. In fact, the function to use,
stopForeground(boolean removeNotification)
...includes a removeNotification parameter just for that purpose. You service can switch from being "foreground" to "regular" on demand, by alternating calls to startForeground() and stopForeground().
In case it's not clear, you'd probably want to call stopForeground() whenever you have at least one Activity in a "started" state. This is something you'd have to track manually. Then, when the number of "started" activities reaches 0, you'd call startForeground().
EDIT
One approach is to use a bound service. Then, it's easy to call stopForeground() on it when you want.
Assume you have a single Activity. You can bind it to the service (see this doc or use one of these examples). Then your onServiceConnected() function could look like this (adapted from the Google example):
//MyActivity.java:
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mService.stopForeground(true); //This makes the notification go away
bound = true;
}
...
#Override
protected void onStart() {
super.onStart();
// Bind to the service
bindService(new Intent(this, MyService.class), this, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (bound) {
Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
.setContentTitle(getString(R.string.app_name))
.setContentText(text)
.setAutoCancel(true);
Notification notification = builder.build();
mService.startForeground(1, notification); //This brings the notification back! Service is already running, and continues to run.
unbindService(this);
bound = false;
}
}
No, it is mandatory even your app is running in foreground your foreground service need a notification.
You won't able to hide it.
Why :
You can use any other background task handler like intent service, job sclr but things is designed defferent for foreground service your user understand that event i will close this one of it's progress is going to keep running but things is defferent with background service your know it will do something in background but when system decide it's best time to do it not when your app want (as like in foreground service).
One more case ex :
Suppose your app in foreground battery level is lower than expected by user or system your foreground service will execute instantly no matter what so it's important for your user to know this it's running and take my resources (battery, data, etc)
Hopefully you got my mean 🙂
I was recently fiddling around with the newest Android P version, trying to run a simple Service with the START_STICKY flag.
Basically, this is my setup:
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstance) {
Intent intent = new Intent(this, MyService.class);
startService(intent);
}
}
MyService.java
public class MyService extends Service {
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate()");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy()");
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
Log.d(TAG, "onTaskRemoved()");
}
#Override
public void onLowMemory() {
super.onLowMemory();
Log.d(TAG, "onLowMemory()");
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand()");
return START_STICKY;
}
}
Of course, I've added a service entry into the application's AndroidManifest file.
Testing the application on my older phone running Android Nougat, everything works as expected.
This is how the lifecycle looks like after starting the app:
On Android N
onCreate()
onStartCommand()
Now, after swiping off the application from the recents menu:
onTaskRemoved()
onCreate()
onStartCommand()
This is the behavior that I expect. But when I launch the application on my newer phone running Android P DP4 the lifecycle looks like this, when starting the app:
On Android P
onCreate()
onStartCommand()
So far, nothing different. But when I clear the app from the recent menu:
onTaskRemoved()
onCreate()
onDestroy()
Thanks to the START_STICKYflag, the service restarts, but instantly gets destroyed, even before having the chance to run the onStartCommand method.
Are there any changes that I am not aware of? The only changes to something service related that I could find are documented here. And it only describes that a foreground service now needs a specific permission to work. However, I am running a background service, and thus this information seems to be irrelevant to me.
Did anyone experience similar problems?
Thanks in advance!
You're looking in the behavioral changes of the wrong Android version. Android Oreo introduced Background Execution Limits which limits services in the background. TL;DR: Starting a service in the background will throw an IllegalStateException, use Jobs instead.
I am struggling with persistent problem with Android app that has MQTT ServiceConnection running on background. When ever I switch to an other application that has fullscreen surface (game, youtube on fullscreen, etc) the application completely freezes when trying to bring it on front again. It even refuses to relaunch if closed from drawer. The app won't reopen unless completely killed. The weird thing is that if I run the application via debugger it works perfectly. It makes the problem difficult to get handle on. I have put on prints on beginning of each life cycle method, even before call to super class method. None of them get printed when the app hangs, which hints that it hangs some where on the system level.
Now if I call stopService at onStop method, it works fine but defeats the purpose of having persistent service on the background. I have called unbindService also on onStop, but it does not help. With all the other application everything works fine, even you tube as long as it is not used in fullscreen.
The dependency to the MQTT library is
compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.0.2'
and this is my onStop() method
#Override
protected void onStop() {
FirebaseUserActions.getInstance().end(getIndexApiAction0());
if(mBoundToService) {
try {
unbindService(serviceConnection);
mBoundToService = false;
}
catch (Exception e)
{
e.printStackTrace();
}
}
super.onStop();
}
And the onStart() method would be this:
#Override
protected void onStart() {
Log.d(TAG, "onStart has been called");
super.onStart();
if(!mBoundToService) {
bindService(mqttIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
FirebaseUserActions.getInstance().start(getIndexApiAction());
}
But this never gets called. From the logs I can see that MQTT service is receiving messages and produces no errors. Messages continue to be received even as the application is hanged so the background service is alive and running.
The inlined Service connection class is like this:
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder binder)
{
mqttService = ((MQTTService.MyBinder) binder).getService();
mBoundToService = true;
Log.d(TAG,"MQTT service has been bound");
Handler handler = getHandler();
//NOTIFY THE LOGIN ACTIVITY THAT THE DATA HAS BEEN ACQUIRED
Message msg = handler.obtainMessage(Utils.HANDLE_MQTT_SERVICE_CONNECTED);
Bundle bundle = new Bundle();
bundle.putString(Utils.KEY_MQTT_SERVICE_BOUND,"Everything is complete");
msg.setData(bundle);
handler.sendMessage(msg);
}
#Override
public void onServiceDisconnected(ComponentName name) {
mBoundToService = false;
Log.d(TAG, "MQTT service has been UNbound");
}
};
The MQTT service is started like this from onCreate():
mqttIntent = new Intent(getApplicationContext(), MQTTService.class);
if(!mBoundToService){
Log.d(TAG,"Starting MQTT Intent");
startService(mqttIntent);
Log.d(TAG,"Binding MQTT Service");
bindService(mqttIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
Am I missing something that I should be doing at the onStop() to be able to resume the application after having fullscreen application on front?
I have a service declared in manifest like
<service android:name=".services.ScreenOnService" android:process="#string/screenProcess"/>
all the service does is registering for Screen_on broadcast (As i always need the information that the Screen was turned on and not only if my app is running)
#Nullable
#Override
public IBinder onBind(Intent intent)
{
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
//All this service has to do is register for the screen on broadcast
//as this one can't be registere in manifest and the ACTION_USER_PRESENT is
//not guaranteed to be fired. (E.g. if no lock screen is used)
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
registerReceiver(screenEventReceiver,filter);
return START_STICKY;
}
#Override
public void onDestroy()
{
unregisterReceiver(screenEventReceiver);
super.onDestroy();
}
I start the service from my Application onCreate
#Override
public void onCreate() {
super.onCreate();
if(!isScreenOnServiceAlreadyRunning())
{
//Start the screen on service
Intent screenOnService = new Intent(this, ScreenOnService.class);
startService(screenOnService);
}
}
Everything is fine as long as the app is running. If I kill the app, the service in it's own process is also killed and I don't get why.
I found a promising article here http://fabcirablog.weebly.com/blog/creating-a-never-ending-background-service-in-android and hoped the best but even if I do this and send a broadcast, it will not work.
Why does the service stop working if the app is killed? I assumed it will keep running, as it's in it's own process. If what I want is not realisable with my approach, what's the best way to do so?
Thanks already.
#Hardcore_Graverobber I think you should start the service as a separate process,
please refer this tutorial
http://www.vogella.com/tutorials/AndroidServices/article.html
I am working on a messaging app, it sends user notification when he is on a different activtyon my app or is on another app but if the user is on MessagingActivity.java it just updates the chat history and does not send any notifications which is perfectly fine, but the problem arises when the user is on MessagingActivity.java meanwhile an email or something else happen user leaves the MessagingActivity.java open and checks that app if in the meantime a message comes user does not receive any notifications
public void parseRequest(Bundle extras) {
if (extras.containsKey("for") && extras.containsKey("recipientID")) {
if (Integer.parseInt(extras.getString("recipientID")) == M.getID(this)) {
switch (extras.getString("for")) {
case "chat":
if (isRunning("MessagingActivity")) {
Intent intent = new Intent("update_messages_list");
intent.putExtra("data", extras);
sendBroadcast(intent);
} else {
Intent resultIntent = new Intent(this, MessagingActivity.class);
resultIntent.putExtra("conversationID", Integer.parseInt(extras.getString("conversationID")));
resultIntent.putExtra("recipientID", Integer.parseInt(extras.getString("ownerID")));
M.showNotification(getApplicationContext(), resultIntent,
extras.getString("ownerUsername"),
extras.getString("message"),
Integer.parseInt(extras.getString("conversationID")));
}
Let me know how you are checking that your MessageActivity is Running i.e. functioning of isRunning("MessagingActivity") method. If you are setting any global boolean variable for checking this and making isRunning value false in onDestroy() method of that activity then, according to life cycle of Activity it is not called until your activity is finished i.e. in your case user just switching from MessageActivity to Mail .
I am by no means an expert, but you could just set a boolean variable by overriding the Activity's onPause() and onResume() events.
Simply set msgActivityActive to true in onResume(), false in onPause(), and change your call to:
if (isRunning("MessagingActivity") && msgActivityActive)