Bringing a service in android into the foreground
I have an android service where I don't want that the phone disables the services automatically. I therefore try to put a notification into the notifcation bar. When I try to do so my HTC Hero reboots.
I use the following code, what's wrong? It's partly copied from the reference. Is there a good tutorial that explains how this API works?
private static final Class<?>[] mSetForegroundSignature = new Class[] {
boolean.class};
private static final Class<?>[] mStartForegroundSignature = new Class[] {
int.class, Notification.class};
private static final Class<?>[] mStopForegroundSignature = new Class[] {
boolean.class};
private NotificationManager mNM;
private Method mSetForeground;
private Method mStartForeground;
pr开发者_Go百科ivate Method mStopForeground;
private Object[] mSetForegroundArgs = new Object[1];
private Object[] mStartForegroundArgs = new Object[2];
private Object[] mStopForegroundArgs = new Object[1];
void invokeMethod(Method method, Object[] args) {
try {
mStartForeground.invoke(this, mStartForegroundArgs);
} catch (InvocationTargetException e) {
// Should not happen.
Log.w("ApiDemos", "Unable to invoke method", e);
} catch (IllegalAccessException e) {
// Should not happen.
Log.w("ApiDemos", "Unable to invoke method", e);
}
}
/**
* This is a wrapper around the new startForeground method, using the older
* APIs if it is not available.
*/
void startForegroundCompat(int id, Notification notification) {
// If we have the new startForeground API, then use it.
if (mStartForeground != null) {
mStartForegroundArgs[0] = Integer.valueOf(id);
mStartForegroundArgs[1] = notification;
invokeMethod(mStartForeground, mStartForegroundArgs);
return;
}
// Fall back on the old API.
mSetForegroundArgs[0] = Boolean.TRUE;
invokeMethod(mSetForeground, mSetForegroundArgs);
mNM.notify(id, notification);
}
/**
* This is a wrapper around the new stopForeground method, using the older
* APIs if it is not available.
*/
void stopForegroundCompat(int id) {
// If we have the new stopForeground API, then use it.
if (mStopForeground != null) {
mStopForegroundArgs[0] = Boolean.TRUE;
try {
mStopForeground.invoke(this, mStopForegroundArgs);
} catch (InvocationTargetException e) {
// Should not happen.
Log.w("ApiDemos", "Unable to invoke stopForeground", e);
} catch (IllegalAccessException e) {
// Should not happen.
Log.w("ApiDemos", "Unable to invoke stopForeground", e);
}
return;
}
// Fall back on the old API. Note to cancel BEFORE changing the
// foreground state, since we could be killed at that point.
mNM.cancel(id);
mSetForegroundArgs[0] = Boolean.FALSE;
invokeMethod(mSetForeground, mSetForegroundArgs);
}
private void setupNotification(){
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
try {
mStartForeground = getClass().getMethod("startForeground",
mStartForegroundSignature);
mStopForeground = getClass().getMethod("stopForeground",
mStopForegroundSignature);
} catch (NoSuchMethodException e) {
// Running on an older platform.
mStartForeground = mStopForeground = null;
return;
}
try {
mSetForeground = getClass().getMethod("setForeground",
mSetForegroundSignature);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(
"OS doesn't have Service.startForeground OR Service.setForeground!");
}
CharSequence text = getText(R.string.notification_active_text);
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.icon, text,
System.currentTimeMillis());
startForegroundCompat(
R.string.foreground_service_started,
notification);
}
Well, it should not reboot, but the code from the sample has at least 2 major bugs and will not work as expected. I've tested it with Android 1.6 emulator and it worked for me only after the following changes:
- invokeMethod() is obviously wrong because it ignores the passed arguments and makes a fixed method call instead. IntelliJ IDEA spots this problem easily and the fix would be changing
mStartForeground.invoke(this, mStartForegroundArgs);
tomethod.invoke(this, args);
- onCreate() in the original sample will not initialize mSetForeground on 1.6 since return statement is in the first catch, while it must be in the first try instead. You'll get an NPE when trying to start the service. Removing return after
mStartForeground = mStopForeground = null;
and placing it aftermStopForeground = getClass().getMethod("stopForeground", mStopForegroundSignature);
will fix this problem.
UPDATE: Looks like Google has already fixed the sample and it should work fine out of the box.
google didn't fix the issue . what worked for me is putting this in the beginning instead of what they use:
private static final Class<?>[] mSetForegroundSignature = new Class[] {Boolean.TYPE};
private static final Class<?>[] mStartForegroundSignature = new Class[] {Integer.TYPE, Notification.class};
private static final Class<?>[] mStopForegroundSignature = new Class[] {Boolean.TYPE};
精彩评论