I have C2DM implemented in my app, I see a decent number of crash reports in the marketplace caused by a null pointer exception in the C2DMBaseReceiver class. This class is from the chrometophone project, which is referenced in the C2DM guide (http://code.google.com/android/c2dm/):
http://code.google.com/p/chrometophone/source/browse/trunk/android/c2dm/com/google/android/c2dm/C2DMBaseReceiver.java
The npe happens in onHandleIntent(), mWakeLock is sometimes null:
// From C2DMBaseReceiver.java:
#Override
public final void onHandleIntent(Intent intent) {
try {
Context context = getApplicationContext();
if (intent.getAction().equals(REGISTRATION_CALLBACK_INTENT)) {
handleRegistration(context, intent);
} else if (intent.getAction().equals(C2DM_INTENT)) {
onMessage(context, intent);
} else if (intent.getAction().equals(C2DM_RETRY)) {
C2DMessaging.register(context, senderId);
}
} finally {
// Release the power lock, so phone can get back to sleep.
// The lock is reference counted by default, so multiple
// messages are ok.
// If the onMessage() needs to spawn a thread or do something else,
// it should use it's own lock.
//
//
//
// NULL POINTER EXCEPTION REPORTS HERE
mWakeLock.release();
//
//
//
}
}
The fix is easy enough, just check for mWakeLock != null before accessing it. But I wonder if anyone else has seen this, if the docs should be updated with this check? Or maybe this is a bigger problem, if the author expected mWakeLock to always be initialized?
Thanks
I put a null check in that line, just moved the error a bit:
java.lang.RuntimeException: WakeLock under-locked C2DM_LIB
at android.os.PowerManager$WakeLock.release(PowerManager.java:304)
at android.os.PowerManager$WakeLock.release(PowerManager.java:279)
at com.google.android.c2dm.C2DMBaseReceiver.onHandleIntent(C2DMBaseReceiver.java:122)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:59)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.os.HandlerThread.run(HandlerThread.java:60)
Still no idea how to fix this.
According to Mark Murphy, who helped develop this code, there are scenarios where this error will occur, even if your code is entirely correct. See this Google Groups thread for his explanation - either the lock wasn't acquired to start with or was acquired in a different process.
If you get this exception persistently then you likely have a defect. Check (using diagnostics) to see if the same process is acquiring the wake lock as the one that is releasing it.
There is a new class in the support library, WakefulBroadcastReceiver , that helps with the hand off of a wake lock between broadcaster and receiver.
The new GCM implementation instructions specify its use: http://developer.android.com/google/gcm/client.html
Related
I'm implementing GCM into my Android app. So far so good, the messaging works, but when a message is received, the app may or may not crash with NullPointerException. The reason this happens is because a static reference is sometimes null and sometimes not, but the reason why this happens is unknown to me.
The object that may or may not be null is a MessageController located via a simple service locator pattern, which looks like this:
public class ControllerLocator
{
private static MessageController controller;
public static MessageController getMessageController()
{
return controller;
}
public static void provide(MessageController mc)
{
controller = mc;
}
}
The controller is set up in the application's onCreate() method:
#Override
public void onCreate()
{
super.onCreate();
ControllerLocator.provide(new MessageController());
}
// Later on in the program after backend authentication:
ControllerLocator.getMessageController().setCredentials(...);
The GCM message handling is like this:
#Override
protected void onHandleIntent(Intent intent)
{
Bundle extras = intent.getExtras();
String msgType = GoogleCloudMessaging.getInstance(this).getMessageType(intent);
if(msgType.equals(GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE))
{
handleMessage(extras.getString("message"));
}
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
private void handleMessage(String msg)
{
// 'controller' may or may not be null
MessageController controller = ControllerLocator.getMessageController();
}
Now, the actual scenario: Everything works fine when the app is running in the foreground. However, if I press "back" to exit close the main activity and a GCM message is received, the crash may or may not occur.
Why does the static reference get removed? How would I fix this to ensure that the controller is always there?
Its because your app process is killed and all the memory it held was released while you were waiting for that GCM message.
When your GCM message was incoming the system creates your app process and Application object over again hence you got null.
Simply check if the instance is null on your get instance method and if its null recreate your object.
EDIT:
There is no way to make sure the same object is alive always, Android system is running many apps at once and cant allow any app to fill up memory, therefore it may kill your process whenever other apps need memory and that would automatically release your reserved memory including static varaiables. Its your job as an Android developer to deal with it :)
To avoid reauth and heavy computations on recreation, you may have to rethink your design such that the state of your object is persistent, using SharedPreferences maybe.
You cannot ensure that your static Controller Object will be alive (not null),, Because an app in the background is an ideal candidate for GC.
You could however save your GCM Message data in SQLite at the point where it is received. And then check the table for data, when you open your app.
Make your MessageController variable static.
I've had in app billing v3 implemented in my app for about a week now. I used a lot of android's sample code to simplify the integration. I've been logging a crash fairly often that I can't seem to reproduce:
Exception Type: java.lang.RuntimeException
Reason: Unable to destroy activity {[package].billing.BillingActivity}: java.lang.IllegalArgumentException: Service not registered: [package].billing.util.IabHelper$1#40646a70
It seems to be breaking on this line:
if (mContext != null) mContext.unbindService(mServiceConn);
I'm binding this service in my onCreate method and disposing it in my onDestroy method (which is where this error is logged). Any pointers?
You could replace the line you mentioned:
if (mContext != null) mContext.unbindService(mServiceConn);
by this line
if (mContext != null && mService != null) mContext.unbindService(mServiceConn);
This should do the trick
I checked out the latest version of the sample project and up to today my recommendation is to currently to NOT use IabHelper. It is massively flawed.
To give you an idea:
1.) the async methods of IabHelper start a new thread. If IabHelper.dispose() is called while a thread is running you will always get various exceptions you cannot even handle.
2.) If the connection to the billing service goes down, they set it to null. But apart from that they never check if mService is null before accessing the methods. So it will always crash with NullPointerException in this case.
public void onServiceDisconnected(ComponentName name) {
logDebug("Billing service disconnected.");
mService = null;
and this is just the tip of the ice berg. Seriously I do not understand how somebody can publish this as reference code.
I just encountered the same issue but on android emulator. Billing v3 requires that Google Play app should be launched at least once and since the emulator lack of Google Play app it cannot set up helper and cannot dispose it in onDestroy().
My personal workaround is just skipping that error in try/catch:
#Override
protected void onDestroy() {
super.onDestroy();
if (bHelper != null){
try {
bHelper.dispose();
}catch (IllegalArgumentException ex){
ex.printStackTrace();
}finally{}
}
bHelper = null;
}
Add this in every onDestroy() where you dispose helper. Works fine for me.
The IabHelper class is working in a normal way.
What you need to do is:
when you call startSetup for the helper, you need to pass a callback IabHelper.OnIabSetupFinishedListener which will tell you the result of starting setup. If you get failure in the callback, the service connection with the google play services was not established.
You should handle future calls to IabHelper depending upon the result received in IabHelper.OnIabSetupFinishedListener. You can surely keep a boolean field to know what was the status.
The answer sam provided is actually a trick (in his own words). The helper classes aren't supposed to throw exceptions so that the user of those classes can implement some task in such scenarios.
And of-course, try/catch is best way if you don't want to go in details (whenever anything breaks due to exception, surely first thing which comes in mind is to put that in a try/catch block).
I have uploaded my application on google play but users have reported the following exception
java.lang.RuntimeException: WakeLock under-locked C2DM_LIB. This exception occurs when I try to release the WakeLock. Can anyone tell what could be the problem.
You didn't post your code, so I don't know if you've already done what I will suggest here,
but I also had that exception and all I added to fix it was a simple "if" to make sure the WakeLock is actually being held, before trying to release it.
All I added in my onPause was this "if" statement (before the "release()"):
if (mWakeLock.isHeld())
mWakeLock.release();
and the exception was gone.
I have traced same exception in new GCM Library too. Actually old C2DM Android library have same error, same crash, and Google hasn't fixed it yet. As I can see by our statistics, about 0.1% of users experiencing this crash.
My investigations shows that problem is in incorrect releasing of network WakeLock in GCM library, when library tries to release WakeLock that holds nothing (internal lock counter becomes negative).
I was satisfied with simple solution - just catch this exception and do nothing, because we don't need to do any extra job then our wakelock hold nothing.
In order to do this you need to import GCM library sources in your project, rather than already compiled .jar file. You can find GCM library sources under "$Android_SDK_Home$/extras/google/gcm/gcm-client/src" folder (you need to download it first using Android SDK Manager).
Next open GCMBaseIntentService class, find line
sWakeLock.release();
and surround it with try-catch.
It should look like this:
synchronized (LOCK) {
// sanity check for null as this is a public method
if (sWakeLock != null) {
Log.v(TAG, "Releasing wakelock");
try {
sWakeLock.release();
} catch (Throwable th) {
// ignoring this exception, probably wakeLock was already released
}
} else {
// should never happen during normal workflow
Log.e(TAG, "Wakelock reference is null");
}
}
UPDATE:
Alternativally, as suggested #fasti in his answer, you can use mWakeLock.isHeld() method to check if wakelock actually holding this lock.
Although the isHeld() solution seems nicer, it can actually fail - because it is not atomic (i.e. not thread safe). If you have more than one thread that might release the lock then between the check (isHeld) and the call to relase another thread may release the lock... and then you fail.
By using try/catch you disguise the bug, but in a thread-safe way.
I don't have this problem as long as I don't reinitialize the wake lock and call acquire on the new Object. You should only keep one instance of wakeLock (so make it a field variable). Then you know you're always releasing that one wakeLock.
So....
if (mWakeLock == null) {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP
| PowerManager.ON_AFTER_RELEASE, "MyWakeLock");
}
try{
mWakeLock.release();//always release before acquiring for safety just in case
}
catch(Exception e){
//probably already released
Log.e(TAG, e.getMessage());
}
mWakeLock.acquire();
My activity's onInit() contains a call to TextToSpeech.setEngineByPackageName():
tts = new TextToSpeech(this, this);
tts.setEngineByPackageName("com.ivona.tts.voicebeta.eng.usa.kendra");
It works on an Android 2.2.2 device, but on an Android 2.3.4 device it produced a NullPointerException, with the following stack trace:
E/TextToSpeech.java - setEngineByPackageName(3423): NullPointerException
W/System.err(3423): java.lang.NullPointerException
W/System.err(3423): at android.os.Parcel.readException(Parcel.java:1328)
W/System.err(3423): at android.os.Parcel.readException(Parcel.java:1276)
W/System.err(3423): at android.speech.tts.ITts$Stub$Proxy.setEngineByPackageName(ITts.java:654)
W/System.err(3423): at android.speech.tts.TextToSpeech.setEngineByPackageName(TextToSpeech.java:1356)
Since I am providing a hard-coded string parameter, I know that the parameter isn't what's causing the NullPointerException.
I also know that setEngineByPackageName() is deprecated but that's only since API 14, so this couldn't be the reason.
Any idea what could be causing this NullPointerException?
EDIT: I wouldn't have been concerned with the "why" if this didn't result in an endless bombardment of:
I/TextToSpeech.java(3652): initTts() successfully bound to service
Followed by calls to onInit() (by the system, not by my code).
My hope is that if I underderstand why this is happening, I can stop the bombardment of onInit()s and recover gracefully from the error.
Is the TTS engine you are referencing installed on the 2.3.4 device? If it is, it might be a platform bug.
EDIT:
Don't remember what results I got when I did this, but calling setEngineByPackageName() when the package doesn't exists is not a good idea. Check if it is installed and don't try to use it if it's not. Something like:
boolean isPackageInstalled(String packageName) {
PackageManager pm = context.getPackageManager();
try {
PackageInfo pi = pm.getPackageInfo(packageName, 0);
return pi != null;
} catch (NameNotFoundException e) {
return false;
}
}
Attempting to investigate this myself, in case an expert on the subject isn't around:
The NullPointerException stack trace is printed by setEngineByPackageName() itself in the catch handler for this try clause:
try {
result = mITts.setEngineByPackageName(enginePackageName);
if (result == TextToSpeech.SUCCESS){
mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = enginePackageName;
}
}
Which suggests that either of the following is null:
mITts
mCachedParams
mCachedParams is unlikely to be the null one because it is initialized in the constructor. So this leaves us with mITts:
If I examine TextToSpeech.initTts() I can easily spot 2 points in which mITts can remain null:
onServiceConnected()
onServiceDisconnected()
But why this is happening only on the 2.3.4 device? This is still a mystery.
Possible clue: The method TextToSpeech.initTts() ends with the following comment:
mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
// TODO handle case where the binding works (should always work) but
// the plugin fails
Which may explain why I receive a barrage of onInit()s ("initTts() successfully bound to service"): Binding always works, but since the package is not installed, "the plugin fails".
The question now is how to stop this endless loop...
I am getting a
NullPointerException at android.app.ActivityThread$PackageInfo$ServiceDispatcher.doConnected(ActivityThread.java:1012)
My application is not even in the stack trace, so I have no idea what is going on.
I am trying to connect to a service when it happens.
How can I fix this problem?
This probably way too old for my response to be of any use, but in case anybody else has this problem, here's what it was for me. I am using a newer version of the SDK, so I'm getting this issue at line 1061.
It was happening to me because I was passing a null ServiceConnection object to the function bindService.
It was helpful to browse the SDK code in my case - although the line numbers don't add up due to version differences, the general code is likely the same (and I knew which method to check):
1097 // If there was an old service, it is not disconnected.
1098 if (old != null) {
1099 mConnection.onServiceDisconnected(name);
1100 }
1101 // If there is a new service, it is now connected.
1102 if (service != null) {
1103 mConnection.onServiceConnected(name, service);
1104 }
mConnection was pretty much the only thing that made sense to be null.