I've created custom broadcast receiver to listen for battery changes (I would like to monitor the percentage level of the battery)
However I'm not getting any updates. Here is my setup:
class MyService : IntentService("MyService") {
val receiver: PowerConnectionReceiver = PowerConnectionReceiver()
override fun onHandleIntent(intent: Intent?) {
Timber.d("Service started now")
}
override fun onCreate() {
val batteryStatus: Intent? =
IntentFilter(Intent.ACTION_BATTERY_CHANGED).let { ifilter ->
registerReceiver(receiver, ifilter)
}
}
class PowerConnectionReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val status: Int = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
Timber.d("Battery changed")
}
}
}
You will receive updates for the < 1 millisecond time that your IntentService is running. Your IntentService shuts down as soon as onHandleIntent() returns. IntentService is designed for short-term transactional sorts of work, not long-term operation.
Switch to a regular Service. Make sure that it is a foreground service (using startForeground()), as otherwise it will only run for 1 minute on Android 8.0+.
Related
I want to receive an Android Shutdown Event in my app to revert a config change when I start the app. For that, I have register a context registered receiver in my main Activity onCreate as shown below
override fun onCreate(..)
{
...
val bootIntentFilter = IntentFilter(Intent.ACTION_SHUTDOWN);
registerReceiver(shutDownReceiver, bootIntentFilter)
}
private val shutDownReceiver = object : BroadcastReceiver()
{
override fun onReceive(context: Context?, intent: Intent?) {
if(Intent.ACTION_SHUTDOWN == intent?.action)
{
Log.e(TAG, "Shutdown Called")
//Run your logic
}
}
}
and I also added the following permission in manifest.xml
I still don't receive the broadcast is there anything I am missing? target API is 29
Any information to help is really appreciated.
Thanks,
Asma
I will send step count:
Foreground Service -> Broadcast Receiver -> Fragment
Since I need to keep tracking step count on background and when app is off, I've created two Broadcast Receiver, one for notification to keep showing step count on background and one for fragment UI which will be unregistered on Destroy.
So this process is for the latter.
Foreground Service
: I send "stepCount" value to broadcast.
Intent().also { intent ->
intent.setAction(ACTION_STEP_COUNTER_NOTIFICATION)
intent.putExtra("stepCount", "$todayTotalStepCount")
sendBroadcast(intent)
}
BroadcastReceiver
open class StepCountBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
super.onReceive(context, intent)
if(intent!!.action == ACTION_STEP_COUNTER_NOTIFICATION) {
var intent = Intent()
var stepCount = intent.getStringExtra("stepCount")
var sendMyDiary = Intent(context!!, MyDiaryFragment::class.java)
sendMyDiary.putExtra("stepCount", stepCount)
context.startActivity(sendMyDiary)
}
}
}
when custom Action is triggered, this will get 'stepCount' from Service and here I will send it to Fragment named MyDiaryFragment.
So I send it by using Intent().putExtra.
Fragment
private val stepCountBroadcastReceiver: BroadcastReceiver = StepCountBroadcastReceiver()
val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION).apply {
addAction(ACTION_STEP_COUNTER_NOTIFICATION)
}
LocalBroadcastManager.getInstance(requireActivity()).registerReceiver(stepCountBroadcastReceiver, filter)
Here is my question.
I register this StepCountBroadcastReceiver in onCreate.
But I don't know how to get stepCount coming from broadcast in Fragment whenever Broadcastreceiver is called.
It that same process like just adding this below line at the bottom?
var stepCount = requireActivity().intent.getStringExtra("stepCount")
val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION).apply {
addAction(ACTION_STEP_COUNTER_NOTIFICATION)
}
LocalBroadcastManager.getInstance(requireActivity()).registerReceiver(stepCountBroadcastReceiver, filter)
var stepCount = requireActivity().intent.getStringExtra("stepCount")
In your broadcast receiver, add this.To Send a LocalBroadCast
override fun onReceive(context: Context?, intent: Intent?) {
super.onReceive(context, intent)
if(intent?.action == ACTION_STEP_COUNTER_NOTIFICATION) {
var stepCount = intent.getStringExtra("stepCount")
var localBroadCastIntent = Intent(LOCAL_BROADCAST_KEY)
localBroadCastIntent.putExtra("stepCount",stepCount?:"")
Handler(Looper.getMainLooper()).post {
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}
}
}
const val LOCAL_BROADCAST_KEY = "step_counter_local_broadcast"
In your Fragment Call this register function.And in onCreateView and make sure to call unregisterReceiver in ondestroyview of Fragment.
private fun registerBroadCastReceiver() {
LocalBroadcastManager.getInstance(applicationContext).registerReceiver(
receiver,
IntentFilter(LOCAL_BROADCAST_KEY)
)
}
}
Add this BroadCast Receiver,in Fragment Class globally.
private var receiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
intent?.let {
val stepCount = it.getStringExtra("stepCount")
}
}
}
Note:
Here Flow will be like this.
Service -> BroadCast Receiver -> LocalBroadCastReceiver ->
UpdateFragment UI.
You can also directly call this localbroadcast from service based on your usecase. So it will be like below.
Service-> LocalBroadCastReceiver -> UpdateFragment UI.
So only when fragment is alive, the local broadcast receiver will receive messages and update ui.
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
I have a broadcast receiver for wifi scan results as a data source and I'd like to make it in coroutine way. I found an answer for suspend function here:
https://stackoverflow.com/a/53520496/5938671
suspend fun getCurrentScanResult(): List<ScanResult> =
suspendCancellableCoroutine { cont ->
//define broadcast reciever
val wifiScanReceiver = object : BroadcastReceiver() {
override fun onReceive(c: Context, intent: Intent) {
if (intent.action?.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) == true) {
context.unregisterReceiver(this)
cont.resume(wifiManager.scanResults)
}
}
}
//setup cancellation action on the continuation
cont.invokeOnCancellation {
context.unregisterReceiver(wifiScanReceiver)
}
//register broadcast reciever
context.registerReceiver(wifiScanReceiver, IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION))
//kick off scanning to eventually receive the broadcast
wifiManager.startScan()
}
This is fine for signle emit, but if I want to get results while scanning is going then I'll get crash because cont.resume() could be called only once. Then I decided to try Flow. And here is my code:
suspend fun getCurrentScanResult(): Flow<List<ScanResult>> =
flow{
val wifiScanReceiver = object : BroadcastReceiver() {
override fun onReceive(c: Context, intent: Intent) {
if (intent.action?.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) == true) {
//context.unregisterReceiver(this)
emit(wifiManager.scanResults)
}
}
}
//setup cancellation action on the continuation
//register broadcast reciever
context.registerReceiver(wifiScanReceiver, IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION))
//kick off scanning to eventually receive the broadcast
wifiManager.startScan()
}
But now Android Stuidio says Suspension functions can be called only within coroutine body for function emit(wifiManager.scanResults) Is there a way to use Flow here?
Please take a look at the callback flow which is specifically designed for this use case. Something like this will do the job:
callbackFlow {
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
if (intent.action == WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) {
sendBlocking(wifiManager.scanResults) // or non-blocking offer()
}
}
}
context.registerReceiver(receiver, intentFilter)
awaitClose {
context.unregisterReceiver(receiver)
}
}
You also might want to share this flow with e.g. shareIn operator to avoid registering a new receiver for each flow subscriber.
I am working with geofences based on this Google sample. However, I am finding that geofence transitions are not occurring accurately in the background unless I am in Google Maps or a different location based app. Because of this I am attempting to turn the JobIntentService into a foreground service that will receive its own regular location updates.
Could someone explain the lifecycle of the JobIntentService and the order in which the methods are called when it is created?
Keep in mind I am using the following classes:
class GeofenceBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
GeofenceTransitionsJobIntentService.enqueueWork(context, intent)
}
}
class GeofenceTransitionsJobIntentService : JobIntentService() {
companion object {
private const val JOB_ID = 573
// Convenience method
fun enqueueWork(context:Context, intent:Intent) {
enqueueWork(context, GeofenceTransitionsJobIntentService::class.java, JOB_ID, intent)
}
}
onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// Make notification and start as a foreground service
return START_STICKY
}
onHandleWork(intent: Intent) {
// Handle the geofences
}