I need to invoke a method on main thread, for that purpose i am using #UiThreadTest and calling the method inside my test.
That method is responsible to send broadcast receiver.
So i am registering the receiver and setting a boolean value to test if broadcast has been sent or not.
In logs i am able to see the broadcast is executed.
But getting assertion failure error inside test.
Is this some threading issue?
//broadcase receiver
private class Receiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, final Intent intent) {
if (mUiUpdateReceiver != null && intent.getAction().equals(MessageCenter.INTENT_UI_REFRESH)) {
mbroadcastFlag = true;
}
}
}
//test
#UiThreadTest
public void test_broadcast () throws Exception{
mUiUpdateReceiver = new Receiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(SampleClass.INTENT_REFRESH);
LocalBroadcastManager.getInstance(mAppContext).registerReceiver(mUiUpdateReceiver, intentFilter);
mbroadcastFlag = false;
sampleClass.method();
Thread.sleep(2000);
assertTrue(mbroadcastFlag);
LocalBroadcastManager.getInstance(mAppContext).unregisterReceiver(mUiUpdateReceiver);
}
Konstantin Loginov's answear is ok, but it uses JUnit3 (because he extends ApplicationTestCase; if you extend any TestCase class Android framework will run your tests with JUnit3 runner by default).
If you want to use newer JUnit 4 You can use something like this:
public class MyReceiver extends BroadcastReceiver {
boolean flag;
#Override
public void onReceive(Context context, Intent intent) {
Uri packageName = intent.getData();
if(packageName.toString().equals("package:" + context.getPackageName())){
flag = true;
}
}
}
And the Test itself:
public class MyReceiverTest {
Context context;
#Before
public void init() {
context = new RenamingDelegatingContext(InstrumentationRegistry.getInstrumentation().getTargetContext(), "test_");
}
#Test
public void myReceiverTest() throws InterruptedException {
MyReceiver receiver = new MyReceiver();
//remeber to change Intent.ACTION_PACKAGE_REPLACED
//with your action name from your mainfest file
LocalBroadcastManager.getInstance(context).registerReceiver(receiver, new IntentFilter(Intent.ACTION_PACKAGE_REPLACED));
String pn = context.getPackageName();
Intent i = new Intent(Intent.ACTION_PACKAGE_REPLACED);
i.setData(Uri.parse("package:" + context.getPackageName()));
LocalBroadcastManager.getInstance(context).sendBroadcast(i);
Thread.sleep(2000);
boolean b = receiver.flag;
assertTrue(b);
LocalBroadcastManager.getInstance(context).unregisterReceiver(receiver);
assertTrue(true);
}
I wrote for you a small hello-world with broadcast receiver and unit test for it.
Here's my broadcast receiver:
public class UiRefreshReceiver extends BroadcastReceiver {
boolean broadcastFlag;
public UiRefreshReceiver() {}
#Override
public void onReceive(Context context, Intent intent) {
broadcastFlag = true;
}
}
And test class for it:
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
#UiThreadTest
public void test_broadcast () throws Exception{
UiRefreshReceiver mUiUpdateReceiver = new UiRefreshReceiver();
LocalBroadcastManager.getInstance(getContext()).registerReceiver(mUiUpdateReceiver, new IntentFilter("my-event"));
assertFalse(mUiUpdateReceiver.broadcastFlag);
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(new Intent("my-event"));
Thread.sleep(2000);
assertTrue(mUiUpdateReceiver.broadcastFlag);
LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(mUiUpdateReceiver);
}
}
I hope, it'd help.
Though, in my humble opinion - it's enough to simply unit-test onReceive() code.
Related
I would like to test my BroadcastReceiver, which depends on sticky broadcasts, with Robolectric. By default Robolectric does not support sticky broadcasts so I created my custom Context to get sticky broadcasts working like this:
public class MyContext extends MockContext {
public MyContext() {
super();
}
#Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
if(receiver == null) { // A sticky broadcast was requested
Intent request = new Intent();
String action = filter.getAction(0);
if(Intent.ACTION_BATTERY_CHANGED.equals(action)) {
request.putExtra(BatteryManager.EXTRA_PLUGGED, 1);
} else if(Intent.ACTION_HEADSET_PLUG.equals(action)) {
request.putExtra("state", 1);
}
return request;
}
return super.registerReceiver(receiver, filter);
}
}
My problem is that I have to use RuntimeEnvironment.application.getApplicationContext to get a valid Context object (I tried to simply call the constructor of my custom Context but that does not work). So how can I get a valid instance of my custom Context or isn't that possible with robolectric?
EDIT: Here is the code from my test and my BroadcastReceiver:
#Before
public void setup() {
context = RuntimeEnvironment.application.getApplicationContext();
receiver = new MyBroadcastReceiver(); // Create Receiver
}
#After
public void finish() {
context.unregisterReceiver(receiver);
}
#Test
public void validateUsbChargingChange() {
IntentFilter filter = new IntentFilter("android.intent.action.ACTION_POWER_CONNECTED");
context.registerReceiver(receiver, filter);
// Simmulate SocketCharging by sending the corresponding Intent
Intent chargingChange = new Intent("android.intent.action.ACTION_POWER_CONNECTED");
RuntimeEnvironment.application.sendBroadcast(chargingChange);
validatePreferences();
}
BroadcastReceiver:
#Override
public void onReceive(Context context, Intent intent) {
IntentFilter iFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent chargingIntent = appContext.registerReceiver(null, iFilter); // sticky
int pluggedState = chargingIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean usbCharge = (pluggedState == BatteryManager.BATTERY_PLUGGED_USB);
if(usbCharge) { /* Write values to preferences */ }
}
you can explicitly sendStickBroadcast in you test class constructor, just like below
#RunWith(RobolectricTestRunner.class)
public class BatterySettingActivityTest {
{
ApplicationProvider.getApplicationContext().sendStickyBroadcast(new Intent(Intent.ACTION_BATTERY_CHANGED).putExtra(BatteryManager.EXTRA_LEVEL, 75).putExtra(BatteryManager.EXTRA_PLUGGED, 0));
}
#Rule
public ActivityScenarioRule<BatterySettingActivity> activityScenarioRule = new ActivityScenarioRule<>(BatterySettingActivity.class);
#Test
public void testStickBroadcast() {
activityScenarioRule.getScenario().onActivity(activity -> {
System.out.println("intentForStickyBroadcast=" + activity.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)));
});
}
}
I have a function in activity I want to run this function with broadcastreceiver. How can I make this?
public class Myclass extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
}
}
This is my broadcastreceiver class I want to run function which is in my activty please tell me with some code how to do this.
If the method you want to execute needs your activity instance, then you can register the broadcast receiver inside your activity, so it can access your activity's state and functions.
In your Activity "onCreate" method:
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("Your Intent action here");
intentFilter.addAction("Another action you want to receive");
final BroadcastReceiver myReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
theFunctionYouWantToExecute();
}
};
registerReceiver(myReceiver, intentFilter);
And in your "onDestroy" method:
unregisterReceiver(myReceiver);
Keep in mind that in this case your broadcast receiver has full access to your activity state, BUT it's lifecycle will be conditioned to the activity lifecycle.
Another option you have is to declare your activity method as static, so you can execute it in any part of your application.
You can declare an interface in Myclass and implement it in your MainActivity
public class Myclass extends BroadcastReceiver{
public interface MyClassInterface {
void onMyClassReceive();
}
private MyClassInterface mListener;
public Myclass(MyClassInterface mMyClassInterface) {
mListener = mMyClassInterface;
}
#Override
public void onReceive(Context context, Intent intent) {
mListener.onMyClassReceive();
}
}
Then in your MainActivity:
public class MainActivity implements Myclass.MyClassInterface {
private mMyClass Myclass = new Myclass(this);
#Override
public void onMyClassReceive() {
// Do stuff when Myclass.onMyClassReceive() is called,
// which will be called when Myclass.onReceive() is called.
}
}
You are almost there. Just create your method in the Activity and using Activity's instance call that method. Remember that your method inside your Activity should be not private.
public class Myclass extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
new YourActivity().yourFunction();
}
}
If you want to create a static method inside your Activity then
public class Myclass extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
YourActivity.yourFunction();
}
}
To trigger the Broadcast, you have to pass an Intent. If you want to trigger it from any Activity then
Intent intent = new Intent();
this.sendBroadcast(intent);
If you want to trigger the Broadcast from a Fragment then
Intent intent = new Intent();
getActivity().sendBroadcast(intent);
I know it's quote naif, but you could call a static method in your activity.
In your activity you declare the method like this:
public static <return_type> yourMethod(<input_objs>){
....
Your code
....
}
In the receiver you can use this function just calling:
YourActivityClass.yourMethod(<input_objs>);
I hope it helped.
I'm trying to add to my broadcast receiver an if, so I will start a different activity if a call has been answered, to the regular activity I start usually, if the screen has just been turned on.
Now as you can see down here I have the class screenJump starting when the user wakes up phone.
I would like to start a phoneActivity I wrote when the user wakes up phone, but only when a call has been answered.
This is my service now.
public class MyService extends Service {
BroadcastReceiver bd;
public MyService() {
}
class ScreenReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent startupIntent = new Intent(context, ScreenJump.class);
startupIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(startupIntent);
}
public ScreenReceiver()
{
}
}
#Override
public void onCreate()
{
super.onCreate();
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_PRESENT);
bd = new ScreenReceiver();
registerReceiver(bd, filter);
}
}
I have a service and the main activity. Is there any way I can share a String in particular between these two. I have a method in the MainActivity that has to be executed only after the String from the service arrives. Is there any way of doing the same.
Thanks.
The following code will allow your Service to publish a String to your Activity. If you need to send data in the other direction use a more casual approach such as startService(Intent).
Setup a local BroadcastReceiver in your Activity:
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (!MyService.ACTION_NEW_STRING.equals(intent.getAction()) return;
String myString = intent.getStringExtra(MyService.EXTRA_STRING);
// do whatever you need to do here
}
}
#Override
public void onStart() {
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(mReceiver, new IntentFilter(MyService.ACTION_NEW_STRING));
}
#Override
public void onStop() {
LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(mReceiver);
}
In your Service declare needed constants:
public static final String ACTION_NEW_STRING = "your_application_id.ACTION_NEW_STRING";
// or BuildConfig.APPLICATION_ID + ".ACTION_NEW_STRING" in Android Studio
public static final String EXTRA_STRING = "EXTRA_STRING";
private LocalBroadcastManager mManager;
#Override
public void onCreate() {
mManager = LocalBroadcastManager.getInstance(getApplicationContext());
}
In your service once you have your new String:
Intent i = new Intent(ACTION_NEW_STRING);
i.putExtra(EXTRA_STRING, myString);
mManager.sendBroadcast(i);
More reading on this topic: http://developer.android.com/reference/android/content/BroadcastReceiver.html
Here is the code that I use in BroadCastReceiver:
public class SMSBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// SMSReceivedService is my custom service, extends from Service class
Intent myIntent = new Intent(context, SMSReceivedService.class);
myIntent.putExtras(intent);
WakefulIntentService.sendWakefulWork(context, myIntent);
}
}
public class SMSReceivedService extends extends WakefulIntentService {
public SMSReceivedService(String name) {
super(name);
}
#Override
protected void doWakefulWork(Intent intent) {
// main code here
}
}
Here is the piece of code that's inside WakefulIntentService
synchronized private static PowerManager.WakeLock getLock(Context context) {
if (lockStatic == null) {
PowerManager mgr =
(PowerManager)context.getSystemService(Context.POWER_SERVICE);
lockStatic=mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, NAME);
lockStatic.setReferenceCounted(true);
}
return(lockStatic);
}
public static void sendWakefulWork(Context ctxt, Intent i) {
getLock(ctxt.getApplicationContext()).acquire();
ctxt.startService(i);
}
As who has used WakefullIntentService will know that, just call sendWakefulWork and WakefullIntentService will do all stuff in the background. But in code above, WakefullIntentService just hold wake lock, but after finish, I don't see any code that release this lock. Is it true ? So it will affect the Android User. Please give me ideas.
Either you are using my WakefulIntentService, or you invented your own with the same name. If you are using my WakefulIntentService, the lock is released in onHandleIntent(), as you can tell by reading the source code.