I am trying to create an Application which toasts the battery level whenever the battery level changes. I am using Broadcast receiver and which is registered with 'ACTION_BATTERY_CHANGED'. It works fine in the background only when the app is still in the recents (i.e. the app is not closed), but it doesn't work when the app is closed!
['ACTION_BATTERY_CHANGED' is fired when the BatteryLevel changes.]
Below is the code:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//registering the reciever.
this.registerReceiver(this.mBatInfoReciever,new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
}
private BroadcastReceiver mBatInfoReciever = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
int level = intent.getIntExtra("level",0);
/*To get the Battery Level.*/
Toast t=Toast.makeText(getApplicationContext(),String.valueOf(level),Toast.LENGTH_LONG);
t.show();
}
};
}
Once your MainActivity is destroyed, your BroadcastReceiver goes away. This will occur:
If the user presses BACK, or
If the user navigates away by some other means (e.g., presses HOME) and your process is terminated
You seem to be defining "the app is closed" as being when the user swipes the app out of the overview screen (recent task list). That usually terminates the app's process, so your BroadcastReceiver will be gone at that point.
Related
I am trying to get actions performed on battery change. For that I have used the following code:
public class BatteryBroadcastReceiver extends
BroadcastReceiver {
DatabaseHelper db;
#Override
public void onReceive(Context context, Intent intent) {
db = new DatabaseHelper(context);
String cont1 = db.usercontact().toString();
String action = intent.getAction();
if (action != null && action.equals(Intent.ACTION_BATTERY_CHANGED)) {
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL,-1);
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE,-1);
int percentage = level * 100 / scale;
if(percentage <= 20){
// doing something like getting location
}
}
}
I have registered my broadcast in my activity:
public class Battery extends AppCompatActivity {
private BatteryBroadcastReceiver batteryReceiver = new BatteryBroadcastReceiver();
private IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_auto_location);
}
#Override
protected void onResume(){
super.onResume();
registerReceiver(batteryReceiver,intentFilter);
}
#Override
protected void onPause(){
unregisterReceiver(batteryReceiver);
super.onPause();
}
This general piece of code is working fine but it is restricting me to just this activity (may be because broadcast is registered in this activity). Like as long as this activity remains open I can perform some task but as I closes it, when battery gets low it perform no task. Now I don't know what to do. I have other activities too in my application and I want it to work even when I am not on this activity. Is there any way that I can register it in manifest so that it could work overall. I have tried doing that but then nothing happens on battery change. Thanks for help in advance :).
It is no longer possible to register for ACTION_BATTERY_CHANGED in the manifest. See https://developer.android.com/reference/android/content/Intent.html#ACTION_BATTERY_CHANGED
If you want to get triggered by this broadcast Intent in all of your activities, you can do this one of 2 ways:
Register and unregister the receiver in each and every one of your activities. You could simplify this by creating a BaseActivity that handles the register and unregister of the BroadcastReceiver and then extending all your activities from BaseActivity.
Register the receiver when your app starts in a custom Application class, or in onCreate() of your first Activity. Don't unregister the receiver. This causes a small memory leak, but I wouldn't worry about it.
When my service is running and I can see my app on the screen, everything works fine. My service sends broadcast messages and I can see them in my "MainActivity". But as soon as I push the home button and my app is no longer in the foreground, it stops. The service is still running but the broadcasts don't arrive at my "dead" or "pausing" app. This is my code:
Main Activity:
onCreate:
Intent intent = new Intent(getApplicationContext(), MainGate_Service.class);
startService(intent);
#Override
protected void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver(ServiceReceiver, new IntentFilter("MainGate_ring"));
}
#Override
protected void onStop() {
super.onStop();
LocalBroadcastManager.getInstance(this).unregisterReceiver(ServiceReceiver);
}
private BroadcastReceiver ServiceReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String message = intent.getStringExtra("ServiceMessage");
if(message != null) {
if(alarmActive && message.equals("ring"))
new soundPlayer().execute();
}
setNoti(message);
}
}
Service:
#Override
protected void onHandleIntent(Intent intent) {
ring = rcv_ring();
Intent ServiceIntent = new Intent("MainGate_ring");
ServiceIntent.putExtra("ServiceMessage", ring);
LocalBroadcastManager.getInstance(this).sendBroadcast(ServiceIntent);
}
whenever you press home button onStop gets call and there you are unregister receiver so there is not broadcast receiver who can receive broadcast.
LocalBroadcastManager.getInstance(this).unregisterReceiver(ServiceReceiver);
Remove above line from onStop() or unregister it whenever your service stop.
onStop#LocalBroadcastManager.getInstance(this).unregisterReceiver(ServiceReceiver);
literally unregister receiver. So there is no receiver to receive broadcasted message from service.
The problem is here
#Override
protected void onStop() {
super.onStop();
LocalBroadcastManager.getInstance(this).unregisterReceiver(ServiceReceiver);
}
Activity.onStart() :
Called after onCreate(Bundle) — or after onRestart() when the activity had been stopped, but is now again being displayed to the user. It will be followed by onResume().
Activity.onStop()
Called when you are no longer visible to the user. You will next receive either onRestart(), onDestroy(), or nothing, depending on later user activity.
So as soon as you leave your activity onStop() is called and you unregister your receiver.
Don't put your unregister code in onDestroy() either, because it may or may not be called and the Activity might be killed without calling it.
As suggested by #Naveen Dissanayake you might want to reconsider your logic.
You register your BroadcastReceiver inside activity, so, it will depend on Activity's lifecycle. When you press back button, activity goes into 'stopped' state - methods onPause and onStop is called. Android can also destroy this activity in it is low on resources.
Service, on the other hand, i smeant to be running inndefinetely, even when ui is not ready. If you wanat to receive notifications from Activity, there is two
possible solutions:
- Create, store and manage BroadcastReceiver in Application instance - Application class is still running until your app is destroyed. It seems like you want to play sound when service notify you about some action.
Register BroadcastReceiver in onCreate and unregister in onDestroy in notifications.
- Another solution - use another Service if you want to trigger some action or IntentService, reacting to that broadcast.
I woud consider solution - create some ResponseService, start it along with your MainGate_Service (from Application) and stop it from application too. In that Service register BroadcastReceiver or, add IntentFilter into manifest if you want it to start even when app is not running. In your Activity, bind to that service. Serive will know if some UI is attached (if Activity is bound), and, if it is, will notify activity. If it don't - will do some other things (perhaps, show notification)
If you want to receive Broadcast even if your Activity is in
Background then,
Register in onCreate(...)
#Override
protected void onCreate(Bundle sis) {
super.onCreate(sis);
setContentView(...);
LocalBroadcastManager.getInstance(this).registerReceiver(ServiceReceiver, new IntentFilter("MainGate_ring"));
}
Unregister in onDestroy()
#Override
protected void onDestroy() {
super.onDestroy();
LocalBroadcastManager.getInstance(this).registerReceiver(ServiceReceiver, new IntentFilter("MainGate_ring"));
}
I am currently working on a test application that will show a dialog that asks the user to stop using the app if he/she is driving. Supposing that the user has their GPS on, I have created a custom intent to be broadcast and then on the onReceive() method of the subclassed BroadcastReceiver class, the speed of the vehicle is calculated and if it's found to be in the range of usual car speeds, a dialog is shown to the user.
The second part (the onReceive() code) works great: the speed is calculated and the dialog is shown. However, I am at a loss about how the dialog can be shown at any point of the lifeycle of that specific activity. If the app is launched while the user is driving or if the user navigates to another activity and then back to the activity that does the speed check (essentially, if onCreate() or onResume() are called), the dialog is shown. If the dialog is dismissed and the user stays on the same activity, the dialog is not reshown, even if the user keeps driving (to be clear, the desired behavior is for the dialog to keep getting shown for as long as the user keeps driving and is in the activity). Here is the code of the activity that sends the Broadcast(the relevant code, everything else is omitted):
public class MainActivity extends Activity {
IntentFilter intentFilter;
SpeedReceiver speedceiver = new SpeedReceiver();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
broadcastSender();
intentFilter = new IntentFilter();
intentFilter.addAction("com.example.animationshitz.ACTION_GET_SPEED");
}
public void onResume() {
super.onResume();
registerReceiver(speedceiver, intentFilter);
Log.d("Loggger", "BROADCAST RECEIVER REGISTERED");
}
public void onPause() {
super.onPause();
unregisterReceiver(speedceiver);
}
public void broadcastSender() {
Intent intent = new Intent();
intent.setAction("com.example.animationshitz.ACTION_GET_SPEED");
sendStickyBroadcast(intent);
Log.d("Loggger", "BROADCAST SENT");
}
}
Am I completely wrong about this? I thought that the sticky broadcast would keep calling the onReceive() method of the receiver so that it would keep checking the speed in the background. Is there some other way to achieve what I want? Maybe the onReceive() method should start a service that keeps checking the speed in the background? Any help would be really appreciated.
I created a BroadcastReceiver and it runs only when my app shown in recent apps menu. If I remove my app from the recent apps the BroadcastReceiver will stop working.
How can I keep the BroadcastReceiver in background?
I register the BroadcastReceiver from my main activity (in OnCreate()).
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
registerReceiver(receiver, intentFilter);
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
}
};
This is not how you should register a receiver. You receiver stops working, because you construct it in onCreate, which means it will live as long as your app is alive. When the app gets destroyed, you also lose the the receiver.
If you register receiver inside an activity, you should always register it in onResume and deregister onPause, which will make it available while the activity is visible to the user. This is a use case when you want to have an active receiver while user interacts with an activity.
If you want a background receiver, you need to register it inside the AndroidManifest (with intent filter), add an IntentService and start it when you receive a broadcast in the receiver.
Here is a tutorial, you are interested in chapter 3.
If you need to be always on, start a foreground service. There is function in Service that lets you: startForeground. Then register your receiver when service is created and deregister when it's destroyed. Foreground services are quite nasty though.
Use a service with it.
Services can survive when the app dies if they have the right flag example:
public class MyService extends Service {
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY; //this defines this service to stay alive
}
#Override
public void onCreate() {
super.onCreate();
appStatus = APPISUP;
//This is a thread that stays alive for as long as you need
new CheckActivityStatus().execute();
//Not needed but in case you wish to lauch other apps from it
}
private class CheckActivityStatus extends AsyncTask<String, Integer, String> {
#Override
protected String doInBackground(String... params) {
while(true) {
... //add something that breaks eventually
}
}
}
To lauch the service you have to lauch it from an activity like so:
Intent service = new Intent(getBaseContext(), MyService.class);
startService(service);
With the service the BroadcastReceiver still functions receiving whatever you want.
Note that the service sometimes stops and comes back. I haven't found out why but I'm betting on priorities of other apps that may ask the system to halt the service
I am creating a replacement Car Home app for Android 2.0+ devices. The app needs to launch when the phone is inserted into the car dock, as well as terminate when it is removed from the dock. It also needs to be able to be launched from the app drawer.
I'm having a problem right now where once the phone is inserted and removed from the dock, I can no longer launch the app from the app drawer because every time I launch the app my BroadcastReceiver picks up a DOCK_EVENT action for some reason. I created a test project that only registers my BroadcastReceiver, and the same thing happens.
Here's the code for the BroadcastReceiver:
public class CarDockBroadcastReceiver extends BroadcastReceiver {
/**
* #see android.content.BroadcastReceiver#onReceive(Context,Intent)
*/
#Override
public void onReceive(Context context, Intent intent) {
// TODO Put your code here
if(intent.getExtras().containsKey("android.intent.extra.DOCK_STATE")){
int state = intent.getExtras().getInt("android.intent.extra.DOCK_STATE",1);
if(state == 0){
Log.i("Dock", "Removed from dock!");
((Activity)context).finish();
}
}
}
}
My main Activity is as follows:
public class MainActivity extends Activity {
/** Called when the activity is first created. */
CarDockBroadcastReceiver receiver;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
IntentFilter filter = new IntentFilter("android.intent.action.DOCK_EVENT");
receiver = new CarDockBroadcastReceiver();
registerReceiver(receiver, filter);
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
unregisterReceiver(receiver);
super.onDestroy();
}
}
The main Activity has an intent filter for action.MAIN, category.LAUNCHER, category.DEFAULT, and category.CAR_DOCK. Any ideas on why this is happening?
Intent.ACTION_DOCK_EVENT is a sticky broadcast. This means that when you register a receiver for it, you will immediately get the last-broadcast Intent for that action, then all subsequent broadcasts until it is unregistered. There is nothing much you can do about it, other than finding a way of dealing with the situation.
BTW, I recommend using Intent.ACTION_DOCK_EVENT rather than "android.intent.action.DOCK_EVENT" in your IntentFilter. This way, if for some goofy reason they change the actual string, your code does not need to change.