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.
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).
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.
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);
}
}
};
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