Service gets killed when app closes - java

I have a main service which should always be running in the background in my Android application for handling a bluetooth device connection and feeding data to it.
According to these questions, it is a normal behavior if my service live in my app process to get killed when app closes,
Android Background Service is restarting when application is killed
Service restarted on Application Close - START_STICKY
keeping background service alive after user exit app
But I even tried running my service in a separate process using this tag android:process=":service" inside my manifest but it also get killed and restarted when my app get killed!
More info:
I start my service in my application onCreate method, and for binding to my service in my activity I use BIND_AUTO_CREATE, which generally I am not sure is it correct or not.
update:
I also have another service which bind inside my current service, I am not sure if it might be source of issue!
more update:
I am using dagger for DI, is it possible by mistake I am using application context for creating some objects inside my service!! could this be the cause of this issue?
some more update
I separate dagger components for my service and now application and service got no common objects, but problem still remains.
update with a sample code which got the same issue
Here is the Application class:
class MyApplication:Application() {
override fun onCreate() {
super.onCreate()
startService(Intent(this, MyService::class.java))
}
}
Here is the Service class:
class MyService : Service() {
private val mBinder = MyBinder()
inner class MyBinder : Binder() {
internal val service: MyService
get() = this#MyService
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.i(TAG, "onStartCommand")
return START_STICKY
}
override fun onCreate() {
super.onCreate()
Log.i(TAG, "onCreate")
}
override fun onBind(intent: Intent): IBinder? {
Log.i(TAG, "onBind")
return mBinder
}
override fun onUnbind(intent: Intent): Boolean {
Log.i(TAG, "onUnbind")
return super.onUnbind(intent)
}
override fun onDestroy() {
Log.i(TAG, "onDestroy")
super.onDestroy()
}
companion object {
val TAG = "MyService"
}
}
This is the Activity:
class MainActivity : AppCompatActivity() {
lateinit var context: Context
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.i(TAG, "onCreate")
context = this
}
//connect to the service
val myServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
Log.i(TAG, "onServiceConnected")
val binder = service as? MyService.MyBinder
// ...
}
override fun onServiceDisconnected(name: ComponentName?) {
Log.i(TAG, "onServiceDisconnected")
}
}
override fun onResume() {
super.onResume()
Log.i(TAG, "onResume")
val intent = Intent(context, MyService::class.java)
bindService(intent, myServiceConnection, Context.BIND_AUTO_CREATE);
}
override fun onPause() {
super.onPause()
Log.i(TAG, "onPause")
unbindService(myServiceConnection)
}
companion object {
val TAG = "MainActivity"
}
}
Last but not least, the Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mirhoseini.stickyservice">
<application
android:name=".MyApplication"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"
android:process=":service" />
</application>
</manifest>

It is a normal behavior for a service to stop when the application main thread stops.
Personally, I don't agree with using an internal service in a separate process for the regular development and functionality sharing between modules. A worker or IntentService is the more appropriate candidate most of the times.
To keep your service alive after user exits the app, try one of the scheduled threading mechanism that suits your needs, best :
1- TimerTask ( not really recommended !)
2- Executors.newScheduledThreadExecutor
3- AlarmManager
4- JobScheduler

Related

Making app service work invisible in background

i have an app that runs a background service infinitely while also hiding the app icon and hiding it from the backstack so it wont be visible to the user while running using:
val componentName = ComponentName(this, FullscreenActivity::class.java)
p.setComponentEnabledSetting(
componentName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP
)
this is how i launch the service from my fragment :
parentFragment?.activity?.startService(
Intent(
requireParentFragment().requireActivity().applicationContext,
RescueService::class.java
).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
)
which successfully hides the icon and the app from backstack and the service keep running, also when i press the square button on my phone to show the backstack apps running i dont see the app there which is what i want, but if i click the "X" button to clear all backstack apps it also kills my app and my service dies even though the app does not appear there as i mentioned.
Any ideas what makes the service die after doing it? because it did not happen to couple of days ago before i did some changes in my app..
One of the changes i made is turnning my Service into LifecycleService because i needed my service to observe a livedata which needs a LifecycleOwner.Can it be the cause of my service to die when clearing the backstack?
All suggestions will be welcomed !
EDIT1 - here is my TestService that performs voice recording uses WorkManager.I call hideApp() to hide the icon from the phone and make the app invisible from the backstack :
class RecordingWork(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override val coroutineContext = Dispatchers.Main
override suspend fun doWork(): Result {
mSpeechRecognizer.startListening(mSpeechRecognizerIntent)
return Result.success()
}
}
private lateinit var mSpeechRecognizer: SpeechRecognizer
private val mSpeechRecognizerIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
class TestService : Service() {
private lateinit var workManager: WorkManager
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d("TestService", "onStartCommand called")
hideApp()
prepareVoiceRecording()
workManager = WorkManager.getInstance(applicationContext)
workManager.enqueue(OneTimeWorkRequestBuilder<RecordingWork>().build())
return START_REDELIVER_INTENT
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
fun prepareVoiceRecording() {
mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(applicationContext)
mSpeechRecognizerIntent.putExtra(
RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
)
mSpeechRecognizerIntent.putExtra(
RecognizerIntent.EXTRA_LANGUAGE,
Locale.getDefault()
)
mSpeechRecognizer.setRecognitionListener(
object : RecognitionListener {
#RequiresApi(Build.VERSION_CODES.M)
override fun onError(i: Int) {
Log.d(TAG, "onErrorCalled error is $i")
if (i == SpeechRecognizer.ERROR_NETWORK || i == SpeechRecognizer.ERROR_SPEECH_TIMEOUT || i == SpeechRecognizer.ERROR_NO_MATCH) {
Log.d(TAG, "error triggered")
mSpeechRecognizer.destroy()
prepareVoiceRecording()
workManager.enqueue(OneTimeWorkRequestBuilder<RecordingWork>().build())
}
}
#RequiresApi(Build.VERSION_CODES.M)
override fun onResults(bundle: Bundle) {
Log.d(TAG, "onResults Called")
//getting all the matches
val matches = bundle
.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION)!!
workManager.enqueue(OneTimeWorkRequestBuilder<RecordingWork>().build())
}
private fun hideApp() {
val componentName = ComponentName(
this,
WelcomeScreenActivity::class.java
)
packageManager.setComponentEnabledSetting(
componentName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP
)
}
this service does work to repeatedly record voice by rescheduling the Worker every time. but this service also dies when clearing backstack (even though the app does not appear there because of hideApp())
Use workManager for long running background task (some device like Mi phone you need to white list the app for battery saver if you don't do that the OS will kill your app) and hiding of app icon not work in android X (only system apps can hide their icon)

How can I randomly reward points for app usage

I am working on an android app where user get points for using the app which can be used to unlock in-app features.
I have a function called rewardPoints() which generates random integer and I want it to get called randomly while the user is using the app. The points then gets added up in database.
fun rewardPoints() {
var points = Random().nextInt((5-1) + 1)
}
How do I call the function rewardPoints() randomly while the user is using/interacting with the app?
I'd use a Handler to post a Runnable that re-posts itself. Like so,
val handler = Handler()
handler.post({
rewardPoints()
handler.postDelayed(this, DELAY_TIME_MS)
})
You could kick this off in your Activity's onResume and stop it onPause to make sure it's only running when the app is active.
You could add an observer on your activities, check whether you have active activities and when that's the case start a periodic task to award points.
Sample:
class MyApp : Application(), Application.ActivityLifecycleCallbacks {
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(this)
}
var count: Int by Delegates.observable(0) { _, old, newValue ->
when (newValue) {
0 -> onBackground()
1 -> if (old == 0) onForeground()
}
}
override fun onActivityResumed(activity: Activity?) {
count++
}
override fun onActivityPaused(activity: Activity?) {
count--
}
fun onForeground() {
Log.d("TAG", "start.")
events.start()
}
fun onBackground() {
Log.d("TAG", "stop.")
events.cancel()
}
val events = object: CountDownTimer(Long.MAX_VALUE, 1000) {
// is called once per second as long as your app is in foreground
override fun onTick(millisUntilFinished: Long) {
if (ThreadLocalRandom.current().nextInt(100) < 5) {
Toast.makeText(this#MyApp, "You earned a point.", Toast.LENGTH_SHORT).show()
}
}
override fun onFinish() { /* will never happen */}
}
/* not needed */
override fun onActivityStarted(activity: Activity?) {}
override fun onActivityDestroyed(activity: Activity?) {}
override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {}
override fun onActivityStopped(activity: Activity?) {}
override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {}
}
If you use architecture components Lifecycle, implementing above is even simpler with https://developer.android.com/reference/android/arch/lifecycle/ProcessLifecycleOwner and listening to the desired Lifecycle.Event

Class not found in kotlin?

Intent intent=new Intent(MainActivity.this, ContactListActivity.class);
startActivity(intent);
MainActivity.this is written in java and ContactListActivity is written in kotlin. I am trying to call the ContactListActivty but end up getting the error
have you declared this activity in your AndroidManifest.xml?
I have also added the activity in the manifest file.
<activity android:name="Chat_Activity$Connections$ContactListActivity"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity"/>
</activity>
EDIT:
ContactListActivity.kt
class ContactListActivity : AppCompatActivity() {
private var mBroadcastReceiver: BroadcastReceiver? = null
private val TAG:String="ContactListActivity"
//static variable
object Obz{
#JvmStatic val GetContactListFromServer:String="Contact List"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(layout.activity_contact_list)
title = "Contact list"
contact_list.hasFixedSize()
contact_list.layoutManager= LinearLayoutManager(this)
getContactListAndNextActivity()
}
fun getContactListAndNextActivity(){
contact_list.adapter= ContactListAdaptor(applicationContext, dbHelper(applicationContext).getContactList())
}
}
Your manifest name entry looks suspect.
android:name="Chat_Activity$Connections$ContactListActivity"
I'm not sure what the fully qualified path name is, but try:
android:name=".ContactListActivity"
or
android:name="<qualified.path.to>.ContactListActivity"
I had similar problem, Try to rename Your activity add 1 symbol for ex: and inside your manifest
write
android:name=".ContactListActivity1"
after that you can rename again and set it old name

registerReceiver() shows error for overloaded method

I was trying Kotlin in my Android project. There is a broadcast receiver for network change events in my activity. Code is as below:
BaseActivity.kt
abstract class BaseActivity : AppCompatActivity() {
private val networkChangeReceiver = NetworkChangeReceiver()
override fun onStart() {
super.onStart()
registerReceiver(
receiver = networkChangeReceiver,
intentFilter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
)
}
override fun onStop() {
super.onStop()
unregisterReceiver(receiver = networkChangeReceiver)
}
}
NetworkChangeReceiver.kt
class NetworkChangeReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.e("network changed")
}
}
I don't see any problem in it. But my kotlin plugin shows me following error:
None of following functions can be called with arguments supplied
The arguments supplied for first one is correct ASAFIK. I am not an expert in Kotlin, just learning it for fun. Is this intended behaviour of Kotlin, error in plugin or am I missing something? Can anyone explain?
First the parameter is named filter and not intentFilter and if you correct this, you get another error. Which says: "Named arguments are not allowed for non-Kotlin functions" Reason for this is that the method public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) is from Android and written in java. Removing the names should work fine:
registerReceiver(
networkChangeReceiver,
IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
)

Getting java.lang.ClassCastException: android.os.BinderProxy every time i declare and run two services

I am encountering following binder.proxy exception every time i declare and run two services. One service runs in different Process(Private to app) and another service runs in same process as My Application is running in(Default App Process) with a Binder Implementation.
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.service.check"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="21" />
<application
android:name="com.service.check.MainApplication"
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="com.service.check.SecondService"
android:exported="false"/>
<service
android:name="com.service.check.FirstService"
android:process=":newProcess" >
</service>
</application>
</manifest>
I am launching my first service in MainActivity on Button click as:
MainActivity.java
public class MainActivity extends ActionBarActivity implements OnClickListener {
private Button mLanchServiceBtn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLanchServiceBtn=(Button) findViewById(R.id.launch_btn);
mLanchServiceBtn.setOnClickListener(this);
}
#Override
public void onClick(View v) {
//Starting first service
Intent launch=new Intent(this,FirstService.class);
startService(launch);
}
}
And second service in MainApplication class as.
MainApplication.java
public class MainApplication extends Application {
private SecondService.LocalBinder mBinder;
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
mBinder = (LocalBinder) service;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
}
};
#Override
public void onCreate() {
super.onCreate();
//starting second service
Intent launch=new Intent(this,SecondService.class);
startService(launch);
//Binding to it
bindService(launch, mConnection, BIND_AUTO_CREATE);
}
}
FirstService.java
public class FirstService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
SecondService.java
public class SecondService extends Service{
//Service Containing Local Binder
private LocalBinder mBinder=new LocalBinder();
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
class LocalBinder extends Binder{
public LocalBinder() {
}
}
}
StackTrace:
02-05 10:32:25.035: E/AndroidRuntime(1424): Process:
com.service.check:newProcess, PID: 1424 02-05 10:32:25.035:
E/AndroidRuntime(1424): java.lang.ClassCastException:
android.os.BinderProxy cannot be cast to
com.service.check.SecondService$LocalBinder 02-05 10:32:25.035:
E/AndroidRuntime(1424): at
com.service.check.MainApplication$1.onServiceConnected(MainApplication.java:23)
02-05 10:32:25.035: E/AndroidRuntime(1424): at
android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1101)
I have referred the following links to sort out the issue which says,
if my activity and service are in separate processes then we should not bind the way I have done.
Android service android.os.BinderProxy error
java.lang.ClassCastException: android.os.BinderProxy cannot be cast to LocalBinder
But in my case:
I am binding to SecondService from MainApplication and both are running in same Process(i.e Default Application Process). Still I am facing binderProxy exception in SecondService , And my FirstService runs in separate process which I am not even binding to.
Please help me out with this situation and, Suggest me a best possible way so that I can implement same scenario without any crash.
Ran into this issue (local service returning a BinderProxy), wanted to post what I'd found since I found this page while trying to debug. The short version as a run on sentence: starting a remote service creates a second instance of your Application class in a new process which then tries to bind to the local service that was started by the original Application instance as if it was a local service but since the service is running in the original process it's binding across processes and you get a BinderProxy instead of your expected Binder class.
There's a few things to keep in mind about Android services. Every service has an assigned process it will run in. If you don't assign a process in your Android Manifest it will run in the default process (the process where the Application, Activities, etc are run). Not giving a process name doesn't mean that it will run the service in the same process that you're binding to/starting the service from.
Let's say I have a MyApplication class which attempts to bind to two services on start up: one service running in the default process (we'll call this the LocalService), one running in a separate process (the RemoteService).
The user launches my app which creates a MyApplication instance in the default process. This instance then tries to bind to the LocalService. Android creates the LocalService in the default process and returns the LocalService's Binder class to the app (mBinder = (LocalBinder) service;). That's all good, we've successfully bound to the LocalService.
Next the app tries to bind to the RemoteService. Android creates a new process with the name you've supplied in the Android Manifest. However, before it can create the RemoteService it needs to create an Application for the service to run in. It creates a new MyApplication instance in the remote process and starts that up.
However, that new MyApplication instance running in a separate process tries to bind to the LocalService during start up. Because the LocalService is running in the default process this is a cross process binding but MyApplication expects this to be an in process binding. Android returns a BinderProxy, the second MyApplication instance tries to cast it to a LocalBinder and crashes. The fun part is that it crashes in a different process so your app and activity can actually continue running. You'll just never be able to bind to the remote service.
If you want to bind to a local service with an Application context and also use a remote service you'll need to handle the fact that Android will create another Application in the remote process when starting the remote service. I haven't bothered to try this (I just made my remote service a local service), but you could probably check the process name during the application's on create and not bind if it's not the default process.
Found an answer after doing some research and debugging,
If we create and bind any service to a MainApplication class(then service gets binded to whole ApplicationContext or BaseContext) and if same application contains other services which are binded to Activity specific Context(s),
//Declared in MainApplication
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
mBinder = (LocalBinder) service;
}
In OnServiceConnected() We will get binder object for both the Services( SecondService Started in MainApplication(registered with BaseContext will get local binderObject) class and FirstService started MainActivity(will get android.os.binderProxyObject hence causing ClassCastException).
So, to fix this issue one has to start all the application
services from any Activity Context rather than using any Global
Application Context. Also this issue is independent of the
Processes
Hence, I moved both SecondService and FirstService into MainActivity
Context which fixed the issue.
MainActivity.java
private Button mLanchServiceBtn;
private SecondService.LocalBinder mBinder;
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
mBinder = (LocalBinder) service;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLanchServiceBtn=(Button) findViewById(R.id.launch_btn);
mLanchServiceBtn.setOnClickListener(this);
//starting second service in activity
Intent launch=new Intent(this,SecondService.class);
startService(launch);
//Binding to it
bindService(launch, mConnection, BIND_AUTO_CREATE);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onClick(View v) {
//Starting FirstService also from MainActivity
Intent launch=new Intent(this,FirstService.class);
startService(launch);
}
}
You can't call directly any methods of your remote services (or cast) because they live in different process so you can't get a reference to it's instance. But Android has specific interfaces to handle this interprocess communications (IPC). The easiest way is using android.os.Messenger (another is AIDL, more complex).
On your Service, your implementation of Service#onBind() will be a little bit different:
override fun onBind(intent: Intent): IBinder? {
mMessenger = Messenger(YourServiceHandler())
return mMessenger.binder
}
And on your Activity implementation of ServiceConnection#onServiceConnected(serviceBinder: IBinder) you will not get a directly reference to your remote service instance, but instead create a Messenger that have a send(message: Message) interface so you can remotelly call the service functions:
override fun onServiceConnected(className: ComponentName, service: IBinder) {
mServiceMessenger = Messenger(service)
}
override fun onCreate(){
doStuff1Button.setOnClickListener{
val msg = Message.obtain(null, YourRemoteService.MESSAGE_DO_STUFF_1, 0, 0)
mServiceMessenger.send(msg)
}
doStuff1Button.setOnClickListener{
val msg = Message.obtain(null, YourRemoteService.MESSAGE_DO_STUFF_2, 0, 0)
mServiceMessenger.send(msg)
}
}
Note that in the message is going a argument do stuff 1 or 2. You will get this back on your service handler Handler#onHandleMessage(message: Message) with the attribute what:
override fun handleMessage(message: Message) {
when (message.what) {
MESSAGE_DO_STUFF_1 -> doStuff1()
MESSAGE_DO_STUFF_2 -> doStuff2()
}
}
Complete guide can be found in this official documentation
I tried above all solutions but none of those worked. If anyone was stuck same as myself try this based on #Coeffect answer. In my scenario service clients doesn't belong to my current application(process)
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
mBinder = LocalBinder.Stub.asInterface(service);
}

Categories