onStartCommand is never invoked - android

I'm running a service from MainActivity by
startService(mServiceIntent)
But there is only onCreate method invoked.I need to collect some extras arguments from Intent so I want to onStartCommand to be invoked too. Do You know what is the reason it's not happening. My Service code.
class ImportantService : Service() {
var phoneListener : MyPhoneStateListener? = null
var listening = false
var telephony: TelephonyManager? = null
override fun onBind(intent: Intent): IBinder {
TODO("Return the communication channel to the service.")
}
override fun onCreate() {
super.onCreate()
Log.d("ImportantService", "On Create")
//startListen()
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
val builder = NotificationCompat.Builder(this, "ch1")
.setContentText("Content text")
.setContentTitle("Content title")
startForeground(101,builder.build())
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return super.onStartCommand(intent, flags, startId)
// this method is never invoked
Log.d("ImportantService", "On Start")
if(listening)
{
Log.d("ImportantService", "Started")
startListen()
}
else
{
Log.d("ImportantService", "Stopped")
// stopListen()
}
}
fun startListen()
{
Log.d("Service", "startListen")
}
}

Make sure to move return super.onStartCommand(intent, flags, startId) to the end of your function.

Related

FirebaseMessagingService immediately destroyed after being created

I am using a BroadcastReceiver to send a broadcast to restart the FirebaseMessagingService when the service is destroyed. However, when starting service this way, the FirebaseMessagingService gets immediately destroyed. When the service is destroyed, I will send another broadcast to the receiver and the loop continues indefinitely.
My code for FirebaseMessagingService.
#AndroidEntryPoint
class MessagingService :
FirebaseMessagingService(),
FirebaseAuth.AuthStateListener {
companion object {
var isRunning = false
const val REQUEST_CODE = 0
const val NOTIFICATION_ID = 0
}
#Inject
lateinit var notificationRepository: NotificationRepository
#Inject
lateinit var authRepository: AuthRepository
override fun onCreate() {
super.onCreate()
Log.d("MessagingService", "I am created")
isRunning = true
FirebaseAuth.getInstance().addAuthStateListener(this)
}
override fun onDestroy() {
super.onDestroy()
Log.d("MessagingService", "I am destroyed")
isRunning = false
FirebaseAuth.getInstance().removeAuthStateListener(this)
val intent = Intent()
intent.action = "restartMessagingService"
intent.setClass(this, MessagingServiceRestarter::class.java)
this.sendBroadcast(intent)
}
override fun onMessageReceived(remoteMessage: RemoteMessage) {
val title = remoteMessage.notification?.title
val message = remoteMessage.notification?.body
if (message != null && title != null) {
sendPushNotification(title, message)
}
}
override fun onNewToken(token: String) {
saveTokenToSharedPreferences(token)
authRepository.getUser()?.let {
val uid = it.uid
sendTokenToFirestore(uid, token)
}
}
override fun onAuthStateChanged(auth: FirebaseAuth) {
auth.currentUser?.let {
val uid = it.uid
val savedRegistrationToken =
PreferenceManager.getDefaultSharedPreferences(this)
.getString(getString(R.string.fcm_token_shared_pref_key), "")
savedRegistrationToken?.let { token -> sendTokenToFirestore(uid, token) }
}
}
private fun sendPushNotification(title: String, messageBody: String) {
val intent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(this, REQUEST_CODE,
intent, PendingIntent.FLAG_UPDATE_CURRENT)
val channelId = getString(R.string.general_notification_channel_id)
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notificationBuilder = NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.mlearn_logo)
.setContentTitle(title)
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent)
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channel = NotificationChannel(
channelId,
getString(R.string.general_notification_channel_name),
NotificationManager.IMPORTANCE_DEFAULT
)
notificationManager.createNotificationChannel(channel)
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build())
}
private fun saveTokenToSharedPreferences(token: String) {
val sharedPref = PreferenceManager.getDefaultSharedPreferences(this)
with (sharedPref.edit()) {
putString(getString(R.string.fcm_token_shared_pref_key), token)
apply()
}
}
private fun sendTokenToFirestore(uid: String, token: String) {
GlobalScope.launch(Dispatchers.IO) {
notificationRepository.sendNotificationToken(uid, token)
}
}
}
My code for the receiver.
class MessagingServiceRestarter : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("MessagingService", "Restarter" + MessagingService.isRunning.toString())
if (!MessagingService.isRunning) {
context.startService(Intent(context, MessagingService::class.java))
MessagingService.isRunning = true
}
}
}
I start my service every time the MainActivity is created.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("MessagingService", "MainActivity" + MessagingService.isRunning.toString())
if (!MessagingService.isRunning) {
val intent = Intent(this, MessagingService::class.java)
startService(intent)
MessagingService.isRunning = true
}
}
override fun onDestroy() {
val intent = Intent()
Log.d("MessagingService", "MainActivity" + MessagingService.isRunning.toString())
intent.action = "restartMessagingService"
intent.setClass(this, MessagingServiceRestarter::class.java)
sendBroadcast(intent)
super.onDestroy()
}
And I have registered my service and receiver on manifest.
<service
android:name=".service.MessagingService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<receiver
android:name=".receiver.MessagingServiceRestarter"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="restartMessagingService"></action>
</intent-filter>
</receiver>
How can I keep my FirebaseMessagingService running? I am using this receiver to restart the service when the service is stopped because the app is cleared from the recent apps.

How to work with background api calls like in Gmail?

I would like to make api calls in background and show sometimes notifications due to api data. I had created service which make all calls:
class EndlessService : MyService() {
private var wakeLock: PowerManager.WakeLock? = null
private var isServiceStarted = false
override fun onBind(intent: Intent?): IBinder? {
log("Some component want to bind with the service")
// We don't provide binding, so return null
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
log("onStartCommand executed with startId: $startId")
if (intent != null) {
val action = intent.action
log("using an intent with action $action")
when (action) {
Actions.START.name -> startService()
Actions.STOP.name -> stopService()
else -> log("This should never happen. No action in the received intent")
}
} else {
log(
"with a null intent. It has been probably restarted by the system."
)
}
// by returning this we make sure the service is restarted if the system kills the service
return START_STICKY
}
override fun onCreate() {
super.onCreate()
log("The service has been created".uppercase(Locale.ROOT))
startForeground(1, createNotification())
}
override fun onDestroy() {
super.onDestroy()
log("The service has been destroyed".uppercase(Locale.ROOT))
Toast.makeText(this, "Service destroyed", Toast.LENGTH_SHORT).show()
}
private fun startService() {
if (isServiceStarted) return
log("Starting the foreground service task")
Toast.makeText(this, "Service starting its task", Toast.LENGTH_SHORT).show()
isServiceStarted = true
setServiceState(this, ServiceState.STARTED)
// we need this lock so our service gets not affected by Doze Mode
wakeLock =
(getSystemService(Context.POWER_SERVICE) as PowerManager).run {
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "EndlessService::lock").apply {
acquire(10*60*1000L /*10 minutes*/)
}
}
// we're starting a loop in a coroutine
GlobalScope.launch(Dispatchers.IO) {
while (isServiceStarted) {
launch(Dispatchers.IO) {
pingFakeServer()
}
delay(10 * 60)
}
log("End of the loop for the service")
}
}
private fun stopService() {
log("Stopping the foreground service")
Toast.makeText(this, "Service stopping", Toast.LENGTH_SHORT).show()
try {
wakeLock?.let {
if (it.isHeld) {
it.release()
}
}
stopForeground(true)
stopSelf()
} catch (e: Exception) {
log("Service stopped without being started: ${e.message}")
}
isServiceStarted = false
setServiceState(this, ServiceState.STOPPED)
}
private fun pingFakeServer() {
}
private fun createNotification(): Notification {
val notificationChannelId = "ENDLESS SERVICE CHANNEL"
// depending on the Android API that we're dealing with we will have
// to use a specific method to create the notification
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager;
val channel = NotificationChannel(
notificationChannelId,
"Endless Service notifications channel",
NotificationManager.IMPORTANCE_MIN
).let {
it.description = "Endless Service channel"
it.enableLights(true)
it.lightColor = Color.RED
it.enableVibration(true)
it.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
it
}
notificationManager.createNotificationChannel(channel)
}
val pendingIntent: PendingIntent = Intent(this, MainActivity::class.java).let { notificationIntent ->
PendingIntent.getActivity(this, 0, notificationIntent, 0)
}
val builder: Notification.Builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) Notification.Builder(
this,
notificationChannelId
) else Notification.Builder(this)
return builder
.setContentTitle("Endless Service")
.setContentText("This is your favorite endless service working")
.setContentIntent(pendingIntent)
.setSmallIcon(R.mipmap.ic_launcher)
.setTicker("Ticker text")
.setPriority(Notification.PRIORITY_MIN) // for under android 26 compatibility
.build()
}
}
then I added all info to the manifest and added classes for this service support:
enum class Actions {
START,
STOP
}
enum class ServiceState {
STARTED,
STOPPED,
}
private const val name = "SPYSERVICE_KEY"
private const val key = "SPYSERVICE_STATE"
fun setServiceState(context: Context, state: ServiceState) {
val sharedPrefs = getPreferences(context)
sharedPrefs.edit().let {
it.putString(key, state.name)
it.apply()
}
}
fun getServiceState(context: Context): ServiceState {
val sharedPrefs = getPreferences(context)
val value = sharedPrefs.getString(key, ServiceState.STOPPED.name)
return ServiceState.valueOf(value!!)
}
private fun getPreferences(context: Context): SharedPreferences {
return context.getSharedPreferences(name, 0)
}
and start it from an activity:
override fun onResume() {
super.onResume()
actionOnService(Actions.STOP)
}
override fun onPause() {
super.onPause()
actionOnService(Actions.START)
}
private fun actionOnService(action: Actions) {
if (getServiceState(this) == ServiceState.STOPPED && action == Actions.STOP) return
Intent(this, EndlessService::class.java).also {
it.action = action.name
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
log("Starting the service in >=26 Mode")
startForegroundService(it)
return
}
log("Starting the service in < 26 Mode")
startService(it)
}
}
but as I see such way creates empty notification which can't be dismissed. Maybe I can use another way?

How can i get geofence event in a foreground service

it is my first time using maps sdk and i am having a lot of trouble to learn how to use it. I am creating an application which will send user's location to backend for each significant displacement, and a choose to use geofence to do it.
I built a foreground to detect when the user moves, even when his application is not open, but i can't get any event through the broadcast, even the initial trigger it cant hear, i am testing manually using fake gps app.
foreground service:
class LocationForegroundService : Service() {
private lateinit var geofencingClient: GeofencingClient
private lateinit var locationClient: FusedLocationProviderClient
val trackingWorking: Boolean get() = geoAlreadyInitialized
private val pendingBroadcastIntent: PendingIntent by lazy {
val intent = Intent(this, GeofenceReceiver::class.java)
PendingIntent.getBroadcast(
this,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
)
}
companion object {
private var geoAlreadyInitialized = false
private const val GEOFENCE_ID = "rider_location"
private const val TRACKING_CHANNEL = "tracking_channel"
private const val TRACKING_NOTIFICATION_ID = 1
private var count = 0
fun startService(context: Context) {
val startIntent = Intent(context, LocationForegroundService::class.java)
ContextCompat.startForegroundService(context, startIntent)
}
fun stopService(context: Context) {
val stopIntent = Intent(context, LocationForegroundService::class.java)
context.stopService(stopIntent)
}
}
override fun onCreate() {
super.onCreate()
locationClient = LocationServices.getFusedLocationProviderClient(this)
geofencingClient = LocationServices.getGeofencingClient(this)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
...
getLastLocation()
return START_NOT_STICKY
}
#SuppressLint("MissingPermission")
private fun getLastLocation() {
if (geoAlreadyInitialized) geofencingClient.removeGeofences(pendingBroadcastIntent)
locationClient.lastLocation
.addOnSuccessListener { location ->
createGeofence(location)
++count
}
.addOnFailureListener {
print("fail")
// TODO WHEN NOTIFICATION IS BIND TO THE FRAGMENT SET TO PAUSE
}
}
private fun createGeofence(location: Location) {
val geofence = getGeofence(location)
val geofencingRequest = getGeofenceRequest(geofence)
createGeofenceEventListeners(geofencingRequest)
}
#SuppressLint("MissingPermission")
private fun createGeofenceEventListeners(geofencingRequest: GeofencingRequest) {
geofencingClient.addGeofences(geofencingRequest, pendingBroadcastIntent)
.addOnSuccessListener {
geoAlreadyInitialized = true
}
.addOnFailureListener {
// TODO
}
}
private fun getGeofenceRequest(geofence: Geofence) =
GeofencingRequest.Builder()
.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
.addGeofence(geofence)
.build()
private fun getGeofence(it: Location) = Geofence.Builder()
.setCircularRegion(it.latitude, it.longitude, 5F)
.setRequestId(GEOFENCE_ID)
.setTransitionTypes(GEOFENCE_TRANSITION_EXIT)
.setExpirationDuration(NEVER_EXPIRE)
.setNotificationResponsiveness(0)
.build()
private fun createNotificationChannel() {...}
override fun onBind(intent: Intent?): IBinder? = null
class GeofenceReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val geofencingEvent = GeofencingEvent.fromIntent(intent)
if (geofencingEvent.hasError()) {
return
}
when (geofencingEvent.geofenceTransition) {
GEOFENCE_TRANSITION_EXIT -> {
context?.showShortToast("EXIT geo blabla")
}
GEOFENCE_TRANSITION_ENTER -> {
context?.showShortToast("ENTER geo blabla")
}
GEOFENCE_TRANSITION_DWELL -> {
context?.showShortToast("dwell geo blabla")
}
}
}
}
}
Manifest
<receiver android:name=".util.LocationForegroundService$GeofenceReceiver"/>
to be more precise the broadcast is never called

How to stop thread inside Android service

java.lang.InterruptedException error is thrown onDestroy because of the Thread.sleep in the Runnable. I thought that the Runnable created a new Thread and allowed for sleeps?
MainActivity.kt
class MainActivity : AppCompatActivity() {
lateinit var startBtn: Button
lateinit var stopBtn: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
startBtn = findViewById(R.id.startButton)
stopBtn = findViewById(R.id.stopButton)
startBtn.setOnClickListener { startService() }
stopBtn.setOnClickListener { stopService() }
}
private fun startService() {
val serviceIntent = Intent(this, ForegroundService::class.java)
ContextCompat.startForegroundService(this, serviceIntent)
}
private fun stopService() {
val serviceIntent = Intent(this, ForegroundService::class.java)
stopService(serviceIntent)
}
}
ForegroundService.kt
class ForegroundService : Service() {
private lateinit var thread: Thread
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel("channel_service", "Foreground Service Channel", NotificationManager.IMPORTANCE_DEFAULT)
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
val notification: Notification = Notification.Builder(this, "channel_service")
.setContentTitle("Title")
.setContentText("Text")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.build()
startForeground(1, notification)
runnable = TestRunnable()
thread = Thread(runnable)
thread.start()
}
return START_NOT_STICKY
}
override fun onDestroy() {
if(thread.isAlive) {
thread.interrupt()
}
super.onDestroy()
}
override fun onBind(intent: Intent): IBinder? {
return null
}
class TestRunnable : Runnable {
override fun run() {
for(i in 1..15) {
Log.d("Debug", "startThread " + i)
Thread.sleep(1000) //exception through here onDestroy
}
}
}
}
Use thread.interrupt() to interrupt the thread. Add try-catch around Thread.sleep(1000) call:
class TestRunnable : Runnable {
override fun run() {
var isInterrupted = false
for(i in 1..15) {
Log.d("Debug", "startThread " + i)
try {
Thread.sleep(1000)
} catch (e: InterruptedException) {
isInterrupted = true
}
if (isInterrupted) break
}
}
}
You can also use some AtomicBoolean variable to break the loop in Runnable implementation:
class TestRunnable : Runnable {
private val isStopped = AtomicBoolean(false)
fun stop() {
isStopped.set(true)
}
override fun run() {
for(i in 1..15) {
if (isStopped.get()) break
// ...
}
}
}
override fun onDestroy() {
if(thread.isAlive) {
runnable.stop()
thread.interrupt()
}
super.onDestroy()
}
Add thread = null; after Thread(runnable).interrupt(). This should work. If you want it to be killed when the user stops the activity you should try overriding onStop instead of onDestroy().
Hope it helps! Happy Coding:)

I want my service and broadcast receiver to run even if app is cleared from app list or forced stop

I want my service and broadcast receiver to run in background even if app is closed or force stopped.
My receiver is detecting screen on response and doing the task.
Its working fine when app is open and when app is in app list but not closed.
When i clear the app from app list, it is not detecting the screen on response.
I have tried many ways but couldn't succeed. Help me out.
Manifest file :-
<receiver
android:name=".broadcastReceiver.MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.SCREEN_OFF"/>
<action android:name="android.intent.action.SCREEN_ON"/>
</intent-filter>
</receiver>
<service
android:name=".service.MyService"
android:enabled="true"
android:exported="true"
android:stopWithTask="false">
</service>
MyService class :-
private var myReceiver:MyReceiver?=null
private var defaultUEH:Thread.UncaughtExceptionHandler?=null
private var uncaughtExceptionHandler = object:Thread.UncaughtExceptionHandler{
override fun uncaughtException(thread: Thread?, ex: Throwable?) {
Log.d("Exception found","Uncaught exception start!")
ex!!.printStackTrace()
unregisterReceiver(myReceiver)
var intent = Intent(applicationContext, MyService::class.java)
intent.setPackage(packageName)
var pendingIntent = PendingIntent.getService(applicationContext,1,intent,PendingIntent.FLAG_ONE_SHOT)
var alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.set(AlarmManager.ELAPSED_REALTIME,SystemClock.elapsedRealtime()+1000,pendingIntent)
System.exit(2)
}
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
var intentFilter = IntentFilter("android.intent.action.SCREEN_OFF")
intentFilter.addAction("android.intent.action.SCREEN_ON")
myReceiver = MyReceiver()
registerReceiver(myReceiver , intentFilter)
return START_STICKY
}
override fun onCreate() {
super.onCreate()
Log.d("Service" , "onCreate fun")
}
override fun onDestroy() {
super.onDestroy()
Log.d("Service" , "onDestroy fun")
}
override fun onTaskRemoved(rootIntent: Intent?) {
unregisterReceiver(myReceiver)
var intent = Intent(applicationContext, MyService::class.java)
intent.setPackage(packageName)
var pendingIntent = PendingIntent.getService(this,1,intent,PendingIntent.FLAG_ONE_SHOT)
var alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.set(AlarmManager.ELAPSED_REALTIME,SystemClock.elapsedRealtime()+1000,pendingIntent)
super.onTaskRemoved(rootIntent)
}
MyReceiver class:-
override fun onReceive(context: Context?, intent: Intent?) {
Log.d("Where ","Broadcast")
if (intent!!.action.equals(Intent.ACTION_SCREEN_ON)){
var dashboardActivity = DashboardActivity.instance
var isSosEnabled = dashboardActivity!!.getSos()
if (isSosEnabled){
Log.d("Service Sos Enabled","True")
}else{
Log.d("Service Sos Enabled", "False")
}
}
}
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);
}
}

Categories

Resources