Running broadcast receiver on a separate thread - android

I have a class which runs on process's main thread. I want to implement a broadcast receiver in this class which runs on a separate thread.
I tried this by making a inner class extending thread & intializing a handler in this inner class, which i can give to registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) for executing broadcast receiver on this handler(which is attached to newly created thread). but in vain. Logs show onReceive is being called on main thread.
please help.(i have seen some posts on how to use registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler), but not of any help).

I would suggest you should use HandlerThread instead of subclassing a Thread
for example
HandlerThread handlerThread = new HandlerThread("DifferentThread" , android.os.Process.THREAD_PRIORITY_BACKGROUND);
handlerThread.start();
Looper looper = handlerThread.getLooper();
private Handler handler = new Handler(looper, this);
// Register the broadcast receiver to run on the separate Thread
registerReceiver (myReceiver, intentFilter, broadcastPermission, handler);
Hope this will help in your Experiment ;)
Njoy!

When using a HandlerThread, be sure to exit the thread after unregistering the BroadcastReceiver. If not, File Descriptor (FD) leaks occur in Linux level and finally the application gets crashed if continue to Register / Unregister.
unregisterReceiver(...);
Then
looper.quit();
Or
looper.quitSafely();
private Handler broadcastReceiverHandler = null;
private HandlerThread broadcastReceiverThread = null;
private Looper broadcastReceiverThreadLooper = null;
private BroadcastReceiver broadcastReceiverReadScans = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
}
}
private void registerForIntents() {
broadcastReceiverThread = new HandlerThread("THREAD_NAME");//Create a thread for BroadcastReceiver
broadcastReceiverThread.start();
broadcastReceiverThreadLooper = broadcastReceiverThread.getLooper();
broadcastReceiverHandler = new Handler(broadcastReceiverThreadLooper);
IntentFilter filterScanReads = new IntentFilter();
filterScanReads.addAction("ACTION_SCAN_READ");
filterScanReads.addCategory("CATEGORY_SCAN");
context.registerReceiver(broadcastReceiverReadScans, filterScanReads, null, broadcastReceiverHandler);
}
private void unregisterIntents() {
context.unregisterReceiver(broadcastReceiverReadScans);
broadcastReceiverThreadLooper.quit();//Don't forget
}

I have forked LocalBroadcastReceiver to be able to receive broadcasts on an arbitrary java thread.
it works for me. I added a Looper param to
public void registerReceiver(Context act, IntentFilter filt, Looper looper)
if looper is null, the main thread looper is used.
the code is here:
https://github.com/sorenoid/LocalBroadcastManager/blob/master/LocalBroadcastManager.java

Related

Service dies even I created separate thread to the broadcast receiver which is inside the service

I'm creating an App to monitoring call logs and send them to the cloud(Firebase Realtime database). I created Service to alive the app. Inside the service class, I created a Broadcast receiver to monitor the phone call states. I know about the background service restriction in android oreo and above
First, I register the broadcast receiver like this
#Override
public void onCreate() {
super.onCreate();
IntentFilter filter = new IntentFilter();
filter.addAction(android.telephony.TelephonyManager.ACTION_PHONE_STATE_CHANGED);
registerReceiver(receiver, filter);
}
After that, I can able to see call state changes as toast messages.
but I try to save to the firebase realtime database inside the phone state listener class, My app background service stops. So I register the Broadcast receiver with a separate thread like below
#Override
public void onCreate() {
super.onCreate();
IntentFilter filter = new IntentFilter();
filter.addAction(android.telephony.TelephonyManager.ACTION_PHONE_STATE_CHANGED);
HandlerThread handlerThread = new HandlerThread("ht");
handlerThread.start();
Looper looper = handlerThread.getLooper();
Handler handler = new Handler(looper);
registerReceiver(receiver, filter, null, handler);
}
Even though My background service stops. What went wrong here?

can a receiver be registered with LocalBroadcastmanager to execute in a different thread

Can a receiver be registered with LocalBroadcastManager to execute in a different thread?
We register for an intent with LocalBroadcastmanager using
void registerReceiver (BroadcastReceiver receiver, IntentFilter filter)
Suppose I want the onReceive method to be called on a different thread other than the main thread then how do I achieve it?
I don't see any API like
Intent registerReceiver (BroadcastReceiver receiver,
IntentFilter filter,
String broadcastPermission,
Handler scheduler)
The direct answer is no.
You'd have to handle the threading yourself. You have sendBroadcast() and sendBroadcastSync(). The first calls schedules the events to run on the main thread. The second one blocks the current thread and sends all pending broadcasts there.
Therefore calling sendBroadcastSync() might have unintended side-effects on other events which might require the main thread. So you could better wrap your receiver in one which is aware of the other threads to reschedule the event there again.
public class HandlerBroadcastReceiver extends BroadcastReceiver {
private final Handler handler;
private final BroadcastReceiver receiver;
HandlerBroadcastReceiver(Handler handler, BroadcastReceiver receiver) {
this.handler = handler;
this.receiver = receiver;
}
#Override public void onReceive(Context context, Intent intent) {
handler.post(() -> receiver.onReceive(context, intent));
}
}
I have forked LocalBroadcastManager and enhanced it to be able broadcast on an arbitrary java thread. Essentially I keep a map of Handers derived from a new Looper param in the registerReceiver method. The handler is invoked when it is time to broadcast. Synchronous behavior is unchanged. I have used it extensively but would appreciate any constructive criticism. The code is here:
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter, Looper looper)
https://github.com/sorenoid/LocalBroadcastManager
I hope it is helpful.

Register a broadcast receiver from a service in a new thread

I have a broadcastreciever which start a long operation (uploading process).
In the code of a service started from the Activity class, I need to register this receiver in a new thread.
I have checked this post Are Android's BroadcastReceivers started in a new thread? but I need a more concrete example about using Context.registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler)
Actually I need to know how to create a new thread from a service and to register the receiver and attached to this thread.
Thank you very much.
RA
In your service's onCreate():
private Handler handler; // Handler for the separate Thread
HandlerThread handlerThread = new HandlerThread("MyNewThread");
handlerThread.start();
// Now get the Looper from the HandlerThread so that we can create a Handler that is attached to
// the HandlerThread
// NOTE: This call will block until the HandlerThread gets control and initializes its Looper
Looper looper = handlerThread.getLooper();
// Create a handler for the service
handler = new Handler(looper);
// Register the broadcast receiver to run on the separate Thread
registerReceiver (myReceiver, intentFilter, broadcastPermission, handler);

Accessing UI thread handler from a service

I am trying some thing new on Android for which I need to access the handler of the UI thread.
I know the following:
The UI thread has its own handler
and looper
Any message will be put
into the message queue of the UI
thread
The looper picks up the event
and passed it to the handler
The handler handles the message and
sends the specfic event to the UI
I want to have my service which has to get the UI thread handler and put a message into this handler.
So that this message will be processed and will be issued to the UI.
Here the service will be a normal service which will be started by some application.
I would like to know if this is possible.
If so please suggest some code snippets, so that I can try it.
Regards
Girish
This snippet of code constructs a Handler associated with the main (UI) thread:
Handler handler = new Handler(Looper.getMainLooper());
You can then post stuff for execution in the main (UI) thread like so:
handler.post(runnable_to_call_from_main_thread);
If the handler itself is created from the main (UI) thread the argument can be omitted for brevity:
Handler handler = new Handler();
The Android Dev Guide on processes and threads has more information.
Create a Messenger object attached to your Handler and pass that Messenger to the Service (e.g., in an Intent extra for startService()). The Service can then send a Message to the Handler via the Messenger. Here is a sample application demonstrating this.
I suggest trying following code:
new Handler(Looper.getMainLooper()).post(() -> {
//UI THREAD CODE HERE
});
At the moment I prefer using event bus library such as Otto for this kind of problem. Just subscribe the desired components (activity):
protected void onResume() {
super.onResume();
bus.register(this);
}
Then provide a callback method:
public void onTimeLeftEvent(TimeLeftEvent ev) {
// process event..
}
and then when your service execute a statement like this:
bus.post(new TimeLeftEvent(340));
That POJO will be passed to your above activity and all other subscribing components. Simple and elegant.
You can get values through broadcast receiver......as follows, First create your own IntentFilter as,
Intent intentFilter=new IntentFilter();
intentFilter.addAction("YOUR_INTENT_FILTER");
Then create inner class BroadcastReceiver as,
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
/** Receives the broadcast that has been fired */
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction()=="YOUR_INTENT_FILTER"){
//HERE YOU WILL GET VALUES FROM BROADCAST THROUGH INTENT EDIT YOUR TEXTVIEW///////////
String receivedValue=intent.getStringExtra("KEY");
}
}
};
Now Register your Broadcast receiver in onResume() as,
registerReceiver(broadcastReceiver, intentFilter);
And finally Unregister BroadcastReceiver in onDestroy() as,
unregisterReceiver(broadcastReceiver);
Now the most important part...You need to fire the broadcast from wherever you need to send values..... so do as,
Intent i=new Intent();
i.setAction("YOUR_INTENT_FILTER");
i.putExtra("KEY", "YOUR_VALUE");
sendBroadcast(i);
....cheers :)
In kotlin thats how you can do it
Let say if you want to show Toast message from service
val handler = Handler(Looper.getMainLooper())
handler.post {
Toast.makeText(context, "This is my message",Toast.LENGTH_LONG).show()
}
Solution:
Create a Handler with Looper from Main Thread : requestHandler
Create a Handler with Looper from Main Thread: responseHandler and override handleMessage method
post a Runnable task on requestHandler
Inside Runnable task, call sendMessage on responseHandler
This sendMessage result invocation of handleMessage in responseHandler.
Get attributes from the Message and process it, update UI
Sample code:
/* Handler from UI Thread to send request */
Handler requestHandler = new Handler(Looper.getMainLooper());
/* Handler from UI Thread to process messages */
final Handler responseHandler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
/* Processing handleMessage */
Toast.makeText(MainActivity.this,
"Runnable completed with result:"+(String)msg.obj,
Toast.LENGTH_LONG)
.show();
}
};
for ( int i=0; i<10; i++) {
Runnable myRunnable = new Runnable() {
#Override
public void run() {
try {
/* Send an Event to UI Thread through message.
Add business logic and prepare message by
replacing example code */
String text = "" + (++rId);
Message msg = new Message();
msg.obj = text.toString();
responseHandler.sendMessage(msg);
System.out.println(text.toString());
} catch (Exception err) {
err.printStackTrace();
}
}
};
requestHandler.post(myRunnable);
}

Are Android's BroadcastReceivers started in a new thread?

If I have an inner class that extends BroadcastReceiver within my Service class, should I care about synchronization, when the BroadcastReceiver class reads/writes to objects from the Service class?
Or to put it in another way: Are BroadacstReceiver's onReceive() Methods started in an extra thread?
The onReceive() method is always called on the main thread (which is also referred to as "UI thread"), unless you requested it to be scheduled on a different thread using the registerReceiver() variant:
Context.registerReceiver(BroadcastReceiver receiver,
IntentFilter filter,
String broadcastPermission,
Handler scheduler)
Are Android's BroadcastReceivers started in a new thread?
Usually but not always, it all depends on how you register it.
If you register your BroadcastReceiver using:
registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
It will run in the main activity thread(aka UI thread).
If you register your BroadcastReceiver using a valid Handler running on a different thread:
registerReceiver (BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler)
It will run in the context of your Handler
For example:
HandlerThread handlerThread = new HandlerThread("ht");
handlerThread.start();
Looper looper = handlerThread.getLooper();
Handler handler = new Handler(looper);
context.registerReceiver(receiver, filter, null, handler); // Will not run on main thread
Details here & here.
The onReceive() method is called on the main thread. So, in case all your access to the service class is done from within the main thread, you don't need any synchronization.
Android Broadcast receivers are by default start in GUI thread (main thread) if you use
RegisterReceiver(broadcastReceiver, intentFilter).
But it can be run in a worker thread as follows;
When using a HandlerThread, be sure to exit the thread after unregistering the BroadcastReceiver. If not, File Descriptor (FD) leaks occur in Linux level and finally the application gets crashed if continue to Register / Unregister.
unregisterReceiver(...);
Then
looper.quit();
Or
looper.quitSafely();
private Handler broadcastReceiverHandler = null;
private HandlerThread broadcastReceiverThread = null;
private Looper broadcastReceiverThreadLooper = null;
private BroadcastReceiver broadcastReceiverReadScans = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
}
}
private void registerForIntents() {
broadcastReceiverThread = new HandlerThread("THREAD_NAME");//Create a thread for BroadcastReceiver
broadcastReceiverThread.start();
broadcastReceiverThreadLooper = broadcastReceiverThread.getLooper();
broadcastReceiverHandler = new Handler(broadcastReceiverThreadLooper);
IntentFilter filterScanReads = new IntentFilter();
filterScanReads.addAction("ACTION_SCAN_READ");
filterScanReads.addCategory("CATEGORY_SCAN");
context.registerReceiver(broadcastReceiverReadScans, filterScanReads, null, broadcastReceiverHandler);
}
private void unregisterIntents() {
context.unregisterReceiver(broadcastReceiverReadScans);
broadcastReceiverThreadLooper.quit();//Don't forget
}
Also, you can specify the "android:process" receiver element attribute in the AndroidManifest.xml. See here. That way you can specify that the receiver runs as a separate process and isn't tied to the main UI thread.

Categories

Resources