Using BroadcastReceiver peekService without an Activity binded to my Service - android

I need to implement a service whos periodically scans for the avaliables wifis networks, and save that information for use whenever I want to. To do that, I've implemented a service who gets started when the android boots, and I want that service always running, indepent from my Activities. I want to access the scan data from a Broadcast Receiver, and to access that, im using the peekService method from the Broadcast Receiver class. My problems is that, the peekservice returns null if my Service (who Im sure is running) isn't binded to an Activity. So for test, I've binded my service with my main activity using the bindService() method, and with that, the peekService don't return null. But I really want to use that data without have to bind an Activity to the Service, anyone knows how to do that?
PS: I don't want to pass a intent from the service to the broadcast receiver either, I want to use the data with my "get" method, because I want to access the informations when I need to, and not when the scan is completed.
Thats my broadcast receiver to start the Service
public class StartMyContextCollector extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equalsIgnoreCase(Intent.ACTION_BOOT_COMPLETED)){
context.startService(new Intent(context, ContextCollectorService.class));
}
}
}
Thats my Service
public class ContextCollectorService extends Service {
WifiManager wifiManager;
private ArrayList<Bundle> wifiEvents = new ArrayList<Bundle>();
List<ScanResult> scanList;
WifiListReceiver wifiReceiver;
public class LocalBinder extends Binder {
public ContextCollectorService getService(){
return ContextCollectorService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return new LocalBinder();
}
#Override
public void onCreate() {
wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
wifiReceiver = new WifiListReceiver();
registerReceiver(wifiReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
}
public ArrayList<Bundle> getWifiEvents(){
return this.wifiEvents;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
wifiManager.startScan();
return Service.START_STICKY;
}
class WifiListReceiver extends BroadcastReceiver{
#SuppressLint("UseValueOf")
#Override
public void onReceive(Context context, Intent intent) {
scanList = wifiManager.getScanResults();
Bundle numberWifis = new Bundle();
numberWifis.putString("NUMBER_OF_WIFI", String.valueOf(scanList.size()));
wifiEvents.add(numberWifis);
for (int i = 0; i < scanList.size(); i++) {
Bundle wifiEvent = new Bundle();
wifiEvent.putString("SSID", scanList.get(i).SSID);
wifiEvent.putString("BSSID", scanList.get(i).BSSID);
wifiEvents.add(wifiEvent);
}
}
}
}
That what im doing to get the information that I want in my Broadcast Receiver
ArrayList<Bundle> wifiEvents = null;
IBinder binder = peekService(context, new Intent(context, ContextCollectorService.class));
if (binder != null) {
ContextCollectorService service = ((ContextCollectorService.LocalBinder) binder).
getService();
wifiEvents = service.getWifiEvents();
}
in my main Activity, I created a connection and used the bindService method on the onStart() command, binding the Activity with the Service, but that's what I don't want to do.

The documentation for peekService() does not fully describe the conditions required to get an IBinder. As Dianne Hackborn (Google Android engineer) explains in this post: "Calling startService() is not enough -- you need to have called bindService() for the same intent, so the system had retrieved the IBinder for it". Your results confirm this behavior: You get an IBinder when you have previously bound the service to an activity, but not when the service has only been started.

Related

How to call a service method in a broadcastreceiver?

So, I have an app that starts a service. This service starts to scan for bluetooth devices with BTAdapter.startDiscovery(). Further I have a broadcastreceiver which listens for the DISCOVERY_FINISHED action. If that occurs I want to call a method from onReceive() in my service that starts the scanning process again. How am I gonna do this?
Here my receiver:
public class PollingReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
ScanBTService sBTs = new ScanBTService();
sBTs.startScan();
}
}
and here the service:
public class ScanBTService extends IntentService {
private BluetoothAdapter mBTAdapter;
private PollingReceiver mPollingReceiver;
public ScanBTService() {
super("ScanBTService");
}
#Override
protected void onHandleIntent(Intent intent) {
final BluetoothManager btManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBTAdapter = btManager.getAdapter();
mBTAdapter.startDiscovery();
}
public void startScan() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mBTAdapter.startDiscovery();
}
}
In your onReceive()-method, restart your service using the following two lines. I did not tested it out but it should work like that.
#Override
public void onReceive(Context context, Intent intent) {
//ScanBTService sBTs = new ScanBTService();
//sBTs.startScan();
Intent i = new Intent(getApplicationContext(), ScanBTService.class);
startService(i);
}
You can then remove the startScan()-method, too.
Try this to resolve the method:
context.startService(new Intent(context, SimpleWakefulService.class));
Since you are using an IntentService, you will need to create an intent for the started service to handle.
This can be achieved by the following :
Intent intent = new Intent(context, ScanBTService.class);
startService(intent);
As described here : https://developer.android.com/training/run-background-service/send-request.html
Now, if you are looking to have a service that maintains bluetooth connections, discover devices, send & receive data... If this is the case, then in my experience, i would argue the following points :
Perhaps the best way to go about this (depending on what you're doing of course), would be to have a service running in it's own separate process which would be responsible for all of that. Check : http://developer.android.com/guide/topics/manifest/service-element.html and the tag
android:process
Take advantage of Android's IPC communication feature to pass & receive messages between you're main app thread and your service. Tutorial : http://www.survivingwithandroid.com/2014/01/android-bound-service-ipc-with-messenger.html.
Create & Maintain connection quick guide : http://developer.android.com/guide/topics/connectivity/bluetooth.html#ConnectingAsAClient
Hope it helps
Cheers

android services and activity lifecycle?

I am having problem with my android IntentService. When I first open the application, the service gets started by intent from the profile activity and data is fetched from this service. If I switch to other activity and then back service is still running and that is ok.
However if you press back, so that activity is finished and put in the background, the service is still working as the application is in background but If I get it back to foreground service stops. I do not know why. Bellow is my code, please help.
I have read activity life cycle couple of times and still do not get it why this is happening.
What is weird is that Service receive data one more time before it stops when MainActivity is brought back to running state. Service is not crashing.
Service
public class SomeService extends IntentService
{
public static final String extra = "someData";
public SomeService()
{
super(SomeService.class.getSimpleName());
}
#Override
protected void onHandleIntent(Intent intent)
{
Log.e("SomeService", "starting service");
while (true)
{
SomeData data = Api.getNewSocketData();
//Broadcast data when received to update the view
Intent broadcastData = new Intent();
broadcastData.setAction(dataBroadcastReceiver.ACTION_DATA_RECEIVED);
broadcastData.addCategory(Intent.CATEGORY_DEFAULT);
broadcastData.putExtra(extra, " ");
sendBroadcast(broadcastData);
Log.e("SomeService", "received from socket");
}
}
}
Receiver
public class dataBroadcastReceiver extends BroadcastReceiver
{
public final static String ACTION_DATA_RECEIVED = "net.bitstamp.intent.action.ACTION_SOMEDATA_RECEIVED";
#Override
public void onReceive(Context context, Intent intent)
{
Log.e("receiver", "data received");
}
}
Main Activity
#Override
public void onPause()
{
super.onPause();
unregisterReceiver(dataBroadcastReceiver);
}
#Override
public void onResume()
{
super.onResume();
IntentFilter intentFilter = new IntentFilter(dataBroadcastReceiver.ACTION_DATA_RECEIVED);
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
dataBroadcastReceiver = new dataBroadcastReceiver();
registerReceiver(dataBroadcastReceiver, intentFilter);
Intent someService = new Intent(this, SomeService.class);
startService(someService);
}
I really need help on this. Thanks
You don't want to the up the IntentService in an infinite loop. It will block all other incoming requests. From the documentation:
All requests are handled on a single worker thread -- they may take as long as necessary (and will not block the application's main loop), but only one request will be processed at a time.
Your Service is likely still happily running along, it just isn't processing your new request because your old one is still being handled in the infinite loop.

send Messages to service without binding it

i have created one service by extending Service in android and i am sending Message to service using Messenger and Handler.
But the issue (which is a common behavior though) is whenever i have to send message to Service i have to bind it and when i go out of activity i have to unbind it which eventually destroys the service itself.
i can keep running service in background by fringing startService method but is there any way to send Messages to service without using bind as i don't want to destroy the service when i go out of activity.
LocalBroadcastManager is a great way to send messages/data,
In your service class create a private broadcastreciever and string for the intent action name:
public static String MSERVICEBROADCASTRECEIVERACTION ="whatevs";
private BroadcastReceiver mServiceBroadcastReceiver= new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("foo","onReceive called");
Log.d("foo","extra = " + intent.getStringExtra("foo")); // should print out " extra = bar"
}
};
And register it in your onCreate
#Override
public void onCreate() {
// your other code...
LocalBroadcastManager.getInstance(this).registerReceiver(mServiceBroadcastReceiver, new IntentFilter(ServiceClassName.MSERVICEBROADCASTRECEIVERACTION));
}
And De-register it in onDestroy()
#Override
public void onDestroy() {
// your other code...
LocalBroadcastManager.getInstance(this).unregisterReceiver(mServiceBroadcastReceiver);
}
As for sending messages to it, from an activity or fragment:
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
Intent intent = new Intent(ServiceClassName.MSERVICEBROADCASTRECEIVERACTION);
// add some data
intent.putExtra("foo","bar");
lbm.sendBroadcast(intent);
HTHs you send data without needing to bind!
Unbind Service will not destroy the service. it will disconnect the service connection between the activity and service
Make sure you return START_STICKY to keep your service running
#Override
public int onStartCommand(Intent intent, int flag, int startId)
{
return START_STICKY;
}
Also make sure your running the service as foreground
using notification to keep running the service after the application is removed from stack.
startForeground(1000,mBuilder.build()); // mBuilder - notification builder

How to make a started service continue after the last bound services un-binds

I would like to have a service (doing occasional monitoring) be active continuously. I plan to start it by listening to a BOOT_COMPLETE, which I believe makes it a "Started Service". I want a UI application to bound to it, which is working and documented. However, after the binding activity is destroyed, the Service dies because it's "unbound from all clients".
Is there a way to have a started service allow binding and still continue after the last bound services un-binds?
Returning true from onUnbind() wouldn't help, as the service should continue to be active even if no additional binder exist.
In Android, services are started in one of two ways - through the startService(Intent i) method, or the bindService(Intent i). The method used to start the service determines whether it is started or bound. A service can be started, then bound to a client - or bound and then have calls to start sent to it (it doesn't restart if already started).
As you mention listening for BOOT_COMPLETE, I assume this is an action for an Intent, which is sent via a Broadcast object. This means that you can create an IntentFilter object with the BOOT_COMPLETE action added to it via the addAction(String action) method. Then a BroadcastReceiver object can be created, which upon receiving an intent with an action of BOOT_COMPLETE can then call the startService(Intent i) method (this is done by overriding the onReceive(Context context, Intent intent) method).
If you call startService(Intent i) when the Intent is received, then the service will be a started service. This means that it will only stop when a call to stopService(Intent i) is made by the app, or if the service calls the stopSelf() method. It can be bound and unbound from by multiple activities during the time it is running, and it will not stop (as it is started, not bound).
Here is an example of this, using two Activity objects and a Service:
Activity 1 (first activity of your app):
public class ServiceActivity extends Activity {
private IntentFilter filter = new IntentFilter();
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if(action.equals(BOOT_COMPLETE) {
startService(new Intent(ServiceActivity.this, MyService.class));
}
}
};
#Override
protected void onStart() {
super.onStart();
filter.addAction(BOOT_COMPLETE);
registerReceiver(receiver, filter);
}
#Override
protected void onStop() {
super.onStop();
unregisterReceiver(receiver);
}
//Some other code
}
Activity 2 (used at some point after activity 1):
public class AnotherActivity extends Activity {
private MyService service;
private ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
service = ((MyService.MyBinder)service).getService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
service = null;
}
};
#Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, MyService.class), connection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
unbindService(connection);
}
//Some other code
}
Service:
public class MyService extends Service {
private MyBinder binder = new MyBinder();
#Override
public IBinder onBind(Intent intent) {
return binder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
//Some other code
final class MyBinder extends Binder {
MyService getService() {
return MyService.this;
}
}
Final notes
To be able to use the service as bound, you need to override the onBind(Intent intent) method, and return an instance of binder MyBinder. Not doing so will result in not being able to bind (the binding sets the service variable by using the getService() method defined in MyBinder).
The BroadcastReceiver must alwasy be unregistered when the Activity it's in closes, as it would be leaked otherwise. That is why in the example, I have registered and unregistered in the onStart() and onStop() methods respectively. Using onDestroy() to unregister is not recommended as it is not always called.
The MyService object that is used when binding must also be unbound when it's Activity closes, as it too can be leaked. It is set to null when onServiceDisconnected(ComponentName name) is called for garbage collecting.
Sources for further reading
https://developer.android.com/reference/android/content/BroadcastReceiver.html
https://developer.android.com/reference/android/app/Activity.html
https://developer.android.com/reference/android/app/Service.html
https://developer.android.com/reference/android/content/ServiceConnection.html

OnBind() on service always returns False - Android

I'm trying to bind a service, but onBind() always returns false.
This is the code for the ServiceConnection-
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with our service has been established,
// giving us the service object we can use to interact with our service.
mBoundService = ((ScheduleService.ServiceBinder) service).getService();
}
public void onServiceDisconnected(ComponentName className) {
mBoundService = null;
}
};
This is call to bindService() -
boolean test = getApplicationContext().bindService(new Intent(this, ScheduleService.class), mConnection, Context.BIND_AUTO_CREATE);
This is the declaration of the service in the Manifest -
<service android:name=".Notifications.ScheduleService" android:enabled="true"/>
I've read previous questions regarding the subject and couldn't find an answer(tried switching the Activity context with the Application context, but it didn't help).
I'm using using Frgaments and ActionBarSherlock, and my Activity extends SlidingFragmentActivity (That's why i'm using the application context, which doesn't help).
Edit - This is the code of the service i'm trying to start -
public class ScheduleService extends Service {
/**
* Class for clients to access
*/
public class ServiceBinder extends Binder {
public ScheduleService getService() {
return ScheduleService.this;
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("ScheduleService", "Received start id " + startId + ": " + intent);
// We want this service to continue running until it is explicitly stopped, so return sticky.
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
// This is the object that receives interactions from clients. See
private final IBinder mBinder = new ServiceBinder();
/**
* Show an alarm for a certain date when the alarm is called it will pop up a notification
*/
public void setAlarm(Calendar c) {
// This starts a new thread to set the alarm
// You want to push off your tasks onto a new thread to free up the UI to carry on responding
new AlarmTask(this, c).run();
}
}
Any help will be appreciated . Thanks.
What is the fully qualified class-name of ScheduleService (i.e. including the full package-name)?
I'm asking this, because in your AndroidManifest.xml file, your service's name is .Notifications.ScheduleService, which seems a bit odd:
This tells me that either
The (last part of the) package-name contains a upper-case
character... not so good.
I would expect .notifications.ScheduleService instead, if this is the case.
The ScheduleService is defined within a file called Notifications.java.
I would expect .Notifications$ScheduleService instead, if this is the case (dollar sign instead of period).
Do you mean bindService() returns false? onBind() returns IBinder type.
Keep in mind that service binding takes some time. If you want to perform some action after binding is done you can perform it in the onServiceConnected() method.
public void onServiceConnected(ComponentName className, IBinder service) {
mBoundService = ((ScheduleService.ServiceBinder) service).getService();
Calendar c = new Calendar();
mBoundService.setAlarm(c);
}
If you need more guidance on this you need to show us your Activity code.
Why do you use the application context to bind the service?
The method bindService is called through the ContextWrapper. It might not be the issue but I'd share contexts across the place where you bind the service and where you have the connection.
In your case instead of
boolean test = getApplicationContext().bindService(new Intent(this, ScheduleService.class), mConnection, Context.BIND_AUTO_CREATE);
I'd do the following
boolean test = bindService(new Intent(this, ScheduleService.class), mConnection, Context.BIND_AUTO_CREATE);
Or if you want to keep a global context within the application, move everything to your Application file and call it similarly the same way suggested above.
The issue can also be on the package name of your app and the declaration of your service in your manifest. If you are unsure make sure to give the global route to your service in the manifest.

Categories

Resources