Inject into BroadcastReceiver before onReceive() - android

I tried to find the answer, how to inject fields into the BroadcastReceiver, but everything I find is like this code in the onReceive():
((MyApplication) context.getApplicationContext()).getAppComponent().inject(this);
I test BroadcastReceiver with
MyReceiver receiver = new MyReceiver();
receiver.onReceive(context, testIntent);
In this case, it's impossible to inject mocks into the Receiver. How do you manage this situation? I tried to do something like this:
#Override public void onReceive(Context context, Intent intent) {
// injecting is here
onPostInjectReceive(context, intent);
}
void onPostInjectReceive(Context context, Intent intent) {
}
But even with this workaround it's impossible to make view tests with Espresso, only unit testing. How am I able to inject before I get the callback?
Really I can store BroadcastReceiver component on the App level, but it looks like dirty solution.

Related

Android Change a variable in service from other app

the title says all, I need to change the variable of my service from a activity in my other app , what to finalize the service or not, this is possible?
I found the Message object , but I do not quite understand
The simplest solution would be to implement a BroadcastReceiver. Your Service listens for the Broadcast and the other App sends the Broadcast.
Example Reciever:
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Get bundle from intent and use it to set your Variable in your Service
}
}
Example Broadcaster (courtesy of Vogella):
Intent intent = new Intent();
intent.setAction("de.vogella.android.mybroadcast");
sendBroadcast(intent);

Why would LocalBroadcastManager not work instead of Context.registerReceiver?

I had to implement a feature to this app which consists of an Activity and a Service working on the background (it implements Service, not IntentService).
I went through a few tutorials on the Internet that are supposed to work, and they all use LocalBroadcastManager, which by the way is the recommended by Android:
If you don't need to send broadcasts across applications, consider
using this class with LocalBroadcastManager instead of the more
general facilities described below.
I literally lost a day to find out the problem why it wouldn't work for me: it only works if I use Context.sendBroadcast(). and Context.registerReceiver() instead of the LocalBroadcastManager methods.
Now my app is working, but I feel I am going against the best practices, and I don't know why.
Any ideas why it could be happening?
EDIT:
After I wrote this question I went further on the problem. LocalBroadcastManager works through a Singleton, as we should call LocalBroadcastManager.getInstance(this).method(). I logged both instances (in the Activity and in the Service) and they have different memory addresses.
Now I came to another question, shouldn't a Service have the same Context as the Activity that called it? From this article a Service runs on the Main Thread, hence I'd think the Context would be
the same.
Any thoughts on that? (sorry for the long post)
Code samples:
MyService
public class MyService extends Service {
...
// When an event is triggered, sends a broadcast
Intent myIntent = new Intent(MainActivity.MY_INTENT);
myIntent.putExtra("myMsg","msg");
sendBroadcast(myIntent);
// Previously I was trying:
// LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(myIntent);
}
MyActivity
public class MainActivity {
...
private BroadcastReceiver messageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("onReceive", "received!");
// TODO something
}
};
#Override
protected void onResume() {
super.onResume();
registerReceiver(messageReceiver, new IntentFilter(MY_INTENT));
// Previously I was trying:
// LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(messageReceiver, new IntentFilter(MY_INTENT));
}
}
I've never used LocalBroadcastManager, but it sounds like you have to register your receiver on there (i.e. lbm.registerReceiver(...), not mycontext.registerReceiver(...)). Are you doing that?
Now I came to another question, shouldn't a Service have the same Context as the Activity that called it? From this article a Service runs on the Main Thread, hence I'd think the Context would be the same.
The Context class is not related to threads. In fact, both Service and Activity are (indirect) subclasses of Context -- so they're their own Contexts! That's why you can use "this" as a Context.
But regardless of which context you send into LocalBroadcastManager.getInstance(), you should be getting the exact same LBM instance out. I can't think of any reason that you wouldn't -- except if you're running the Activity and Service in different processes?
Declaration:
private BroadcastReceiver receiver;
Initialization:
receiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
//todo
}
};
Registration:
LocalBroadcastManager.getInstance(context).registerReceiver(receiver, new IntentFilter("RECEIVER_FILTER"));
context can be any type of Context, you can use the application context.
Unregister:
LocalBroadcastManager.getInstance(context).unregisterReceiver(receiver);
Broadcast:
Intent intent = new Intent("RECEIVER_FILTER");
intent.putExtra("EXTRA", someExtra);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
check out if your Service and Activity are run in different process, LocalBroadcastManager can't apply in different process.(you should see it in AndroidManifest.xml file)

ShadowApplication: registered receivers contain uninitialized object under test

First, I get a spied instance of my class under test:
TestedClass testedClass = spy(new TestedClass(Robolectric.buildActivity(Activity.class).create().get());
Then, some changes happen to the tested class:
testedClass.someString = "whatever"
Then, I simulate sending an intent to a broadcast receiver registered in the tested class:
ShadowApplication shadowApplication = Robolectric.getShadowApplication();
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
List<BroadcastReceiver> broadcastReceivers = shadowApplication.getReceiversForIntent(intent);
broadcastReceivers.get(0).onReceive(Robolectric.application, intent);
The BroadcastReceiver is found in the list, so I can call onReceive and onReceive event is fired:
public void onReceive(Context context, Intent intent) {
Log.i(tag, someString);
}
However, the TestedClass object that appears is an uninitialized version, this is: someString == ""
It's not the same object that was being spied.
Maybe, it's the normal behaviour, but I would like to get the same instance I created for the test, not a mocked one created parallel.
It depends on the way you instance the BroadcastReceiver.
If you instance the BroadcastReceiver with a default value, the simulated calls to the BroadcastReceiver won't be executed
//Class member myBroadcastReceiver
public BroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
However, if you instance the BroadcastReceiver later, it works properly:
public myBroadcastReceiver;
public initializeBroadcastReceiver() {
myBroadcastReceiver = new MyBroadcastReceiver();
}
These are the facts, nonetheless, I still don't know if the first case is expected behaviour or it is a flaw of Robolectric.

Broadcast Receiver class and registerReceiver method

Hi i am trying to understand Broadcast Receiver , i went through many sample codes , but still have some doubts. I wanted to know when we have to extend the Broadcast Receiver class and when should we use registerReceiver() method and when should we create object for BroadcastReceiver. In some programs i came across registerReceiver methods being used but without extending the Broadcast Receiver class. I also wanted to know how the onReceive method gets called.
Which approach should be used when?
here is the registerReceiver method:
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
switch (getResultCode()) {
........
}
}
}, new IntentFilter(SENT));
Object being created for BroadcastReceiver:
private BroadcastReceiver intentReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
.................
}
};
Android has intent action for broadcast receiver. BroadCast receiver will be trigger when it listen any action which registered within it.
Now we will take one example :
That we need to listen the action of "whenever any bluetooth device connect to our device". For that android has it fix action android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED
So you can get it via manifest & registration also
BY Manifest Registration:
Put this in your manifest
<receiver android:name="MyBTReceiver">
<intent-filter>
<action android:name="android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED" />
</intent-filter>
</receiver>
Create MyBTReceiver.class
public class MyBTReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals("android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED")){
Log.d(TAG,"Bluetooth connect");
}
}
}
That was the simplest broadcast Receiver.
Now,
if you are only interested in receiving a broadcast while you are running, it is better to use registerReceiver(). You can also register it within your existing class file. you also need to unregister it onDestroy().
here, you dont need any broadcast registration in manifest except activity registration
For example
public class MainActivity extends Activity {
IntentFilter filter1;
#Override
public void onCreate() {
filter1 = new IntentFilter("android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED");
registerReceiver(myReceiver, filter1);
}
//The BroadcastReceiver that listens for bluetooth broadcasts
private final BroadcastReceiver myReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equalsIgnoreCase("android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED")) {
Log.d(TAG,"Bluetooth connect");
}
}
};
#Override
public void onDestroy() {
unregisterReceiver(myReceiver);
}
}
In both cases BroadcastReceiver will be extended. In your second example you create so called anonymous class. New class has no specific name, that is why it's called so. Anyway this new class extends BroadcastReceiver and overrides onReceive() method.
Now back to your question. There are two kinds of receivers - statically and dynamically defined ones.
If you declare your receiver in AndroidManifest file, then it is statically defined. In this case you need to refer to a class implementing BroadcastReceiver by name. As you can see, you cannot use an anonymous class, because the last has no name. You have to explicitly implement a receiver. It's worth to mention, that in this case you do not use registerReceiver() method. Android does it for you automatically.
If you declare receivers dynamically (for instance in activity's onResume() method), then you can use anonymous class for that. To register a receiver you call registerReceiver() method. You can also use a named class too. Both options are valid in this case.
Hope this explains the difference.
In both case you are creating object.But in first case there is not any reference for
the receiver object so it can not be unregistered later but second one has so it can be
unregistered after registering object using below methods:
registerReceiver(intentReceiver );
unregisterReceiver(intentReceiver );

Robolectric and IntentServices

Using Robolectric, how would one go about testing an IntentService that broadcasts intents as a response?
Assuming the following class:
class MyService extends IntentService {
#Override
protected void onHandleIntent(Intent intent) {
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent("action"));
}
}
In my test case, I'm attempting to do something like this:
#RunWith(RobolectricTestRunner.class)
public class MyServiceTest{
#Test
public void testPurchaseHappyPath() throws Exception {
Context context = new Activity();
// register broadcast receiver
BroadcastReceiver br = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// test logic to ensure that this is called
}
};
context.registerReceiver(br, new IntentFilter("action"));
// This doesn't work
context.startService(new Intent(context, MyService.class));
}
}
MyService is never started using this approach. I'm relatively new to Robolectric, so I'm probably missing something obvious. Is there some sort of binding I have to do before calling startService? I've verified that broadcasting works by just calling sendBroadcast on the context. Any ideas?
You can't test the service initialization like you're trying to do. When you create a new activity under Robolectric, the activity you get back is actually a ShadowActivity (kind of). That means when you call startService, the method that actually gets executed is this one, which just calls into ShadowApplication#startService. This is the contents of that method:
#Implementation
#Override
public ComponentName startService(Intent intent) {
startedServices.add(intent);
return new ComponentName("some.service.package", "SomeServiceName-FIXME");
}
You'll notice that it doesn't actually try to start your service at all. It just notes that you attempted to start the service. This is useful for the case that some code under test should start the service.
If you want to test the actual service, I think you need to simulate the service lifecycle for the initialization bit. Something like this might work:
#RunWith(RobolectricTestRunner.class)
public class MyServiceTest{
#Test
public void testPurchaseHappyPath() throws Exception {
Intent startIntent = new Intent(Robolectric.application, MyService.class);
MyService service = new MyService();
service.onCreate();
service.onStartCommand(startIntent, 0, 42);
// TODO: test test test
service.onDestroy();
}
}
I'm not familiar with how Robolectric treats BroadcastReceivers, so I left it out.
EDIT: It might make even more sense to do the service creation/destruction in JUnit #Before/#After methods, which would allow your test to only contain the onStartCommand and "test test test" bits.

Categories

Resources