Unable to start service lateinit property has not been initialized - android

Hi and thank you for your expertise.
I am trying to run a service which is going to display a notification and after some time check if it is still there or user has got rid of it.
So in the MainActivity.kt when onPause function is triggered I start the service like this
startService(Intent(this,NewService::class.java))
then in the NewService.kt I would like to check using MainActivity.kt function checkNotifications() if there is a notification displayed
class NewService:Service() {
#RequiresApi(Build.VERSION_CODES.M)
override fun onStartCommand(init : Intent, flag : Int, startId: Int):Int{
MainActivity().checkNotifications()
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
}
override fun onBind(p0: Intent?): IBinder? {
return null
}
}
This is what checkNotifications() function in MainActivity.kt looks like
#RequiresApi(Build.VERSION_CODES.M)
fun checkNotifications(){
val notifications: Array<StatusBarNotification> = notificationManager.activeNotifications
if(notifications.isNotEmpty())
{
println("Notification exists")
}
else
{
println("No notifications")
}
}
So my app works 'fine' as long as I don't call checkNotifications(). What I mean the service starts and so on. However when I try to call checkNotifications() I get this error
java.lang.RuntimeException: Unable to start service abc.com.app.NewService#b380e9f with Intent { cmp=abc.com.app/.NewService }: kotlin.UninitializedPropertyAccessException: lateinit property notificationManager has not been initialized
The property notificationManager is initialized in onCreate() function and everyting is working up until I call checkNotifications()
Would anyone be so kind and tell me what am I doing wrong?
Thank You

MainActivity().checkNotifications()
Never create an instance of an activity yourself. Move checkNotifications() into the service or into some utility class (where you provide a Context as a parameter to checkNotifications() for the purposes of obtaining a NotificationManager).

Related

Long running background task from BroadcastReceiver - which class to use?

Since AsyncTask, IntentSerrvice and JobIntentService are all deprecated, which tool or class should I go for in 2022?
I want to re-schedule alarms in a BroadcastReceiver after a device rebooted (since alarms get lost in the process). The task will most probably take < 1 min to finish. I just need the safety of it completing and not being killed off by the system.
The documentation on Broadcasts shows an (outdated) example with goAsync() and the deprecated AsyncTask.
But it also mentions JobService. Is that the replacement? What about WorkManager?
goAsync() return a PendingIntent - it mean you ask for android system extend time life of Broadcast receiver => goAsync() is used for short background task.
Life time of BroadcastReceiver is very short, so... for long time background task, you must to change to other context has longer life time, such as: Service, JobService..etc.
Example:
BroadcastReceiver received intent
BroadcastReceiver start a service, run worker thread to process long time task
after worker thread finish, call finish service too
=========================================
class MyIntentService : Service() {
private val handleThread = HandlerThread("MyThread")
private lateinit var workerHandler: Handler
override fun onCreate() {
super.onCreate()
handleThread.start()
workerHandler = Handler(handleThread.looper)
}
override fun onDestroy() {
workerHandler.removeCallbacksAndMessages(null)
handleThread.quitSafely()
super.onDestroy()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val data = intent?.data
workerTask(data)
return START_NOT_STICKY
}
private fun workerTask(data: Uri?) {
workerHandler.post {
heavyTask(data)
finishMyIntentService()
}
}
private fun finishMyIntentService() {
stopSelf()
}
private fun heavyTask(data: Uri?) {
// to do heavyTask example
for (i in 1..20)
{
Log.d("test","#heavyTask() $i")
Thread.sleep(1000)
}
}
override fun onBind(intent: Intent?): IBinder? {
TODO("Not yet implemented")
}
}
then startService from BroadCastReceiver

Triggering Notification from service when app is in background / killed

I'm making a todolist app where the user needs to add a task along with date and time that i use later to trigger the notification , when app is in foreground , it works fine but since services are submitted to limitations after android oreo , now i'm lost on how to trigger the notification when app is in background or killed , if you guys could enlighten me , i woudl appreciate it
This is my service class
class NotificationService(var context: FragmentActivity) : Service(){
private lateinit var remindersViewModel: remindersViewModel
private lateinit var compat : NotificationManagerCompat
override fun onCreate() {
super.onCreate()
compat = NotificationManagerCompat.from(this)
remindersViewModel = ViewModelProvider(context)[remindersViewModel::class.java]
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
isNotificationEnabled()
return START_NOT_STICKY
}
override fun onDestroy() {
super.onDestroy()
}
private fun isNotificationEnabled(){
//TODO : NOTIFICATION
val notificationPrefs = getSharedPreferences("notificationPrefs", Context.MODE_PRIVATE)
val isNotificationEnabled = notificationPrefs.getBoolean("notification", false)
remindersViewModel.getAllTasks().observe(context, Observer {
if(isNotificationEnabled){
CoroutineScope(Dispatchers.Main).launch {
delay(3000)
HandleOperations.taskNotification(it, context, compat)
}
}
})
}
}
As of Android Oreo, all background services will be killed if not visible by a foreground service. The best case scenario for your problem is to use a Job Scheduler (here).
For timely event based tasks Job Sceduler is the only options though it's no longer available in the support library.
You will also need a Notification Channel for devices with Oreo and above.

how to observe a live data inside service class

I want to insert some records which I get from the API to my database,
I am using a service class to do this process, I was trying to use this concept of live data inside service class, but it required my service class to be a lifecycleowner.
am stuck with how to make my service class observer to the changes in a live data
Any help will be good!!
If your service should not be affected by activity lifecycle (onStop(), onStart() etc) then you can use LiveData<T>.observeForever(Observer<T>) method. Like so,
val observer = Observer<YourDataType> { data ->
//Live data value has changed
}
liveData.observeForever(observer)
To stop observing you will have to call LiveData.removeObserver(Observer<T>). Like so:
liveData.removeObserver(observer)
If you need to stop observing when the application is in the background, you can bind your service in the calling activity in the onStart() method of the activity and unbind your service in the onStop() method. Like so:
override fun onStart() {
super.onStart()
val serviceIntent = Intent(this, myService::class.java)
bindService(serviceIntent, myServiceConnection, Context.BIND_AUTO_CREATE)
}
override fun onStop() {
unbindService(myServiceConnection)
super.onStop()
}
Read on bound services here
Then, in the service
override onBind(Intent) and onRebind(Intent) method and start observing the LiveData (App is in foreground)
override fun onBind(intent: Intent?): IBinder? {
liveData.observeForever(observer)
return serviceBinder
}
override fun onRebind(intent: Intent?) {
liveData.observeForever(observer)
super.onRebind(intent)
}
Remove LiveData observer in onUnbind(Intent) (App is in background)
override fun onUnbind(intent: Intent?): Boolean {
liveData.removeObserver(observer)
return true
}

Kill foreground service when activity destroy

I use foreground service for playing music.
I've done research and figured out that process keep living even if I call stopForeground(true) and stopSelf(). It doesn't seem to work if I call it from the onDestroy of my activity. But if I call them from another place for example when user clicks button, it works.
But how to do that my service to be killed when app is killed.
private const val EMPTY_ROOT = "emptyRoot"
private val TAG = SoundsService::class.java.name
class SoundsService : MediaBrowserServiceCompat() {
#Inject
lateinit var mediaSession: MediaSessionCompat
#Inject
lateinit var callback: MediaSessionCallback
override fun onCreate() {
inject()
super.onCreate()
mediaSession.apply {
setCallback(callback)
isActive = true
}
sessionToken = mediaSession.sessionToken
}
override fun onGetRoot(
clientPackageName: String,
clientUid: Int,
rootHints: Bundle?
): BrowserRoot? {
return BrowserRoot(EMPTY_ROOT, null)
}
override fun onLoadChildren(
parentId: String,
result: Result<MutableList<MediaBrowserCompat.MediaItem>>
) {}
override fun onDestroy() {
mediaSession.run {
isActive = false
release()
}
stopForeground(false)
stopSelf()
super.onDestroy()
}
}
It will work in the onDestroy. Just make sure your are calling it, before super call. And use ApplicationContenxt for that as well.
applicationContext.startForegroundService(Intent(this, ServiceTest::class.java));
applicationContext.stopService(Intent(this, ServiceTest::class.java))
Have you checkt for any crashes when you call stopForeground(true) or stopSelf()

Notification Listener Service not started when running in debug mode

I am building an app using the NotificationListenerService. But always when I run the app in debug mode the Service is not started. I reduced my code to the following:
My Acticity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val intent = Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS")
startActivity(intent)
}
override fun onResume() {
super.onResume()
val isServiceRunning = isMyServiceRunning(NLService::class.java)
Log.i("MainActivity", "service running: " + isServiceRunning)
}
private fun isMyServiceRunning(serviceClass: Class<*>): Boolean {
val manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
for (service in manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.name == service.service.className) {
return true
}
}
return false
}
}
The Service:
class NLService : NotificationListenerService() {
private val TAG: String? = "NLService"
override fun onBind(intent: Intent): IBinder? {
Log.i(TAG, "onBind()")
return super.onBind(intent)
}
override fun onCreate() {
super.onCreate()
Log.i(TAG, "onCreate()")
}
override fun onDestroy() {
super.onDestroy()
}
override fun onNotificationPosted(sbn: StatusBarNotification?) {
Log.i(TAG, "onNotificationPosted() sbn: $sbn")
super.onNotificationPosted(sbn)
}
}
Of course I added this in manifest:
<service
android:name=".NLService"
android:label="MyNLService"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
When starting the app in debug mode then I always get the log output service running: false in onResume. The value is true when starting normally, without debug. What is going wrong and how to fix that?
Okay, finally I don't have a complete solution but a kind of improvement which is close to a working solution.
So what are actually the technical problems in my original app code? And how did I solve them?
I made some initialization in NLService class' onConnect() method. I moved all this initialization to onListenerConnected(), adding a handler.postDelayed(runnable, 500);.
I created an object of a class (Let's call it MyHandlerClass) within the NLService class and passed a reference to the Service into it. This is not a good solution because Android documentation says something about many methods within the NotificationListenerService:
The service should wait for the onListenerConnected() event before performing this operation.
So in MyHandlerClass I called a nlService.getActiveNotifications(). This call was made maybe before Android called NLServices' onListenerConnected. So I made wrappers for methods inside NLService, like e.g.:
fun getActiveNotifications(): Array<StatusBarNotification>?
{
return if (isConnected)
{
super.getActiveNotifications()
}
else
{
null
}
}
And I toggled my boolean variable isConnected within onListenerConnected()and onListenerDisconnected()
Now the service still crashes when running app in debug mode. But running in normal mode the amount of crashes could be reduced by the described improvements.

Categories

Resources