Maybe I'm missing something. I want to write test cases for a BroadcastReceiver; specifically, it is for receiving the BOOT_COMPLETED event and setting an alarm for another receiver to handle later; it doesn't seem to be setting it properly, but the point is that I have no obvious way to test it. I can't exactly attach a debugger and wait for BOOT_COMPLETED, and I can't send a fake BOOT_COMPLETED broadcast.
Why are there instrumentation classes for Activity, Service, and Provider, but not BroadcastReceiver? Any advice for testing this?
There is nothing magical about the life cycle for the BroadcastReceiver. It's enough to test it with an AndroidTestCase. In a test case, instantiate your BroadcastReceiver, create whatever Intent you want to send and call onReceive using the Context available from AndroidTestCase or some mock Context.
E.g.
public class TestMyBroadcastReceiver extends AndroidTestCase {
public void testReceive() {
MyBroadcastReceiver r = new MyBroadcastReceiver();
Intent i = new Intent("MY_ACTION");
// TODO put extras
r.onReceive(getContext(), i);
// TODO query application state to verify results
}
}
For most cases I agree completely with https://stackoverflow.com/a/5181010/527016
There are however cases when extending AndroidTestCase is not suitable (and can cause surprises). In particular, if you are doing more complex integration testing and want to test your BroadcastReceiver with an actual Intent sent by the system. The main reason is that the onReceive method in the broadcast receiver runs on the main application thread while the tests in AndroidTestCase run in another thread. This can cause test-related threading issues in code that was not intended to run on multiple threads.
The solution to this is to subclass your test from InstrumentationTestCase instead and use the #UiThreadTest annotation to make the tests run on the same thread as the onReceive method.
For more info (and an example) see: http://olafurhelgason.blogspot.com/2012/12/threading-and-android-integration.html
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
Is it possible to send an intent from a service to an Application class? Not Activity?
I wouldn't know what activity would be running at a particular time, so I am adding a boolean flag in the activity class that detects the activity and sends the appropriate data based on the broadcast received.
If your Service is active, then your Application class is active as well.
Otherwise you wouldn't be able to use getApplicationContext().
Although I'm skeptic about a service that runs forever there is a very clean way to make the Service communicate with a certain Activity, should the last one be currently active.
Such clean way is called LocalBroadcastManager.
The Activity meant to receive the data should register a BroadcastReceiver in onResume() and unregister it in onPause().
You instantiate your BroadcastReceiver in your Activity's onCreate()
this.localBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Do what you have to do here if you receive data from the Service.
}
}
You create a Filter so your Activity only listens to a certain type of signals.
private IntentFilter notifIntentFilter new IntentFilter("com.you.yourapp.MY_SIGNAL");
in onResume()
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, notifIntentFilter);
in onPause()
LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
Now whenever you want to send data to your Activity, your Service can call:
final Intent intent = new Intent();
intent.setAction("com.you.yourapp.MY_SIGNAL");
// put your data in intent
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
If your Activity is awake, it will respond to the signal. Otherwise, if it's in the background, or it is not instantiated it won't.
You can apply this pattern to as many Activities as you wish.
Still, I have never used this inside the Application class. But you can try to register your receiver there. It might work, since if the Application class is destroyed, the BroadcastReceiver is destroyed too and thus probably unregistered as well.
The point is, if your Application gets destroyed, your Service will be killed as well. Unless you launched it in another process. But then it will have it's own instance of Application; and this is a complex thing you probably do not want to get into details now...
Important: since the Application class is not tied to any UI component, you can do whatever you need directly inside your service. If you need to manipulate the UI, then the pattern described above will work for you.
Please read about new Android's background limitations.
Edit:
Oh yeah right, if you need your Service to call a function declared in your Application class, you can just do
((MyApplication) getApplication()).myFunctionToHandleData(Intent intent);
I didn't really understand your question though, but either of the methods described above should work for you.
My BroadcastReceiver receives Intent.ACTION_NEW_OUTGOING_CALL. It needs to call this.setResultData(null) so that the number is not subsequently dialled, as described here.
This works fine.
How can I test this with Robolectric?
This is what I have:
// Create the intent
Intent myIntent = new Intent("Intent.ACTION_NEW_OUTGOING_CALL");
myIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, "123");
// Find my BroadcastReceiver
List<BroadcastReceiver> receivers = ShadowApplication.getInstance().getReceiversForIntent(myIntent);
MyReceiver receiver = (MyReceiver)receivers.get(0);
// Invoke it - I understand this is the correct way to do it in a Robolectric test
Context context = ShadowApplication.geInstance().getApplicationContext();
receiver.onReceive(context, myIntent);
My Broadcast receiver is launched, but when it calls setResultData() an exception is raised: java.lang.IllegalStateException: Call while result is not pending. This happens due to the state of the underlying BroadcastReceiver because setResultData()->checkSynchronousHint() finds that mPendingResult==null.
mPendingResult is only ever set non-null by setPendingResult(), which I cannot call (even using reflection).
What is the proper way to test BroadcastReceiver which needs to call setResultData() please?
I've tried to reproduce your issue and was not able.
Take a look to my test https://github.com/emartynov/robolectic-experiments/blob/master/robolectric-3.0-test/src/test/java/com/bijdorpstudio/myapplication/IncomingCallReceiverTest.java
However, I was not able to get BroadcastReceiver to be picked up by Robolectric from AndroidManifest.xml. Added by hand in tests.
And I think it is OK approach. I would add some xml test for correct BroadcastReceiver tagging in AndroidManifest.xml. Testing that android is correctly picking it up will be still my lowest priority task
Suppose i want to run both wakeful and non wakeful service alternatively.
Something like a toggle button. If its On then i have to start my WakefulIntentService with doWakefulWork() method.
And if its off then I have to do all same tasks in same WakefulIntentService but with a different method which don't interact with CPU and wifi in sleep mode something like non wakeful work in my word..
I just added a new method in my Service, but unable to go forward.
public class NetworkCommunicationService extends WakefulIntentService{
private static String UserID;
public NetworkCommunicationService() {
super("NetworkCommunicationService");
}
#Override
protected void doWakefulWork(Intent intent) {
if (Utils.isNetworkAvailable(this))
new SyncValidater().execute();
}
/**I have to do something like this.... */
protected void doNonWakefulWork() {
if (Utils.isNetworkAvailable(this))
new SyncValidater().execute();
}
Is their any solution for this... Or I have to create a new service to do the non wakeful work.
Please help. Thanks
Suppose i want to run both wakeful and non wakeful service alternatively
It is not clear to me what situation would make sense for this. WakefulIntentService does not support this scenario (or, if it does, it is by accident, not intention).
I would recommend switching to WakefulBroadcastReceiver. Then, when you want the service to run "wakefully", follow the WakefulBroadcastReceiver recipe and call startWakefulService(). If you want the service to run normally, just have the receiver call startService(). Note, though, that there is a chance that the service will not get an opportunity to run in this case, if the device falls asleep before the service starts.
I have a BroadcastReceiver, and on its OnReceive I am supposed to call a method which is defined in the MainActivity. wherever I searched I found that I will have to write the following code.
Bundle bundle = intent.Extras;
Intent callingIntent = new Intent(context, typeof(MainActivity));
callingIntent.PutExtra("isSynched", true);
callingIntent.AddFlags(ActivityFlags.NewTask);
callingIntent.AddFlags(ActivityFlags.SingleTop);
context.StartActivity(callingIntent);
Now this calls my method, but the app keeps opening up. I don't want that to happen. I want the method to be called when the app is in background and want the app to be in background. What should I do? I am using xamarin to write the code.
I have created a service that gets the data but after I receive data I have to call a method in MainActivity to update the calendar. I am currently doing it in OnReceive like this, public override void
OnReceive(Context context, Android.Content.Intent intent)
{
if (context.GetType().Equals(typeof(MainActivity)))
{
((MainActivity)context).SyncCalendar();
}
}
this context is coming as restrictedaccess. So not able to call SyncCalendar Method
Depending on whether you need or not execute that code all the time your app lives, I'd recommend:
AsyncThread: This is somelike an improved version of a Thread as it already implements some of the mechanisms you would need to set manually with a Thread, but it's not recommended to execute all your app life, just for ending processes. You may find more info there
Service: Otherwise, if your function is intended to run all your app's life long, Service is the correct choice. It's a bit harder to understand than the AsyncThread, as it's a class that it's executed until you stop it paralelly to your main UI, but it's not a thread. You may find this useful and also this.