4

I know this question has been asked before, but I've been over all of the answers I could find and still haven't been able to solve the problem.

The issue is that when by BroadcastReceiver starts the IntentService onHandleIntent() isn't called. Weirdly enough the constructor does run (as I can see by the Log output). This is my code:

NoLiSeA.class (This class contains the BroadcastReceiver that starts my service)

public void toProcess(StatusBarNotification sbn) {  
    LocalBroadcastManager.getInstance(this).registerReceiver(notificationForwarder, new IntentFilter("to_forward"));
    Intent intent = new Intent("to_forward");
    intent.putExtra("sbn", sbn);
    LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    Log.i("NoLiSe.TAG", "toProcess");
}

private BroadcastReceiver notificationForwarder = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
            Log.i("NoLiSe.TAG", "BroadCastReceiver.onReceive");
            Intent i = new Intent(context, CoreTwoA.class);
            i.putExtras(intent);
            startService(i);
        }
    }
};

CoreTwoA.class (This is the IntentService. onHandleIntent() is not called as I can see due to no log text in the console.)

public class CoreTwoA extends IntentService {

   private TextToSpeech mtts;

   public CoreTwoA() {
       super("TheCoreWorker");
       Log.d("source", "exception", new Exception());
       Log.i("CoreTwoA.TAG", "Constructor");
   }

   @Override
   protected void onHandleIntent(Intent intent) {
       Log.i("CoreTwoA.TAG", "onHandleIntent");

   }
}

AndroidManifest.xml

    <service
        android:name=".CoreTwoA"
        android:label="@string/service_name"
        android:exported="false">
    </service>

UPDATE

So based on discussions below, I was able to narrow down the problem to the following line of code in the BroadCastReceiver:

i.putExtra("sbn", sbn) 

If I remove it, i.e. add no extras to the intent, then my the onHandleIntent() method in my IntentService does run.

If it is included, onHandleIntent() doesn't run and the following is written to logcat by the Log.d() in the Constructor of my IntentService

06-10 19:40:35.355 25094-25094/com.dezainapps.myapp D/source: exception
                                                                       java.lang.Exception
                                                                           at com.dezainapps.myapp.CoreTwoA.<init>(CoreTwoA.java:20)
                                                                           at java.lang.Class.newInstance(Native Method)
                                                                           at android.app.ActivityThread.handleCreateService(ActivityThread.java:2859)
                                                                           at android.app.ActivityThread.-wrap4(ActivityThread.java)
                                                                           at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1427)
                                                                           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)
06-10 19:40:35.355 25094-25094/com.dezainapps.myapp I/CoreTwoA.TAG: Constructor

Any ideas why passing a StatusBarNotification object, that implements Parcelable, to a IntentService via an Intent doesn't work? Oddly enough broadcasting the same StatusBarNotfication sbn object from my toProcess() method via an intent (see code) does work.

REG1
  • 486
  • 4
  • 15
  • 2
    Have you checked with some log that `notificationForwarder` actually runs? Maybe you forgot to register the receiver. The service isn't started when `onStartCommand` isn't executed or vice versa. You can also try to do `Log.d("source", "", new Exception())` in the constructor to figure out what creates it. – zapl Jun 10 '16 at 00:56
  • @zapl So I added the Log.d() to the constructor and now I receive the exception posted above. However, I do not understand the exception. Would appreciate some help. Thanks for the good suggestion. – REG1 Jun 10 '16 at 09:18
  • @AADTechnical I added the method that sends the broadcast – REG1 Jun 10 '16 at 10:18
  • how is this broadcastreceiver getting invoked? can you show that part? I am calling your IntentService `startService(new Intent(MainActivity.this, CoreTwoA.class));` where MainActivity is Activity class and I can see that onHandleIntent is getting called. There is an issue with how your broadcastreceiver is being called here... – AADProgramming Jun 10 '16 at 10:22
  • 1
    i posted the answer, with working code, test at your end! the issue is you are incorrectly using i.putextra – AADProgramming Jun 10 '16 at 10:29
  • @zapl I would appreciate it if you could check out my updated question – REG1 Jun 10 '16 at 13:34
  • The only reason I can imagine that adding a parcellable object could cause the service not to start is because it fails while writing to the parcel. But that should cause an error visible in logcat (is there?). The stacktrace showed that it was creating the service (oops, did I not hit enter when writing that in a comment earlier?). And that should IMO only happen after parceling, and in case of services local to the activity there should be no parceling that could fail at all. – zapl Jun 10 '16 at 14:15
  • @zapl No there is no error message in the logcat, that I can see is linked to failure in the parcel being written. Although the `Log.d()` in the Constructor of the IntentService does write the some exception to logcat (see update above). However I do not understand it. Any ideas? Thanks for responding. – REG1 Jun 10 '16 at 17:51
  • The exception was intentional to see where the constructor was called from. You can manually do `new Exception()`and then see the current stack trace. – zapl Jun 10 '16 at 20:05
  • Please post the complete exception with stacktrace from the logcat. Don't filter the logcat because you might be missing something. I'm assuming that the marshalling/unmarshalling of the `StatusBarNotification` is failing. You should see something about that in the logcat. – David Wasser Jun 10 '16 at 20:54
  • @DavidWasser I posted all that there is. There is no uncaught exception that is thrown and nothing about the 'StatusBarNotification' being marshalled/unmarshalled. – REG1 Jun 10 '16 at 21:55

4 Answers4

2

I had the same problem as the OP.

The issue turned out to be that I was passing a huge ArrayList (of about 4000 Parcelable elements) via the Intent that starts the IntentService. It was extremely difficult to diagnose as it failing 'silently' on a customer's device, so I got no ACRA error report.

Anyway, I used a quick and dirty trick to fix the problem - basically to store the ArrayList in a static element that I used to set/get the ArrayList rather than try to pass it through the Intent.

On further investigation (doing some tests other devices) I found a TransactionTooLargeException being thrown. More discussion on that here.

Here's my test code if anyone would like to replicate:

Test code

If you want to make it work, change the 4000 value to something much smaller.

ArrayList<ParcelableNameValuePair> testArrayList = new ArrayList<>();
for (int i = 0; i < 4000; i++) {
    testArrayList.add(new ParcelableNameValuePair("name" + i, "value" + i));
}
TestIntentService.startAction(AdminHomeActivity.this, testArrayList);

TestIntentService.java

public class TestIntentService extends IntentService {

    private static final String LOG_TAG = TestIntentService.class.getSimpleName();

    private static final String TEST_ACTION = "com.example.action.FOO";

    private static final String EXTRA_PARAM1 = "com.example.extra.PARAM1";
    private static final String EXTRA_PARAM2 = "com.example.extra.PARAM2";

    public TestIntentService() {
        super("TestIntentService");
    }

    public static void startAction(Context context, ArrayList<ParcelableNameValuePair> testArrayList) {

        Log.d(LOG_TAG, "1. startAction()");
        Utilities.makeToast(context, "1. startAction()");

        try {
            int arrayListSize = (testArrayList == null) ? -1 : testArrayList.size();
            Log.d(LOG_TAG, "2. arrayListSize: " + arrayListSize);
            Utilities.makeToast(context, "2. arrayListSize: " + arrayListSize);

            Intent intent = new Intent(context, TestIntentService.class);
            intent.setAction(TEST_ACTION);
            //intent.putExtra(EXTRA_PARAM1, testArrayList);
            intent.putParcelableArrayListExtra(EXTRA_PARAM2, testArrayList);

            /**
             * This line should result in a call to onHandleIntent() but, if we're sending a huge ArrayList, it doesn't...
             */
            context.startService(intent);
        }
        catch(Exception e) {
            Log.e(LOG_TAG, "Exception starting service", e);
            Utilities.makeToast(context, "Exception starting service: " + e);
        }
    }

    @Override
    protected void onHandleIntent(Intent intent) {

        Log.d(LOG_TAG, "3. onHandleIntent()");
        Utilities.makeToast(getApplicationContext(), "3. onHandleIntent()");

        try {
            if (intent != null) {
                final String action = intent.getAction();
                if (TEST_ACTION.equals(action)) {

                    ArrayList<ParcelableNameValuePair> testArrayList = intent.getParcelableArrayListExtra(EXTRA_PARAM1);
                    int testArrayListSize = (testArrayList == null) ? -1 : testArrayList.size();
                    Log.d(LOG_TAG, "4. testArrayListSize: " + testArrayListSize);
                    Utilities.makeToast(getApplicationContext(), "4. testArrayListSize: " + testArrayListSize);

                    ArrayList<ParcelableNameValuePair> testArrayList2 = intent.getParcelableArrayListExtra(EXTRA_PARAM2);
                    int testArrayList2Size = (testArrayList2 == null) ? -1 : testArrayList2.size();
                    Log.d(LOG_TAG, "5. testArrayList2Size: " + testArrayList2Size);
                    Utilities.makeToast(getApplicationContext(), "5. testArrayList2Size: " + testArrayList2Size);

                }
            }
        }
        catch(Exception e) {
            Log.e(LOG_TAG, "Exception handling service intent", e);
            Utilities.makeToast(getApplicationContext(), "Exception handling service intent: " + e);
        }
    }

}

ParcelableNameValuePair.java

public class ParcelableNameValuePair implements Parcelable {

    private String name, value;

    public ParcelableNameValuePair(String name, String value) {
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public String getValue() {
        return value;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeString(name);
        out.writeString(value);
    }

    public static final Parcelable.Creator<ParcelableNameValuePair> CREATOR = new Parcelable.Creator<ParcelableNameValuePair>() {
        public ParcelableNameValuePair createFromParcel(Parcel in) {
            return new ParcelableNameValuePair(in);
        }

        public ParcelableNameValuePair[] newArray(int size) {
            return new ParcelableNameValuePair[size];
        }
    };

    private ParcelableNameValuePair(Parcel in) {
        name = in.readString();
        value = in.readString();
    }

}

Like I say, I used a quick and dirty solution to get around this problem. I think a better solution would be for the app to write the ArrayList to the file system, then pass a reference to that file (e.g., filename/path) via the Intent to the IntentService and then let the IntentService retrieve the file contents and convert it back to an ArrayList.

When the IntentService has done with the file, it should either delete it or pass the instruction back to the app via a Local Broadcast to delete the file that it created (passing back the same file reference that was supplied to it).

Community
  • 1
  • 1
ban-geoengineering
  • 18,324
  • 27
  • 171
  • 253
0

Have you tried using context.startService(intent);?

When you're in a broadcast receiver like this you don't have a context of your own to reference I believe so you need to use the one passed to the onRecieve method.

0

Use this:

private BroadcastReceiver notificationForwarder = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
        Log.i("NoLiSe.TAG", "BroadCastReceiver.onReceive");
        Intent i = new Intent(context, CoreTwoA.class);
        i.putExtra("intent",intent);
        context.startService(i);
    }
}

};

Muneesh
  • 433
  • 10
  • 19
  • For the sake of leaving no stone unturned I tried this, but it doesn't change anything. – REG1 Jun 10 '16 at 10:01
0

Use below code:

private BroadcastReceiver notificationForwarder = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e("NoLiSe.TAG", "BroadCastReceiver.onReceive");
            Intent i = new Intent(context, CoreTwoA.class);
            i.putExtra("sbn",intent.getParcelableExtra("sbn"));
            startService(i);
        }
    };

You are incorrectly using i.putExtras(intent); which I removed and added other way of sending the StatusBarNotification object through putExtra.

I tested this code and does call onHandleIntent

AADProgramming
  • 6,077
  • 11
  • 38
  • 58
  • Ok so I copy and pasted your code and it does call onHandleIntent so it works. But now the intent has no extras. How do I send by StatusBarNotification sbn as an extra? I tried adding `StatusBarNotfication sbn = intent.getParcelableExtra("sbn"); i.putExtra("sbn", sbn); ` to your code, but then weirdly enough onHandleIntent isn't called, so back to the old issue again. Any ideas? Thanks for at least assuring that my IntentService does work to some extent. – REG1 Jun 10 '16 at 10:40
  • @REG1 this is what we need to do `i.putExtra("sbn",intent.getParcelableExtra("sbn"));` Here we use the putExtra API which takes 2nd Parameter as Parcelable. `putExtra(String name, Parcelable value)` Again I tried this and i could see onHandleIntent is called – AADProgramming Jun 10 '16 at 10:58
  • So I am doing exactly as you say: `i.putExtra("sbn", intent.getParcelableExtra("sbn");` where 'sbn' is of type StatusBarNotification. However, for me onHandleIntent() isn't called. Furthermore, I tried starting my service straight from my toProcess() method and using its StatusBarNotfication sbn parameter as my extra to the intent. That doesn't work either. – REG1 Jun 10 '16 at 11:14
  • when you are sending the sbn from `toProcess`, there also you need to use `intent.putExtra("sbn", statusBarNotification);` where we use the putExtra API which takes 2nd Parameter as Parcelable. So, you need to only take care that you are sending using correct `putExtra` If you are using Android studio, you can see this overloaded `putExtra` which takes 2nd Parameter as Parcelable. – AADProgramming Jun 10 '16 at 11:19
  • But there is no actual way of specifying which `.putExtra()` you're using, I can't do something like `.putExtraParcelable()`. The way I understand it you simply use `.putExtra(String str, x)` where 'x' can be any of the accepted extra-types, the system will then match the correct API method according to your type of object x. – REG1 Jun 10 '16 at 11:25
  • there is a way, if you use android studio. just type intent and dot. And wait...AS should automatically show all methods that you can use, along with all `putExtra` options. Here, you need to choose the `putExtra` which takes 2nd Argument as Parcelable – AADProgramming Jun 10 '16 at 11:32
  • Ok, I am actually doing that. I specifically choose the `.putExtra(String name, Parcelable value)` API. However, it doesn't actually change anything. – REG1 Jun 10 '16 at 11:37