Rarely I have an issue where a phone will get in a state (known as "funny state") where my Intent Services won't get a startService command from a Broadcast Receiver. (yes, the manifest has the receivers and services defined).
In this example I am listening for push notifications then calling a CheckinService.
Receiver:
public class PushReceiver extends BroadcastReceiver {
private static final String LOG_TAG = "push_receiver";
#Override
public void onReceive(Context context, Intent intent) {
logger.putExtra("debug", "Received Push");
Intent serviceIntent = new Intent(context, CheckinService.class);
context.startService(serviceIntent);
logger.putExtra("debug", "Sent to Service");
}
Service:
public class CheckinService extends IntentService {
private static final String LOG_TAG = "checkin_service";
public static final int SERVICE_ID = 3;
public CheckinService() {
super(LOG_TAG);
Log.i(LOG_TAG, "Service says: Checkin service started no constructor");
}
public CheckinService(String name) {
super(LOG_TAG);
Log.i(LOG_TAG, "Service says: Checkin service started with constructor");
}
#Override
protected void onHandleIntent(Intent intent) {
Log.i(LOG_TAG, "Auto Checkin started");
.....tons of genius logic.....
}
}
So when the phone gets in the funny state the "received push" gets logged and the "sent to service" gets logged but the constructors and onHandleIntent methods of the service never get called.
I also have this happen not only on pushes but on receivers for inexactRepeatingAlarm and perhaps others but these two have been confirmed for sure.
Again this is very, very, rare and seems to happen after the phone has been left unused for a period of time; and perhaps goes into a power saving mode.
Also, terminating the application's process clears this up.
I realized what was happening here.
The IntentService is single threaded. So if something in my " .....tons of genius logic....." was blocking (like a http request with no timeout) the next intent that came into the service would not be processed.
Nice and humbling.
Related
I am having an issue getting my ActivityRecognition Service to remain running. I currently have a service (GService) that runs continuously in the background. I want to start the ActivityRecognition service within GService, and have the ActivityRecognition service broadcast the activity result back to GService. I am able to start the service and receive feedback that it is running, and I also get one result from the intent handler (no actual data), but never again.
Here is the section of code from my continuous service setting up the intent, pending intent:
#Override
public void onConnected(Bundle bundle) {
Log.d(TAG, "onConnected - isConnected ...............: " + mGoogleApiClient.isConnected());
startLocationUpdates();
//start process to receive activity updates
Intent intent = new Intent(this, DetectedActivitiesIntentService.class);
PendingIntent mActivityRecognitionPendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(mGoogleApiClient, ActivityConstants.DETECTION_INTERVAL_MILLISECONDS_MOVING,
mActivityRecognitionPendingIntent).setResultCallback(this);
startService(intent); // this should start the DetectedActivitiesIntentService
This is the Broadcast receiver within GService:
public class ActivityDetectionBroadcastReceiver extends BroadcastReceiver {
protected static final String TAG_AR = "ADRR";
#Override
public void onReceive(Context context, Intent intent){
//ArrayList<DetectedActivity> updatedActivities =
// intent.getParcelableArrayListExtra(ActivityConstants.ACTIVITY_EXTRA);
//updateDetectedActivitiesList(updatedActivities);
String action = intent.getAction();
if(action.equals("com.gt.useractivity"))
{
Log.d(TAG_AR, "received broadcast from Activity service");
// below line should grab the resulting string activity from the intent and log it.
Log.d(TAG_AR, "activity is : " + intent.getExtras().getString(ActivityConstants.ACTIVITY_EXTRA));
}
}
}
Here is the ActivityRecognition Service code:
public class DetectedActivitiesIntentService extends IntentService {
protected static final String TAG = "ADIS";
/**
* This constructor is required, and calls the super IntentService(String)
* constructor with the name for a worker thread.
*/
public DetectedActivitiesIntentService() {
// Use the TAG to name the worker thread.
super(TAG);
Log.d(TAG, "Activity service started....");
}
#Override
public void onCreate() {
super.onCreate();
}
/**
* Handles incoming intents.
* #param intent The Intent is provided (inside a PendingIntent) when requestActivityUpdates()
* is called.
*/
#Override
protected void onHandleIntent(Intent intent) {
if(ActivityRecognitionResult.hasResult(intent))
{
ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);
Intent localIntent = new Intent(ActivityConstants.BROADCAST_ACTION);
// Get the list of the probable activities associated with the current state of the
// device. Each activity is associated with a confidence level, which is an int between
// 0 and 100.
ArrayList<DetectedActivity> detectedActivities = (ArrayList) result.getProbableActivities();
// Log each activity.
Log.i(TAG, "activities detected");
for (DetectedActivity da: detectedActivities) {
Log.i(TAG, ActivityConstants.getActivityString(da.getType()) + " " + da.getConfidence() + "%");
}
String activity = result.getMostProbableActivity().toString(); // get the activity and convert to string
// Broadcast the list of detected activities.
//localIntent.putExtra(ActivityConstants.ACTIVITY_EXTRA, detectedActivities);
//localIntent.setAction("com.gt.useractivity");
localIntent.putExtra(ActivityConstants.ACTIVITY_EXTRA, activity); // set the activity string to be transmitted
LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
}
else{
Log.d(TAG, "Intent had no activity data....");
}
}
}
This Activity recognition sample is based from the Google Github sample.
All the examples I have found when using the PendingIntent is being called from a main activity, not from a service. I'm obviously doing something incorrect, but I can't figure it out. Any advice would be appreciated. I should also note that I have 2 broadcast receivers within my GService. I don't know if this would cause an issue or not.
It looks like I have solved the problem. I have a second intent within my GService used for broadcasting. From what I can tell from this thread (Pending intent works correctly for first notification but not for the rest) if there are multiple intents being used, they have to be unique. Thus, I added one line of code when declaring my intent intent.setAction(Long.toString(System.currentTimeMillis())); which is enough to differentiate it from the other intent to the system. Once I did that, I began to receive the Activity broadcasts from the intent service, as well as still receiving the location requests from within the GService routine.
So, I have this problem.
I'm using a dependency project that is some kind of GCM notification parser. It's a bit poorly written, however I'm forced to use it, becase of work related reasons. Anyways:
The main service (that extends IntentService) is launched with WakefulBroadcastReceiver.
After it receives message from GCM I does some magic and sends it to the main App using broadcast.
In main app I'm constantly running service with another BroadcastReceiver that catches messages and saves everything in database etc.
Why is it so complicated? Firstly - originally it was someone else's project and now I'm trying to fix bugs. Secondly - I have no access from dependency to the main application project so I pass messages with broadcasts.
And now, the fun part. I need to filter whether I want to show notification or not. While sending a message to my main AppService I check it with the history of previous messages and then I decide if I need to show this message to User or not. However, no matter what my decision is, my dependency still shows my notification.
So I added yet another broadcast, when after successful validation I launch in my dependency notification building method.
Here is the code:
My WakefulBroadcastReceiver:
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
ComponentName comp = new ComponentName(context.getPackageName(), PushService.class.getName());
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
Here is my Depencency service
public NotificationCheckerReceiver notificationCheckerReceiver;
...
#Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
String messageType = gcm.getMessageType(intent);
if (!extras.isEmpty()) {
if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
//Launch my "approval" receiving broadcast
launchBroadcastReceiver(extras, intent);
//send broadcast to main app with the message we will parse etc.
sendSmsBroadcast(...));
}
}
}
#Override
public void onDestroy() {
unregisterReceiver(notificationCheckerReceiver);
super.onDestroy();
}
//Launch to build notification
public void showNotification(Bundle extras){
...
//Basic notification builder
}
//Receive broadcast from DB if notification was already in the DB
private void launchBroadcastReceiver(Bundle extras, Intent intent){
Log.d(TAG, "Broadcast receiver loaded");
notificationCheckerReceiver = new NotificationCheckerReceiver(new NotiFlag() {
#Override
public void onReceiveApproval(Boolean flag, Intent intent, Bundle extras) {
Log.d(TAG, "Approved notification show");
showNotification(extras);
JustPushGcmBroadcastReceiver.completeWakefulIntent(intent);
}
}, intent, extras);
registerReceiver(notificationCheckerReceiver, new IntentFilter(notificationCheckerReceiver.INTENT_EVENT_NAME));
}
public void sendSmsBroadcast(String message, boolean isAppOnScreen){
...
//This works
}
}
and my "faulty" receiver:
public class NotificationCheckerReceiver extends BroadcastReceiver{
private final String TAG = getClass().getSimpleName();
public static final String INTENT_EVENT_NAME = "NOTIFLAG";
public static final String INTENT_FLAG_KEY = "FLAG";
Intent intent;
Bundle extras;
NotiFlag nofiFlag;
public NotificationCheckerReceiver(NotiFlag nofiFlag, Intent intent, Bundle extras){
Log.d(TAG, "Launched constructor NotificationChecker");
this.nofiFlag = nofiFlag;
this.intent = intent;
this.extras = extras;
}
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Launched onReceive");
Boolean bool = intent.getExtras().getBoolean(INTENT_FLAG_KEY);
Log.d(TAG, "___________Broadcast receiver got something and it is intent: "+bool);
if (bool != false) {
nofiFlag.onReceiveApproval(bool, this.intent, this.extras);
}
}
}
and lastly, what I'm sending from my main service:
public void sendNotificationCheckerBroadcast(Boolean message){
Intent flag = new Intent(NotificationCheckerReceiver.INTENT_EVENT_NAME);
flag.putExtra(NotificationCheckerReceiver.INTENT_FLAG_KEY, message);
DvLogs.d(TAG, "__________Sending intent: "+message);
sendBroadcast(flag);
}
What happens is that eveything to the point where I launch "sendNotificationCheckerBroadcast()". I get that I'm sending some kind of boolean... and that's it.
The funny part is: it SOMETIMES works.
I don't know why, but when for some reason it launches - everything is awesome.
EDIT:
When it works, because sometimes it does, I have this error:
01-15 11:20:22.204 3234-3234/pl.digitalvirgo.lafarge E/ActivityThread﹕ Service com.example.name.PushService has leaked IntentReceiver com.example.name.NotificationCheckerReceiver#43042b50 that was originally registered here. Are you missing a call to unregisterReceiver()?
android.app.IntentReceiverLeaked: Service com.example.name.PushService has leaked IntentReceiver com.example.name.NotificationCheckerReceiver#43042b50 that was originally registered here. Are you missing a call to unregisterReceiver()?
at android.app.LoadedApk$ReceiverDispatcher.<init>(LoadedApk.java:814)
at android.app.LoadedApk.getReceiverDispatcher(LoadedApk.java:610)
at android.app.ContextImpl.registerReceiverInternal(ContextImpl.java:1772)
at android.app.ContextImpl.registerReceiver(ContextImpl.java:1752)
at android.app.ContextImpl.registerReceiver(ContextImpl.java:1746)
at android.content.ContextWrapper.registerReceiver(ContextWrapper.java:479)
at com.example.name.PushService.launchBroadcastReceiver(Unknown Source)
at com.example.name.PushService.onHandleIntent(Unknown Source)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:157)
at android.os.HandlerThread.run(HandlerThread.java:61)
Maybe it's somehow related?
I know that I should unRegister this Receiver ... somewhere. Tried onStop, but as we can see - no success.
Edit2:
Weird.
I believe, that the problem is in onStop() method. Probably it's called too early (?) so my Receiver has no chance to work. When I launch app without unRegister everything works. Of course I get bug above, but still... it's something.
Any ideas guys?
Well. The problem was inside the idea of IntentService.
intentService kills itself after onHandleIntent() method.
So the solution for this problem is to change IntentService into Service remembering to handle stopping this thing.
I am having problem with my android IntentService. When I first open the application, the service gets started by intent from the profile activity and data is fetched from this service. If I switch to other activity and then back service is still running and that is ok.
However if you press back, so that activity is finished and put in the background, the service is still working as the application is in background but If I get it back to foreground service stops. I do not know why. Bellow is my code, please help.
I have read activity life cycle couple of times and still do not get it why this is happening.
What is weird is that Service receive data one more time before it stops when MainActivity is brought back to running state. Service is not crashing.
Service
public class SomeService extends IntentService
{
public static final String extra = "someData";
public SomeService()
{
super(SomeService.class.getSimpleName());
}
#Override
protected void onHandleIntent(Intent intent)
{
Log.e("SomeService", "starting service");
while (true)
{
SomeData data = Api.getNewSocketData();
//Broadcast data when received to update the view
Intent broadcastData = new Intent();
broadcastData.setAction(dataBroadcastReceiver.ACTION_DATA_RECEIVED);
broadcastData.addCategory(Intent.CATEGORY_DEFAULT);
broadcastData.putExtra(extra, " ");
sendBroadcast(broadcastData);
Log.e("SomeService", "received from socket");
}
}
}
Receiver
public class dataBroadcastReceiver extends BroadcastReceiver
{
public final static String ACTION_DATA_RECEIVED = "net.bitstamp.intent.action.ACTION_SOMEDATA_RECEIVED";
#Override
public void onReceive(Context context, Intent intent)
{
Log.e("receiver", "data received");
}
}
Main Activity
#Override
public void onPause()
{
super.onPause();
unregisterReceiver(dataBroadcastReceiver);
}
#Override
public void onResume()
{
super.onResume();
IntentFilter intentFilter = new IntentFilter(dataBroadcastReceiver.ACTION_DATA_RECEIVED);
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
dataBroadcastReceiver = new dataBroadcastReceiver();
registerReceiver(dataBroadcastReceiver, intentFilter);
Intent someService = new Intent(this, SomeService.class);
startService(someService);
}
I really need help on this. Thanks
You don't want to the up the IntentService in an infinite loop. It will block all other incoming requests. From the documentation:
All requests are handled on a single worker thread -- they may take as long as necessary (and will not block the application's main loop), but only one request will be processed at a time.
Your Service is likely still happily running along, it just isn't processing your new request because your old one is still being handled in the infinite loop.
i have created one service by extending Service in android and i am sending Message to service using Messenger and Handler.
But the issue (which is a common behavior though) is whenever i have to send message to Service i have to bind it and when i go out of activity i have to unbind it which eventually destroys the service itself.
i can keep running service in background by fringing startService method but is there any way to send Messages to service without using bind as i don't want to destroy the service when i go out of activity.
LocalBroadcastManager is a great way to send messages/data,
In your service class create a private broadcastreciever and string for the intent action name:
public static String MSERVICEBROADCASTRECEIVERACTION ="whatevs";
private BroadcastReceiver mServiceBroadcastReceiver= new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("foo","onReceive called");
Log.d("foo","extra = " + intent.getStringExtra("foo")); // should print out " extra = bar"
}
};
And register it in your onCreate
#Override
public void onCreate() {
// your other code...
LocalBroadcastManager.getInstance(this).registerReceiver(mServiceBroadcastReceiver, new IntentFilter(ServiceClassName.MSERVICEBROADCASTRECEIVERACTION));
}
And De-register it in onDestroy()
#Override
public void onDestroy() {
// your other code...
LocalBroadcastManager.getInstance(this).unregisterReceiver(mServiceBroadcastReceiver);
}
As for sending messages to it, from an activity or fragment:
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
Intent intent = new Intent(ServiceClassName.MSERVICEBROADCASTRECEIVERACTION);
// add some data
intent.putExtra("foo","bar");
lbm.sendBroadcast(intent);
HTHs you send data without needing to bind!
Unbind Service will not destroy the service. it will disconnect the service connection between the activity and service
Make sure you return START_STICKY to keep your service running
#Override
public int onStartCommand(Intent intent, int flag, int startId)
{
return START_STICKY;
}
Also make sure your running the service as foreground
using notification to keep running the service after the application is removed from stack.
startForeground(1000,mBuilder.build()); // mBuilder - notification builder
I stop a service from various places. How can I check when the sender of the command "stopService(Intent intent)" was my NetworkReceivar class (extends BroadcastReceiver)??
This is my code for do this more clear:
public class NetworkReceiver extends BroadcastReceiver{
public void onReceive(Context context, Intent intent) {
boolean isNetworkDown = intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
if (isNetworkDown) {
context.stopService(new Intent(context, MyService.class));
}
My service class:
public void onDestroy() {
Log.i(TAG, "OnDestroy");
// if came from NetworkReceiver do something.
this.updater.interrupt();
this.updater = null;
super.onDestroy();
}
How can I check when the sender of the command "stopService(Intent intent)" was my NetworkReceivar class (extends BroadcastReceiver)??
AFAIK, you can't.
However, instead of stopService(), you could call startService(), sending over a command that your service interprets as "time to shut down". Something on that Intent that is your command could be used to indicate the sender, so that you can distinguish one sender from another. And, the service can call stopSelf() to shut itself down.