I am working on a service that can start at device boot completion, i am setting a preference file to store service running state so i can retrieve it when i need it, in my broadcast receiver :
public class MyServiceBootReceiver extends BroadcastReceiver {
public MyServiceBootReceiver() {super();}
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
if(MyService.isRunning(context)) //static method to get shared preferences value
context.startService(new Intent(context, MyService.class));
}
}
}
Now i am confused about the "context" parameter in onReceive() method, since the application and all its components will be destroyed when device is shutdown, which context is passed in the receiver, and wich component of my application is actually receiving it ?
Before calling your BroadcastReceiver, Android will create your app's Application context. You can actually see this happenning if you have your own class that inherits Application and put up a Log on it's onCreate.
However, the context you receive in your Receiver is restricted: it cannot call registerReceiver() or bindService().
More information about different types of contexts can be found here.
Related
I am bit confused what is the right place to use Service (for background task).
This is my scenario:
I have a class that extends Broadcast receiver. It receives WiFi state changes. Depending on the state change, I call another class. This is a pure Java class, not extending any class.
This class is instantiated by passing the Context (received with the broadcast receiver).
I need to pass the Context because, among other things, I access SharedPreferences, display a notification, etc. But this not a foreground activity.
Is this the correct way? Or should my class extend Service and work as a background task?
Is it wrong to pass the Context to initiate a class?
For example,
public class WifiStateBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
...
WifiChangeReceptionClass wifiChanged = new WifiChangeReceptionClass(context);
wifiChanged.showNotification();
...
}
What is wrong with this approach?
Try any of this:
public class WifiChangeReceptionClass{
public static void showNotification(Context context){
//showYourNotification
}
}
Or
Create an Application class that has a static method to get it's context, like so.
public class MyApplication extends Application {
...
public static MyApplication get(){
return this;
}
...
}
Then in your class, just call:
public class WifiChangeReceptionClass{
public static void showNotification(){
Context context = MyApplication.get();
//showYourNotification
}
}
Or
Just use dagger for your dependency injections.
Check docs here
It is wrong because context reference will be available as long as you are in onReceive() method. Once call back onReceive() method gets completed context will no longer be available.
It is absolutely fine as long as you are using the context within the lifecycle of broadcast receiver (you may pass it to any class). Since broadcast receiver and service, both run on UI thread, using service would not make much difference.
If you want to perform some network operation or long running operation, then you can instantiate a intent service from receiver.
Registering for broadcast receiver to get Wifi state changes might not from Android 7 its has disabled the CONNECTIVITY_CHANGED.
Apps targeting Android 7.0 (API level 24) and higher do not receive CONNECTIVITY_ACTION broadcasts if they declare the broadcast receiver in their manifest. Apps will still receive CONNECTIVITY_ACTION broadcasts if they register their BroadcastReceiver with Context.registerReceiver() and that context is still valid.
This includes connectivity change. The better option is to use JobScheduler. Refer this link
As mentioned in the response, onReceive() of BroadcastReceiver is executed on main thread. Based on android documentation for broadcast receiver
As a general rule, broadcast receivers are allowed to run for up to 10 seconds before they system will consider them non-responsive and ANR the app. Since these usually execute on the app's main thread, they are already bound by the ~5 second time limit of various operations that can happen there (not to mention just avoiding UI jank), so the receive limit is generally not of concern. However, once you use goAsync, though able to be off the main thread, the broadcast execution limit still applies, and that includes the time spent between calling this method and ultimately PendingResult.finish().
If your WifiChangeReceptionClass is doing some extensive work, then refrain from running directly in onRecevie(). Instead start Service (you have to spawn a new thread thread) or IntentService
I have an activity that starts a background service. Once it is started, it runs forever.
Lets say the background service needs the activity that started it to update something. Then how can I start the activity again "If it is not started" however if it is already started then send a broadcast?
Thanks
You would have to bind to the service in your activity. Then, in the service, implement the onBind and onUnbind methods to set a boolean "bound". Check the boolean to see whether the activity is active.
With activity you mean the service's process. If the service is started forever, then its process its started foerever (except when the system kill its for recovering memory purpose and recreates it later). That doesnt mean the rest of activities/fragments/services are not kill. A service is just an entry point for your application and it gives your process a position into the process priority ladder.
Its hard to say, since I don't know the details of your app, but I think that you may want to consider a bit of a redesign.
As you have noticed, Activities are ephemeral. Generally speaking, a service should not (cannot) depend on a particular Activity being active.
In fact, a well-designed service should not depend on any particular activity at all.
I'm not sure I completely follow your question. However, when communicating from a Service to an Activity I use a broadcast receiver in the Activity class.
This can be created as follows:
// register the BroadcastReceiver in your activity class
this.receiver = new NotificationReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("com.example.LISTENER");
registerReceiver(receiver, filter);
// insert in your activity class
class MyReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
if(intent.hasExtra("command")) {
if(intent.getStringExtra("command").equals("userRegistered")) {
// insert code to do something when this intent is received
}
}
}
}
// insert in your service class to send message
Intent i = new Intent("com.example.LISTENER");
i.putExtra("command", "userRegistered");
sendBroadcast(i);
I have a BroadcastReceiver, and the onReceive is called from two different postExecute methods in two different asyncTasks, in two different Activities.
I have a third activity that is running all the time called HomeActivity, and I want to publish some text to the HomeActivity's UI from the onReceive method.
Is it possible? I know that the context parameter is the context of the activity who raised the onReceive, but I want to access the HomeActivity's UI.
Here is the code
public class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// here I want to publish some text to the HomeActivity
}
}
any ideas? thanks in advance
You want to change text in your running activity based on what you receive in the onReceive of the BroadcastReceiver? Right? One way is that you can use LocalBroadcast. See LocalBroadcast Manager.
For how to implement is, there is a great example on how to use LocalBroadcastManager?.
LocalBroadcast Manager is a helper to register for and send broadcasts of Intents to local objects within your process. The data you are broadcasting won't leave your app, so don't need to worry about leaking private data.`
Your HomeActivity can registers for this local broadcast. From the MyBroadcastReceiver you send a LocalBroadcast from within the onReceive (saying that hey, I received a message. Do you want to do something now activity). Then inside your Activity you can listen to the broadcast. This way if the activity is in the forefront/is active, it will receive the broadcast otherwise it won't. So, whenever you receive that local broadcast, you may change the text etc, if activity is open.
I'm using a BroadcastReceiver in my Android app which simply contains the following piece of code:
public BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
GcmIntentService.isHandled = true;
Toast.makeText(context, "broadcast receiver test", Toast.LENGTH_SHORT).show();
}
}
I'm using this receiver to determine if my activity is running and carry out some updates in a ListView without having any notifications produced by GcmIntentService.
With the code being simple so far, only creating a toast message, I'm unable to catch the boolean value from GcmIntentService.isHandled as soon as the sendBroadcast is invoked.
Is it possible in any way to determine if the code for my receiver has finished running. I understand that sendBroadcast is an asynchronous call, and I'm making use of Thread.sleep(1000) so far to wait for the isHandled value, but it would be nice if there is a more reliable method on achieving this.
Any thoughts?
Your question can be divided to two parts:
1.How to know that if there is a receiver actually received the broadcast.
2.How should the receiver notify the service that message is been handled.
It seems difficult to achieve the first goal through standard Intent api, instead I suggest you may try the "observer pattern".
You may create a global Observable object in your Application and make your Activity implements Observer, register itself in onCreate() and unRegister in onDestory().Inside the Service you can check if there is an Activity running through countObservers() and then simply notify it.
I have some Service class, which registers multiple alarms.
In my BroadcastReceiver class, I want the onReceive() method to call some method of the Service class.
However, I don't see how I can bind them together. I tried to make the BroadcastReceiver an inner class, but then I got more errors and couldn't fire the alarm at all.
Thanks
Look at http://developer.android.com/reference/android/content/BroadcastReceiver.html life cycle. BroadcastReceiver is created only for handling a message. It means that it's life is very short, ant it is also stateless. So you cannot bind anything to it.
Anyway you can try to start a service form onReceive(Context context, Intent intent) method of BroadcastReceiver,
like this:
public void onReceive(Context context, Intent intent) {
Intent intent2 = new Intent(context, GCMService.class);
intent2.putExtras(intent);
context.startService(intent2);
}
Then a=the service should handle a broadcast message.
From the http://developer.android.com/guide/components/bound-services.html
Note: Only activities, services, and content providers can bind to a service - you cannot bind to a service from a broadcast receiver.