OnHandleIntent() not called in IntentService

2019-05-05 11:09发布

问题:

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.

回答1:

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



回答2:

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.



回答3:

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

};



回答4:

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