Since Android Oreo you cannot start a service when the app is not in foreground. In my app I start a service in the activity's onStart-method. This works perfectly fine most of the times. However, from time to time an IllegalStateException is thrown saying that the application is trying to start a service while in the background:
java.lang.IllegalStateException: Not allowed to start service Intent { act=ui cmp=com.someapp/.services.ConnectionService }: app is in background uid UidRecord{8d70361 u0a255 TPSL bg:+3m12s948ms idle change:cached procs:1 proclist:20368, seq(0,0,0)}
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1666)
at android.app.ContextImpl.startService(ContextImpl.java:1611)
at android.content.ContextWrapper.startService(ContextWrapper.java:677)
at com.someapp.ui.SomeActivity.connectToBackend(SomeActivity.java:62)
at com.someapp.ui.SomeActivity.onStart(SomeActivity.java:55)
at com.someapp.ui.MainActivity.onStart(MainActivity.kt:228)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1391)
at android.app.Activity.performStart(Activity.java:7348)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3131)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:180)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:165)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:142)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1947)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7032)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:964)
I start the service as follows:
#Override
protected void onStart() {
if (BuildConfig.DEBUG_MODE) Log.d(TAG, "activity started");
super.onStart();
connectToBackend();
}
void connectToBackend() {
Intent intent = new Intent(this, ConnectionService.class);
intent.setAction("ui");
startService(intent);
getApplicationContext().bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
I noticed that this only happens when I lock and then unlock the phone while in the app. I cannot reproduce it consistently.
Does somebody have the same issue?
The device I'm using is a Samsung Galaxy S10e.
For post Nougat devices, use startForegroundService, and for pre Oreo devices, use startService
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent);
} else {
startService(intent);
}
I have hundred of crashes reported by my users and I still can't find a fix for it. These crashes are coming from Android 8 (Samsung, Huawei, Google).
I am getting these two crashes:
Fatal Exception: android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1881)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6938)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
and the other one:
Fatal Exception: android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2104)
at android.os.Handler.dispatchMessage(Handler.java:108)
at android.os.Looper.loop(Looper.java:166)
at android.app.ActivityThread.main(ActivityThread.java:7428)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
I assume these crashes are the same, but as you can see the stack trace shows different line of code.
The problem is that I can't reproduce it, everything works fine on my devices and my emulator. However, I (somehow) reproduced by creating a service without calling the startForeground() within the Service class.
I'm unable to "catch" the exception, because it comes from system-level right after 5 seconds when the service was created.
What have I done is that I have created a method which creates a sticky notification and calling the startForeground method (my Service class):
private void startWithNotification() {
Resources res = getResources();
String title = res.getString(R.string.application_name);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createChannels();
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, ANDROID_CHANNEL_ID)
.setContentTitle(title)
.setChannelId(ANDROID_CHANNEL_ID)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setOngoing(true)
.setAutoCancel(false)
.setSmallIcon(R.drawable.ic_siluette)
.setColor(ContextCompat.getColor(this, R.color.colorPrimary))
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.application_icon));
startForeground(NOTIFICATION_APP, builder.build());
}
private void createChannels() {
// create android channel
NotificationChannel androidChannel = new NotificationChannel(ANDROID_CHANNEL_ID, ANDROID_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
// Sets whether notifications posted to this channel should display notification lights
androidChannel.enableLights(true);
// Sets whether notification posted to this channel should vibrate.
androidChannel.enableVibration(true);
// Sets the notification light color for notifications posted to this channel
androidChannel.setLightColor(Color.GREEN);
// Sets whether notifications posted to this channel appear on the lockscreen or not
androidChannel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.createNotificationChannel(androidChannel);
}
This method is getting called from different Service life-cycle-events:
onCreate()
onStartCommand()
stopService()
onDestroy()
I am calling the method within these events, because people said that the Service might not being created and it's automatically destroyed.
The service gets started when an incoming or an outgoing call is made via BroadcastReceiver:
public class IncomingOutgoingCallReceiver extends BroadcastReceiver {
private void callAppService(Context context, int callType) {
Intent intent = new Intent(context, MyService.class);
Bundle bundle = new Bundle();
bundle.putInt(CALL_TYPE, callType);
intent.putExtras(bundle);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
}
else {
context.startService(intent);
}
}
private void onCallEnd(Context context) {
context.stopService(new Intent(context, MyService.class));
}
}
The Service class:
public class MyService extends Service {
private void handleIntent(Intent intent) {
// Use intent data and do work
if (canStartService(intent)) {
return;
}
}
private boolean canStartService(Intent intent) {
// multiple checks
// if (intent bundle contains ... ) return false;
// if (phone number contains .... ) return false;
return true;
}
#Override
public void onCreate() {
super.onCreate();
startWithNotification();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
handleIntent(intent);
startWithNotification();
return START_NOT_STICKY;
}
private void startWithNotification() {
// Contains the code from above (didn't put here because of space)
}
#Override
public boolean stopService(Intent name) {
startWithNotification();
return super.stopService(name);
}
// Can be called from different Views which are attached to the WindowManager (user interacting with the UI)
public void stopService() {
startWithNotification();
stopForeground(true);
stopSelf();
}
#Override
public void onDestroy() {
startWithNotification();
super.onDestroy();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
I literally searched everywhere I could and but just could not find a solution to this thing!!
So I am building an count down Timer app. But the problem I face is that each time it counts till 00:00.0, it waits an additional ~5seconds before it proceeds to sound the alarm. And if I happen to click anything in that period it crashes the app, but if I wait till it sounds the alarm and repeat the process it will wait ~5sec again but this time if I press Reset(during or after those 5sec) for example, it is working just fine! Here is the error:
07-13 14:51:08.838 8808-8808/tech.dronerace.productivitytimer D/AndroidRuntime: Shutting down VM
07-13 14:51:08.838 8808-8808/tech.dronerace.productivitytimer E/AndroidRuntime: FATAL EXCEPTION: main
Process: tech.dronerace.productivitytimer, PID: 8808
java.lang.RuntimeException: Unable to start service tech.dronerace.productivitytimer.Ringtone#11d1fec with Intent { cmp=tech.dronerace.productivitytimer/.Ringtone (has extras) }: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.media.MediaPlayer.stop()' on a null object reference
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3027)
at android.app.ActivityThread.-wrap17(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1442)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.media.MediaPlayer.stop()' on a null object reference
at tech.dronerace.productivitytimer.Ringtone.onStartCommand(Ringtone.java:52)
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3010)
at android.app.ActivityThread.-wrap17(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1442)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
07-13 14:56:09.149 8808-8808/tech.dronerace.productivitytimer I/Process: Sending signal. PID: 8808 SIG: 9
and here is my method code:
public void countDone() {
((Button) findViewById(R.id.b_start)).setVisibility(View.GONE);
((Button) findViewById(R.id.b_reset)).setVisibility(View.VISIBLE);
((Button) findViewById(R.id.b_stop)).setVisibility(View.GONE);
((TextView) findViewById(R.id.timer_id)).setText("00:00");
((TextView) findViewById(R.id.timerMs_id)).setText(".0");
//----------------Alarm && Vibration && Progress----------------\\
final Intent i = new Intent(SecondStep.this, Alarm.class);
i.putExtra("extra", "soundOn");
PendingIntent pi = PendingIntent.getBroadcast(getApplicationContext(), 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), pi);
} else {
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), pi);
}
Button b = (Button) findViewById(R.id.b_reset);
b.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
updateTimer(startTimeInitialG);
i.putExtra("extra", "soundOff");
PendingIntent.getBroadcast(getApplicationContext(), 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
sendBroadcast(i);
clickReset(v);
((Button) findViewById(R.id.b_start)).setVisibility(View.VISIBLE);
((Button) findViewById(R.id.b_reset)).setVisibility(View.GONE);
}
});
}
Now I chose the Pending Intent as this way I can put Extras to determine weather the music has been shut down by pressing the reset button or is it still playing. So knowingly that it is an Pending intent I set it up to fire immediately as well as for the Alarm to be exact(see the code above)as well as the flag to Update. But nothing seems to help. I then also switched from my custom built timer to CountDownTimer class thinking that there might be an inherit fault in AlarmManager being used for Timer but the same problem reappeared.
Why is my alarm being called with an delay? What am I doing wrong? :P
PS here is my Ringtone class(due to the error).
public class Ringtone extends Service {
MediaPlayer alarmPlayer;
boolean isRunning;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//get info on weather the reset was pressed and convert it to int so you can see weather you play or stop the music
String state = intent.getExtras().getString("extra");
assert state != null; //prevents app from crashing
switch (state) {
case "soundOn":
startId = 1;
break;
case "soundOff":
startId = 0;
break;
default:
startId = 0;
break;
}
Log.e("Is the state = 1 or 0?", state);
System.out.println(startId);
//we pressed reset or ('soundOff' ->extra) then cut it off
if(startId == 0 ) {
alarmPlayer.stop();
alarmPlayer.reset();
isRunning = false;
}
//we pressed nothing and is automatically ('soundOn' ->extra) so let it play
else if(startId == 1) {
alarmPlayer = MediaPlayer.create(this, R.raw.imperialmarch);
alarmPlayer.start();
isRunning = true;
}
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
Log.e("On Destroy called..", "yes");
super.onDestroy();
}
}
Why are you creating a PendingIntent and setting an alarm that should go off immediately to trigger your BroadcastReciever? You can just send the broadcast Intent yourself, like this:
final Intent i = new Intent(SecondStep.this, Alarm.class);
i.putExtra("extra", "soundOn");
sendBroadcast(i);
This will trigger immediately, and you don't need a PendingIntent, nor the involvement of the alarm manager.
I've noticed someone who is using my app reported a crash which logged by the Google Developer Console:
java.lang.NoSuchMethodError: No static method canDrawOverlays(Landroid/content/Context;)Z in class Landroid/provider/Settings; or its super classes (declaration of 'android.provider.Settings' appears in /system/framework/framework.jar)
at com.pack.MainActivity.checkDrawOverlayPermission(MainActivity.java:311)
at com.pack.MainActivity.onCreate(MainActivity.java:127)
at android.app.Activity.performCreate(Activity.java:6033)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2288)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2397)
at android.app.ActivityThread.access$800(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1310)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5268)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:902)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:697)
The canDrawOverlays is an API 23+ method and I Use it like that:
/** code to post/handler request for permission */
public final static int REQUEST_CODE = 100; /*(see edit II)*/
#SuppressLint("NewApi")
public void checkDrawOverlayPermission() {
/** check if we already have permission to draw over other apps */
if (!Settings.canDrawOverlays(this)) {
/** if not construct intent to request permission */
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
/** request permission via start activity for result */
startActivityForResult(intent, REQUEST_CODE);
}
}
And I have this in my MainActivity:
checkDrawOverlayPermission();
The device which crashed using Android 5.1
HOw I can make sure my app will work on ANdroid 5.1? (API 22 and below) who don't have this method which available from API 23 and up?
Check the current API of the device which runs your code. If it >= 23, you can use the code
if(Build.VERSION.SDK_INT >= 23) {
// if (!Settings.canDrawOverlays(this)) {
//
} else {
// another similar method that supports device have API < 23
}
Use this
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
/** request permission via start activity for result */
startActivityForResult(intent, REQUEST_CODE);
}
} else {
}
For all methods that appeared in newer SDK versions you should use the following pattern:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// code for Marshmallow (SDK 23) and newer versions
} else {
// fallback for older versions
}
In my developer console people keep reporting an error that I cannot reproduce on any phone I have. One person left a message saying he gets it when they try to open the settings screen of my battery service. As you can see from the error it says that the receiver is not registered.
java.lang.RuntimeException: Unable to stop service .BatteryService#4616d688: java.lang.IllegalArgumentException: Receiver not registered: com.app.notifyme.BatteryService$BatteryNotifyReceiver#4616d9d0
at android.app.ActivityThread.handleStopService(ActivityThread.java:3164)
at android.app.ActivityThread.access$3900(ActivityThread.java:129)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2173)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:143)
at android.app.ActivityThread.main(ActivityThread.java:4701)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalArgumentException: Receiver not registered:com..BatteryService$BatteryNotifyReceiver#4616d9d0
at android.app.ActivityThread$PackageInfo.forgetReceiverDispatcher(ActivityThread.java:805)
at android.app.ContextImpl.unregisterReceiver(ContextImpl.java:859)
at android.content.ContextWrapper.unregisterReceiver(ContextWrapper.java:331)
at com.app.notifyme.BatteryService.onDestroy(BatteryService.java:128)
at android.app.ActivityThread.handleStopService(ActivityThread.java:3150)
I register is in my onCreate
#Override
public void onCreate(){
super.onCreate();
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(Intent.ACTION_POWER_CONNECTED);
filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
registerReceiver(batteryNotifyReceiver,filter);
pref.registerOnSharedPreferenceChangeListener(this);
}
Unregister in onDestroy and also with a preference listener
#Override
public void onDestroy(){
super.onDestroy();
unregisterReceiver(batteryNotifyReceiver);
}
and this is my receiver in the service
private final class BatteryNotifyReceiver extends BroadcastReceiver {
boolean connected;
#Override
public void onReceive(Context context, Intent intent) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor edit = prefs.edit();
updatePreferences(prefs);
level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
if(intent.getAction().equals(Intent.ACTION_POWER_CONNECTED)){
connected = true;
}else if(intent.getAction().equals(Intent.ACTION_POWER_DISCONNECTED)){
connected = false;
}else if(intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)){
if(level < lastLevel){
if(level > 40){
edit.putBoolean("first", false).commit();
edit.putBoolean("second", false).commit();
edit.putBoolean("third", false).commit();
edit.putBoolean("fourth",false).commit();
edit.putBoolean("fifth", false).commit();
}
if(level == 40){
if(!first){
notification(context,battColor,battBlink,battVib,battSound);
edit.putBoolean("first", true).commit();
}
}else if(level == 30){
if(!second){
notification(context,battColor,battBlink,battVib,battSound);
edit.putBoolean("second", true).commit();
}
}else if(level == 20){
if(!third){
notification(context,battColor,battBlink,battVib,battSound);
edit.putBoolean("third", true).commit();
}
}else if(level == 15){
if(!fourth){
notification(context,battColor,battBlink,battVib,battSound);
edit.putBoolean("fourth", true).commit();
}
}else if(level == 5){
if(!fifth){
notification(context,battColor,battBlink,battVib,battSound);
edit.putBoolean("fifth", true).commit();
}
}
lastLevel = temp;
}
}
Intent i = new Intent(context,BatteryNotifyReceiver.class);
context.startService(i);
}
}
any idea why they would be getting that error?
The root of your problem is located here:
unregisterReceiver(batteryNotifyReceiver);
If the receiver was already unregistered (probably in the code that you didn't include in this post) or was not registered, then call to unregisterReceiver throws IllegalArgumentException. In your case you need to just put special try/catch for this exception and ignore it (assuming you can't or don't want to control number of times you call unregisterReceiver on the same recevier).
Be careful, when you register by
LocalBroadcastManager.getInstance(this).registerReceiver()
you can't unregister by
unregisterReceiver()
you must use
LocalBroadcastManager.getInstance(this).unregisterReceiver()
or app will crash, log as follow:
09-30 14:00:55.458 19064-19064/com.jialan.guangdian.view E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.jialan.guangdian.view, PID: 19064
java.lang.RuntimeException: Unable to stop service com.google.android.exoplayer.demo.player.PlayService#141ba331: java.lang.IllegalArgumentException: Receiver not registered: com.google.android.exoplayer.demo.player.PlayService$PlayStatusReceiver#19538584
at android.app.ActivityThread.handleStopService(ActivityThread.java:2941)
at android.app.ActivityThread.access$2200(ActivityThread.java:148)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1395)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5310)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:901)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:696)
Caused by: java.lang.IllegalArgumentException: Receiver not registered: com.google.android.exoplayer.demo.player.PlayService$PlayStatusReceiver#19538584
at android.app.LoadedApk.forgetReceiverDispatcher(LoadedApk.java:769)
at android.app.ContextImpl.unregisterReceiver(ContextImpl.java:1794)
at android.content.ContextWrapper.unregisterReceiver(ContextWrapper.java:510)
at com.google.android.exoplayer.demo.player.PlayService.onDestroy(PlayService.java:542)
at android.app.ActivityThread.handleStopService(ActivityThread.java:2924)
at android.app.ActivityThread.access$2200(ActivityThread.java:148)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1395)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5310)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:901)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:696)
Use this code everywhere for unregisterReceiver:
if (batteryNotifyReceiver != null) {
unregisterReceiver(batteryNotifyReceiver);
batteryNotifyReceiver = null;
}
As mentioned in other answers, the exception is being thrown because each call to registerReceiver is not being matched by exactly one call to unregisterReceiver. Why not?
An Activity does not always have a matching onDestroy call for every onCreate call. If the system runs out of memory, your app is evicted without calling onDestroy.
The correct place to put a registerReceiver call is in the onResume call, and unregisterReceiver in onPause. This pair of calls is always matched. See the Activity lifecycle diagram for more details.
http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle
Your code would change to:
SharedPreferences mPref
IntentFilter mFilter;
#Override
public void onCreate(){
super.onCreate();
mPref = PreferenceManager.getDefaultSharedPreferences(this);
mFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(Intent.ACTION_POWER_CONNECTED);
filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
}
#Override
public void onResume() {
registerReceiver(batteryNotifyReceiver,mFilter);
mPref.registerOnSharedPreferenceChangeListener(this);
}
#Override
public void onPause(){
unregisterReceiver(batteryNotifyReceiver, mFilter);
mPref.unregisterOnSharedPreferenceChangeListener(this);
}
EDIT: This is the answer for inazaruk and electrichead... I had run into a similar issue to them and found out the following...
There is a long-standing bug for this problem here: http://code.google.com/p/android/issues/detail?id=6191
Looks like it started around Android 2.1 and has been present in all of the Android 2.x releases since. I'm not sure if it is still a problem in Android 3.x or 4.x though.
Anyway, this StackOverflow post explains how to workaround the problem correctly (it doesn't look relevant by the URL but I promise it is)
Why does keyboard-slide crash my app?
I used a try - catch block to solve the issue temporarily.
// Unregister Observer - Stop monitoring the underlying data source.
if (mDataSetChangeObserver != null) {
// Sometimes the Fragment onDestroy() unregisters the observer before calling below code
// See <a>http://stackoverflow.com/questions/6165070/receiver-not-registered-exception-error</a>
try {
getContext().unregisterReceiver(mDataSetChangeObserver);
mDataSetChangeObserver = null;
}
catch (IllegalArgumentException e) {
// Check wether we are in debug mode
if (BuildConfig.IS_DEBUG_MODE) {
e.printStackTrace();
}
}
}
Declare receiver as null and then Put register and unregister methods in onResume() and onPause() of the activity respectively.
#Override
protected void onResume() {
super.onResume();
if (receiver == null) {
filter = new IntentFilter(ResponseReceiver.ACTION_RESP);
filter.addCategory(Intent.CATEGORY_DEFAULT);
receiver = new ResponseReceiver();
registerReceiver(receiver, filter);
}
}
#Override
protected void onPause() {
super.onPause();
if (receiver != null) {
unregisterReceiver(receiver);
receiver = null;
}
}
When the UI component that registers the BR is destroyed, so is the BR. Therefore when the code gets to unregistering, the BR may have already been destroyed.
For anybody who will come upon this problem and they tried all that was suggested and nothing still works, this is how I sorted my problem, instead of doing LocalBroadcastManager.getInstance(this).registerReceiver(...)
I first created a local variable of type LocalBroadcastManager,
private LocalBroadcastManager lbman;
And used this variable to carry out the registering and unregistering on the broadcastreceiver, that is
lbman.registerReceiver(bReceiver);
and
lbman.unregisterReceiver(bReceiver);
Lets assume your broadcastReceiver is defined like this:
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// your code
}
};
If you are using LocalBroadcast in an Activity, then this is how you'll unregister:
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
If you are using LocalBroadcast in a Fragment, then this is how you'll unregister:
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(broadcastReceiver);
If you are using normal broadcast in an Activity, then this is how you'll unregister:
unregisterReceiver(broadcastReceiver);
If you are using normal broadcast in a Fragment, then this is how you'll unregister:
getActivity().unregisterReceiver(broadcastReceiver);
Unregister broadcast receiver in Try Catch
try {
unregisterReceiver(receiver);
} catch (IllegalArgumentException e) {
System.out.printf(e.getMessage());
}