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.
Related
I've trying all day to figure out a way to remove a Twilio CallbackListener from a Twilio channel when requesting unconsumed message count to avoid leaking memory when my activity is destroyed. Calling channel.removeAllListeners seems to only remove ChannelListener from the channel. I've posted some code below of what I'm attempting to do and here is a link to the Twilio chat docs. Any ideas?
CallbackListener<Long> callbackListener = new CallbackListener<Long>() {
#Override
public void onSuccess(Long unconsumedCount) {
}
#Override
public void onError(ErrorInfo errorInfo) {
super.onError(errorInfo);
}
};
channel.getUnconsumedMessagesCount(callbackListener);
Twilio developer evangelist here.
channel.getUnconsumedMessagesCount only sets up the callbackListener to be called once when the result is returned. It doesn't permanently set it up as a listener, so you are not leaking memory, and if you need to get the unconsumed message count again you need to call it again. Note that the value is cached for 5 seconds, so there is no need to call it any more often than that.
Let me know if that helps at all.
Background
We use Crashlytics SDK to manage app crashes and get needed information about them.
So far, the information that the SDK automatically gathered was enough
The problem
I'd like to add more information for each crash, such as: available&total heap memory, activity stack,...
Thing is, I don't see a way to achieve this.
I know that the way Android framework works with unhandled exceptions is pretty easy (using Thread.setDefaultUncaughtExceptionHandler) and it's probably how the SDK works, but I can't find where to use the listener of the SDK itself.
What I've tried
The SDK has a listener, but it seems it's not of the current session, as shown here. The function name is "crashlyticsDidDetectCrashDuringPreviousExecution" , meaning it's of the previous session. Same callback was available before in deprecated methods.
There are "Custom Logging" and "Custom Keys" features, but those occur when I call them (not right when the crash occurs).
The question
Is there a way to add extra information to Crashlytics right when a crash occurs ?
If so, how?
Try creating an UncaughtExceptionHandler and use Custom Key(s) to store the information you want to be associated with your crash report.
Create your custom UncaughtExceptionHandler (ensuring that it will pass exception to default UncaughtExceptionHandler to be handled later via Crashlytics).
In the uncaughtException method add custom logic to set your key e.g. Crashlytics.setString("available_memory", "5784");
Check your Crashlytics dashboard to view your custom key(s) when your app crashes
Create a custom Application subclass to hold your logic:
public class MyApplication extends Application {
private static Thread.UncaughtExceptionHandler mDefaultUncaughtExceptionHandler;
private static Thread.UncaughtExceptionHandler mCaughtExceptionHandler = new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread thread, Throwable ex) {
// Custom logic goes here
// Calculate available memory
Crashlytics.setString("available_memory", "5784");
// This will make Crashlytics do its job
mDefaultUncaughtExceptionHandler.uncaughtException(thread, ex);
}
};
#Override
public void onCreate() {
super.onCreate();
// Order is important!
// First, start Crashlytics
Crashlytics.start(this);
// Second, cache a reference to default uncaught exception handler
mDefaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
// Third, set custom UncaughtExceptionHandler
Thread.setDefaultUncaughtExceptionHandler(mCaughtExceptionHandler);
}
}
Remember to specify the name of your Application subclass in your AndroidManifest.xml’s tag
<application android:name="MyApplication">
I'm writing a simple app to send public photos from Dropbox public folder to Chromecast.
Instead of CastCompanion library I decided to write my own stuff to understand better the API.
According to Google Guidelines:
if the sender application becomes disconnected from the media route, such as when the user or the operating system kills the application without the user first disconnecting from the Cast device, then the application must restore the session with the receiver when the sender application starts again.
It seems to me that the same solution should apply to Activity recreation upon orientation change since it recreates the Activity from scratch.
My first question: Is my assumption correct? Both scenarios, orientation change and system kill, may use the same solution?
Given this assumption I wrote some code to restore session upon Activity restoration.
I'm considering the orientation change scenario, when Activity is recreated from scratch and I am supposed to restore route Id, Session Id and try to reconnect (I'm storing and retrieving both values from shared preferences).
I've been testing with and it's working fine.
That's what I do (based on Google Sender Guidelines code):
After discovering the ongoing Route Id and find the cast device I call this method:
private void connectToDevice(CastDevice castDevice) {
Log.d(TAG, "connecting to " + castDevice);
Cast.CastOptions.Builder apiOptionsBuilder = Cast.CastOptions
.builder(castDevice, new CastListener());
Log.d(TAG, "apiClient is null ? " + (apiClient == null));
apiClient = new GoogleApiClient.Builder(this)
.addApi(Cast.API, apiOptionsBuilder.build())
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
Log.d(TAG, "apiClient connected? " + apiClient.isConnected());
Log.d(TAG, "apiClient connecting? " + apiClient.isConnecting());
apiClient.connect();
}
private class CastListener extends Cast.Listener {
#Override
public void onApplicationStatusChanged() {
if (apiClient != null) {
Log.d(TAG, "callback => " + this);
}
}
#Override
public void onVolumeChanged() {
}
#Override
public void onApplicationDisconnected(int errorCode) {
teardown();
}
}
After this method I call Cast.CastApi.joinApplication if I recognize a reconnection.
But once reconnected to Chromecast the log of onApplicationStatusChanged prints one different instance for every phone's rotation. E.g: if I rotate phone 3 times the log prints 3 times with 3 different pointer addresses. That makes me believe it is internally holding all callbacks instances.
How am I supposed to handle this situation since the Activity is being recreated and I need to create another instance of GoogleApiClient keeping the session?
Full source:
https://github.com/ivan-aguirre/chromecast_samples/blob/master/DropboxCast/app/src/main/java/com/dropboxcast/dropboxcast/MainActivity.java
IMHO, I believe the proper way (or at least a better way) to approach this is one of the following:
if you have only one activity and that is all you care about, then use a fragment that persists across configuration changes and put the stuff that you want to persist seamlessly, there. This way, rotation of the phone is not going to cause any disruption in your cast related stuff.
if you have more than a single activity, think about creating an object that lasts across all your activities and put the cast stuff there and then ask that object for the instance of CastApi whenever needed, etc.
In your case, do you really get disconnected when you rotate the phone? Since you are setting up a whole new connection, you might want to disconnect yourself first when configuration changes (assuming you don't want to go with my earlier proposed (1) or (2)).
Just a quick background I'm Running CM7 on a rooted Nexus one.
I am trying to detect when an outgoing call is actually connected: has stopped ringing and the person you are calling has answered. Looking through the forums this seems to be a tough and perhaps unanswered question. I'd really appreciate any insight into this.
In my searching the best I could find was in:
Android : How to get a state that the outgoing call has been answered?
#PattabiRaman said: "instead of detecting the outgoing call connection state, it is easy to get the duration of the last dialed call."
Does he mean that one should get the duration of the last dialed call as the call is in progress? And when that duration goes over 0 then you know?
The class com.android.internal.telephony.CallManager should have information about when the call actually is answered. It has a public static method getInstance() which returns the CallManager instance, and a public method getActiveFgCallState() which returns the current call state as a Call.State enum.
So in theory something like this might work:
Method getFgState = null;
Object cm = null;
try {
Class cmDesc = Class.forName("com.android.internal.telephony.CallManager");
Method getCM = cmDesc.getMethod("getInstance");
getFgState = cmDesc.getMethod("getActiveFgCallState");
cm = getCM.invoke(null);
} catch (Exception e) {
e.printStackTrace();
}
And then repeatedly poll the state:
Object state = getFgState.invoke(cm);
if (state.toString().equals("IDLE")) {
...
} else if (state.toString().equals("ACTIVE")) {
// If the previous state wasn't "ACTIVE" then the
// call has been established.
}
I haven't verified that this actually works. And even if it does you'll have to keep in mind that the API could change, since this isn't something that app developers are supposed to rely on.
I have looked into the code.
It will always give null unless you instantiate a Phone object and set it as default Phone.
But instantiating it needs some System permissions allowed only to system aps.
By using this method:
com.android.internal.telephony.PhoneFactory# public static void makeDefaultPhones(Context context) {
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.4_r1.2/com/android/internal/telephony/PhoneFactory.java
Has anybody had success integrating the Licensing Verification Library (LVL) with a Live Wallpaper? If it were just running an Activity, it'd be crystal clear to just extend my Activity from the Licensing Activity, which in turn extends Activity. But Live Wallpapers are a Service, and I'm not sure how the two are intended to interact.
I'm using code derived from this: http://www.droidforums.net/forum/android-app-developers/69899-market-license-easy-implementation-protect-your-apps.html which seems to be the code that nearly everything I can find on the web refers to.
I notice that wallpaper settings are an activity, and I have those working properly, but for some reason I can't grok the Licensing stuff...
It's actually really quite simple, you don't need to use any Activity class to implement licensing into a WallpaperService.
Make sure you've followed the directions carefully at http://developer.android.com/guide/publishing/licensing.html
Here's how I did it:
Your extended Engine class should include something similar to the following... (code not essential to your question has been removed)
class startYourEngines extends Engine {
public startYourEngines() {
super();
licenseStatus(); //custom license check method (for modularity)
//the rest of your engine would go here
}
public void onDestroy() {
super.onDestroy();
licenseChecker.onDestroy(); //we call this to close IPC connections
}
//prep work
private static final String BASE64_PUBLIC_KEY = //OMITTED//;
private LicenseCheckerCallback licenseCallback;
private LicenseChecker licenseChecker;
private byte[] salt = "rAnd0mStr!ng".getBytes();
private AESObfuscator aes;
private String deviceId;
//our custom license check method
private void licenseStatus() {
deviceId = Secure.getString(getContentResolver(), Secure.ANDROID_ID);
aes = new AESObfuscator(salt, getPackageName(), deviceId);
licenseCallback = new licenseVerification();
licenseChecker = new LicenseChecker(context, new ServerManagedPolicy(context, aes), BASE64_PUBLIC_KEY);
licenseChecker.checkAccess(licenseCallback);
}
//our callback method
private class licenseVerification implements LicenseCheckerCallback {
#Override
public void allow() {
//allow full app use
}
#Override
public void dontAllow() {
//prevent or limit app use
}
#Override
public void applicationError(ApplicationErrorCode errorCode) {
//error handling here
}
}
}
Licensing on the Android platform was created with versatility in mind. Just be sure to read through the documentation, and you shouldn't have any issues.
I have only written applications that start activities, but looking at my source code, it seems that the only reason that you would have to have an Activity do the license check is to show dialogs.
In all of the examples available on line, the LicenseCheckerCallback implementation always shows a dialog in the allow() and dontAllow() methods. Why not just show a toast in dontAllow() and exit your wallpaper service (call stopSelf(YourService.this))?
Let me know if you want more information, because I dont think you are limited to only using an activity for license checking. As an aside, make sure that you dont keep whole strings, etc in your app or in the preferences. Anyone with root access can access your preferences and if your app is decompiled, your strings are visible...
I think I've actually got it working now. I'm extending LicenseCheckActivity to my own Activity class that I'm calling in the manifest file with the usual MAIN action and LAUNCH category. I instantiate my class, do the license check, and then either allow the wallpaper to function or not based on that result (though the best way to do that is still something I need to sort out).
It almost seems too easy that I think I must be missing something. I'd appreciate anybody with experience with selling a licensed live wallpaper on the Android Market to share whatever wisdom they care to.