Class extending BroadcastReceiver not working - java

I made a helper class that extends BroadcastReceiver to listen for BluetoothDevice found and discovery finish intents. I have two activities which use this class by passing a handler. The handler receives messages as per the intent. I instantiate the class and registerReceiver like this:
From mainActivity:
deviceHelper=new DevicesHelper(myHandler,DevicesHelper.REQUEST_DETECT_DEVICES_IN_RANGE);
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
this.registerReceiver(deviceHelper, filter);
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
this.registerReceiver(deviceHelper, filter);
if(myBluetoothAdapter.isDiscovering())myBluetoothAdapter.cancelDiscovery();
myBluetoothAdapter.startDiscovery();
From ListActivity:
deviceHelper=new DevicesHelper(deviceListHandler,DevicesHelper.REQUEST_DEVICE_LIST_ACTIVITY);
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
this.registerReceiver(deviceHelper, filter);
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
this.registerReceiver(deviceHelper, filter);
DeviceHelper class:
public class DevicesHelper extends BroadcastReceiver {
public final static int REQUEST_DEVICE_LIST_ACTIVITY=1;
public final static int REQUEST_DETECT_DEVICES_IN_RANGE=2;
Handler myHandler;
int requestCode;
public DevicesHelper(){
}
public DevicesHelper(Handler handler,int requestCode){
this.requestCode=requestCode;
myHandler=handler;
}
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
String action = intent.getAction();
// When discovery finds a device
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Get the BluetoothDevice object from the Intent
switch(requestCode){
case REQUEST_DEVICE_LIST_ACTIVITY:
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
//newDevicesCount++;
String[] deviceInfo={device.getName(),device.getAddress()};
myHandler.obtainMessage(DeviceListActivity.MESSAGE_NEW_DEVICE,deviceInfo);
};
break;
case REQUEST_DETECT_DEVICES_IN_RANGE:
String[] deviceInfo={device.getName(),device.getAddress()};
myHandler.obtainMessage(StripChartRecorder.MESSAGE_NEW_DEVICE,deviceInfo);
break;
}
// When discovery is finished, change the Activity title
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
myHandler.obtainMessage(StripChartRecorder.MESSAGE_DISCOVERY_FINISHED);
}
}
Handler:
private final Handler myHandler=new Handler() {
#Override
public void handleMessage(Message msg) {
switch(msg.what) {
case MESSAGE_NEW_DEVICE:
doOtherStuff();
case MESSAGE_DISCOVERY_FINISHED:
dostuff();
}
break;
}}
Am I missing something here? I appreciate any help.

You need to sendMessage(Message msg) where msg is the Message you get with obtainMessage.

The problem was how I was handling the message received from the handler. Turned out Set<> was initially set to null as per eclipse's instruction, so it was never adding the devices received from the BroadcastReceiver helper class. I appreciate anyone who tried to help.

Related

BroadcastReceiver onReceive() never called

I am trying to discover bluetooth devices nearby. I followed tutorials but I can't figure out why onReceive() method is never called. Even though startDiscovery() is successful,the method onReceive() never gets called. I have added the proper permissions to the manifest
if(adapter.isDiscovering()) {
adapter.cancelDiscovery();
if (adapter.startDiscovery())
test.setText("Searching >>>");
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
Receiver receiver = new Receiver();
registerReceiver(receiver, filter);
}
}
if(!adapter.isDiscovering()) {
if (adapter.startDiscovery())
test.setText("Searching >>>");
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
Receiver receiver = new Receiver();
registerReceiver(receiver, filter);
}
private class Receiver extends BroadcastReceiver {
public Receiver() {
}
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
devices = new ArrayList<BluetoothDevice> ();
devices.add(device);
listAdapter = new DeviceListAdapter(context,R.layout.activity_bluetooth_connection,devices);
pairedDivices = (ListView) findViewById(R.id.pairedDevices);
pairedDivices.setAdapter(listAdapter);
if(devices.isEmpty())
test.setText("no devices");
}
}
}

Can I attach and then retrieve my own class descending from android.os.ResultReceiver to an Intent?

I'm trying to make a class that extends Android's ResultReceiver for use with a custom IntentService. The problem is that when I try to attach my receiver to an Intent using putExtra and then retrieve it in onHandleIntent that I am unable to cast it to the child class. A simple code example can be seen here.
public class MyReceiver extends ResultReceiver {
public JSONReceiver(Handler handler) {
super(handler);
}
}
public class MyService extends IntentService {
private static final String EXTRA_RECEIVER = "com.myname.extra.receiver";
protected static void sendIntent(Context context, MyReceiver receiver) {
Intent intent = new Intent(context, MyService.class);
intent.putExtra(EXTRA_RECEIVER, receiver);
//outputs com.myname.MyReceiver
Log.i("type", receiver.getClass().getName());
//outputs com.myname.MyReceiver
Log.i("type", intent.getParcelableExtra(EXTRA_RECEIVER).getClass().getName());
context.startService(intent);
}
#Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final Parcelable parcelable = intent.getParcelableExtra(EXTRA_RECEIVER);
//outputs android.os.ResultReceiver
Log.i("type", parcelable.getClass().getName());
//results in a casting error
final MyReceiver receiver = (MyReceiver) parcelable;
}
}
}
It seems that the retrieval works as expected before calling startService so something must be happening between that and onHandleIntent being called that changes what the receiver is an instance of. I'm curious why/how this is happening and also if it is possible to achieve what I'm trying to accomplish here. Thanks!

Send data to activity from already running sticky service

I created sticky background service, that should be started on boot:
public class AutostartReceiver extends BroadcastReceiver
{
public AutostartReceiver()
{
}
#Override
public void onReceive(Context context, Intent intent)
{
context.startService(new Intent(context, MyService.class));
}
}
My service is intended to do some work in background, it's implemented by creating thread for this. Also there is Messenger class used for sending work status to my main activity:
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
Log.i(TAG, "Service onStartCommand " + startId);
final int currentId = startId;
Messenger callback = null;
if(intent!=null)
{
callback = intent.getParcelableExtra("messenger");
}
final Messenger finalCallback = callback;
Runnable r = new Runnable()
{
public void run()
{
... do something, then stop
stopSelf();
}
};
if(t == null)
{
t = new Thread(r);
t.start();
}
return Service.START_STICKY;
}
Main activity receives messages sent from background thread running inside my service (some commands, that service is sending periodically:
Handler mHandler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
switch (msg.what)
{
case 0:
Toast.makeText(MainActivity.this,"Service runing", Toast.LENGTH_LONG).show();
break;
}
}
};
This works only if I start my service from activity, with activity's context. Obviously, if service is started on boot, or my app was closed (removed from last app list) and opened again, my activity is unable to get any messages from service any more.
If I invoke start service while service is already running, it will simply run OnStartCommand again, so either new thread will be created (I don't want it) or I need to destroy running thread and run thread again.
Is there any way to get my activity receiving messages from service, without actually touching already running thread inside it? I know about bound services, but it's not clear for me how to use them in my specific case.
As Alternate way You can use LocalBroadcastManager to send Data from Service to Activity.
Broadcast Your message from Service:
private void broadcastMessage(Context context){
Intent intent = new Intent("UI_UPDATE_BROADCAST");
intent.putExtra("MESSAGE", "MyMessage");
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
Register Broadcast Receiver in your activity to receive messages:
#Override
public void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(mContext).registerReceiver(mMessageReceiver, new IntentFilter("UI_UPDATE_BROADCAST"));
}
#Override
public void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mMessageReceiver);
}
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//Do Something With Received Data
String msg = intent.getStringExtra("MESSAGE");
}
};
I would use a broadcast receiver for Service-to-Activity communication.
Code:
public class MyActivity extends Activity {
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//Toast here
}
#Override
protected void onCreate(Bundle savedInstanceState) {
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, new IntentFilter("message-name"));
}
#Override
public void onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
super.onDestroy();
}
}
Then in your service you would broadcast the message like this:
Intent intent = new Intent("message-name");
intent.putExtra("data", 1);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);

Accessing context from another thread

I am starting to migrate some PC Java applications to Android environments being a complete newbie concerning Android platform.
I found a problem when I tried to use a Service reference as context for a Toast message.
This is the relevant part of my Service code:
public class ServicePFPE extends Service {
Timer messageSimulator;
TimerTask messagePoll;
private class MessageReceptionTask extends TimerTask
{
public MessageReceptionTask(Context c) {
context = c;
}
#Override
public void run() {
String shownText = "Message received!! on " + (new Date(System.currentTimeMillis())).toString();
//doToast(shownText, context); //THIS LINE MAKES THE APP CRASH!
System.out.println(shownText); //But I can do this
}
private Context context;
}
public ServicePFPE() {
super();
messageSimulator = new Timer();
messagePoll = new MessageReceptionTask(this);
}
#Override
public IBinder onBind(Intent intent)
{
doToast("Service: onBind");
return null;
}
...
...
...
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
doToast("Service: onStartCommand");
messageSimulator.schedule(messagePoll, 5000, 5000);
return super.onStartCommand(intent, flags, startId);
}
...
...
...
private void doToast(String msg) { doToast(msg, this); }
private void doToast(String msg, Context con) {
Toast.makeText(con,msg,Toast.LENGTH_SHORT).show();
}
}
When the scheduled task runs reaching doToast call Android notifies that "Unfortunatelly, myAPP has stopped".
I think it has something to do with the fact I am using the service context in a different thread but I don't know for sure.
Could you confirm if that is the case? What is the right way to run a timer from a service and be able to use its context? If that is not possible, can I get a context for that thread so I can generate Toasts user messages.
It depends on what you really need, if you are planning to show simple notifications, maybe instead of toasts you can use Android notification bar (which is the standard way to show them). For example you can use:
/**
* Show a notification while this service is running.
*/
private void showNotification() {
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText(R.string.local_service_started);
NotificationManager mNM;
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.stat_sample, text,
System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, LocalServiceActivities.Controller.class), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(this, getText(R.string.local_service_label),
text, contentIntent);
// Send the notification.
mNM.notify(NOTIFICATION, notification);
}
however, if you just want toasts, you can show them from the service, your problem is that the timertask is being executed in a different thread that the UI thread (where the service is running). to "post" this code to the UI thread you can do it directly with something like this:
Handler handler;
#Override
public void onCreate() {
// Handler will get associated with the current thread,
// which is the main thread.
handler = new Handler();
super.onCreate();
}
private void runOnUiThread(Runnable runnable) {
handler.post(runnable);
}
Source
And finally if you want fully interaction between service and activities, you have several ways:
Use binders, for simple communications, this is moreless what you need.
Use a messenger, to more complicated communications.
If you only need dialogs you are always able to launch new activities in dialog mode.
AIDL...
Documentation about 1 & 2 here and here
Binders:
They let you bind different objects in your application letting them access directly to the object itself and its functions, example from android doc:
public class LocalService extends Service {
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
// Random number generator
private final Random mGenerator = new Random();
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/** method for clients */
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
#Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
/** Called when a button is clicked (the button in the layout file attaches to
* this method with the android:onClick attribute) */
public void onButtonClick(View v) {
if (mBound) {
// Call a method from the LocalService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
int num = mService.getRandomNumber();
Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
Messenger:
More advanced & complicated, in this way you can send messages from one object to another:
public class MessengerService extends Service {
/** Command to the service to display a message */
static final int MSG_SAY_HELLO = 1;
/**
* Handler of incoming messages from clients.
*/
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
/**
* When binding to the service, we return an interface to our messenger
* for sending messages to the service.
*/
#Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}
public class ActivityMessenger extends Activity {
/** Messenger for communicating with the service. */
Messenger mService = null;
/** Flag indicating whether we have called bind on the service. */
boolean mBound;
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the object we can use to
// interact with the service. We are communicating with the
// service using a Messenger, so here we get a client-side
// representation of that from the raw IBinder object.
mService = new Messenger(service);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mBound = false;
}
};
public void sayHello(View v) {
if (!mBound) return;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
#Override
protected void onStart() {
super.onStart();
// Bind to the service
bindService(new Intent(this, MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
}
In case you want to show activities as fancy dialogs to show the updates you can use a regular activity with this theme:
<activity android:theme="#android:style/Theme.Dialog" />
Any UI related code should be ran on the UI thread using RunOnUiThread method.
you should set a global context like this:
public static Activity currentActivity=null;
and after run your main activity or any activity that runs service set context like this:
MainActivity.currentActivity = this;
after that in toast use this context:
Toast.makeText(MainActivity.currentActivity," text", Toast.LENGTH_LONG);
hope use full

How do I send scanresults from broadcastreceiver to service

I have a broadcastreceiver created in my Service class.
It is set to react to this action: WifiManager.SCAN_RESULTS_AVAILABLE_ACTION
So basically everytime I do a method call of wifi.startScan(); , and the results become available, the broadcastreceiver's onReceive method does its thing.
My issue is that I need to process those scan results and its likely not good practice to do so much in the broadcastreceiver. I want to do all the calculations back in my service class but I need to somehow access the ScanResults.
Any sort of help with this? As it is much needed.
Here is a simplified version of my code conveying the purpose of my broadcastreceiver:
Snippet from my Service class:
IntentFilter i = new IntentFilter();
i.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); //reacts to the scan results being available
registerReceiver(mybroadcast,i);
wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
if(!wifi.isWifiEnabled()){ // if wifi is not enabled
toast = Toast.makeText(getApplicationContext(), "Wifi is off. Please turn it on.", Toast.LENGTH_LONG);
toast.show();
//wifi.setWifiEnabled(true);
//startActivity(backIntent);
}
else
{
wifi.startScan(); //what the receiver is going to react to
}
Code for my receiver:
private final BroadcastReceiver mybroadcast = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//gets the scan results
wifi = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
List<ScanResult> scans = wifi.getScanResults();
// do some work here...
}
}
You can try adding an handler to service for ex:
private final Handler handler = new Handler() {
public void handleMessage(Message msg) {
String aResponse = msg.getData().getString("message");
if ((null != aResponse)) {
// ALERT MESSAGE
Toast.makeText(getBaseContext(),
"Server Response: " + aResponse, Toast.LENGTH_SHORT)
.show();
} else {
// ALERT MESSAGE
Toast.makeText(getBaseContext(),
"Not Got Response From Server.", Toast.LENGTH_SHORT)
.show();
}
}
};
And send a message to it in OnReceive of your BroadcastReceiver:
Message msgObj = handler.obtainMessage();
Bundle b = new Bundle();
b.putString("message", msg);
msgObj.setData(b);
handler.sendMessage(msgObj);

Categories