在IntentService中OnHandleIntent()方法未被调用

4

我知道这个问题之前已经被问过,但是我看过所有能找到的答案,仍然无法解决这个问题。

问题在于当广播接收器启动IntentService时,onHandleIntent()方法没有被调用。奇怪的是构造函数确实运行了(我可以通过日志输出看到)。这是我的代码:

NoLiSeA.class (这个类包含启动我的服务的广播接收器)

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 (这是IntentService。由于控制台中没有日志文本,我无法看到onHandleIntent()被调用。)

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>

更新

根据下面的讨论,我能够将问题缩小到BroadCastReceiver中以下代码行:

i.putExtra("sbn", sbn) 

如果我不添加额外内容到意图中,也就是移除它,那么我的IntentService中的onHandleIntent()方法会运行。如果包含它,则onHandleIntent()不会运行,并且我的IntentService构造函数中的Log.d()会将以下内容写入logcat。
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

有什么想法,为什么通过Intent传递实现Parcelable接口的StatusBarNotification对象到IntentService不起作用? 有趣的是,通过意图从我的toProcess()方法广播相同的StatusBarNotfication sbn对象(请参见代码)确实有效。

2
你是否检查了一些日志,确保 notificationForwarder 实际运行?也许您忘记注册接收器了。当 onStartCommand 没有执行或反之亦然时,该服务不会启动。您还可以尝试在构造函数中执行 Log.d("source", "", new Exception()) 以找出是什么创建了它。 - zapl
1
我已经发布了答案,附上了可以工作的代码,请你自己测试!问题是你错误地使用了i.putextra。 - AADProgramming
我能想到的唯一原因是添加一个可包含对象可能导致服务无法启动,是因为在写入包裹时失败了。但这应该会在logcat中显示错误(有吗?)。堆栈跟踪显示它正在创建服务(哎呀,我之前在注释中没有按回车键吗?)。在我的看法中,这只会发生在打包后,对于活动本地的服务来说,根本不会出现任何可能失败的打包。 - zapl
异常是有意为之的,以查看构造函数从哪里调用。您可以手动执行 new Exception(),然后查看当前堆栈跟踪。 - zapl
请在logcat中发布完整的异常和堆栈跟踪。不要过滤logcat,因为您可能会错过一些信息。我假设StatusBarNotification的编组/解组失败了。您应该在logcat中看到相关信息。 - David Wasser
显示剩余6条评论
4个回答

2

我遇到了与OP相同的问题。

问题原因是我通过启动IntentServiceIntent传递了一个巨大的ArrayList(大约有4000个Parcelable元素)。它在客户设备上默默失败,非常难以诊断,因此我没有收到ACRA错误报告。

无论如何,我使用了一个简单而粗暴的方法来解决这个问题 - 基本上是将ArrayList存储在静态元素中,我用它来设置/获取ArrayList,而不是尝试通过Intent传递它。

进一步调查后(在其他设备上进行了一些测试),我发现抛出了一个TransactionTooLargeException异常。 这里有更多讨论

如果有人想要复制我的测试代码,可以在下面找到:

测试代码

如果您想使其工作,请将4000值更改为小得多的值。

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

}

就像我说的那样,我使用了一个简单粗暴的解决方案来解决这个问题。我认为更好的解决方案是让应用程序将 ArrayList 写入文件系统,然后通过 Intent 将对该文件的引用(例如,文件名/路径)传递给 IntentService,然后让 IntentService 检索文件内容并将其转换回 ArrayList

IntentService 完成对文件的操作后,它应该删除该文件或通过本地广播将指令传递回应用程序以删除创建的文件(传递回相同的文件引用)。


0
你尝试过使用context.startService(intent)吗?
当你在像这样的广播接收器中时,我相信你没有自己的上下文可以引用,因此你需要使用传递给onRecieve方法的上下文。

我将其更改为context.startService(intent); 但是这并没有改变任何事情。 - REG1

0
使用这个:
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);
    }
}

};

为了不留下任何遗漏,我尝试了这个方法,但它并没有改变任何事情。 - REG1

0
请使用以下代码:
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);
        }
    };

你错误地使用了 i.putExtras(intent);,我已将其删除并添加了其他通过putExtra发送StatusBarNotification对象的方式。
我测试了这段代码并调用了onHandleIntent

好的,我复制并粘贴了你的代码,它确实调用了onHandleIntent,所以它是有效的。但现在intent没有额外的内容。如何将StatusBarNotification sbn作为额外的发送?我尝试将StatusBarNotfication sbn = intent.getParcelableExtra("sbn"); i.putExtra("sbn", sbn); 添加到你的代码中,但奇怪的是onHandleIntent没有被调用,所以又回到了旧问题。有什么想法吗?感谢你至少保证了我的IntentService在某种程度上是有效的。 - REG1
所以我正在按照您说的做:i.putExtra("sbn", intent.getParcelableExtra("sbn"); 其中sbn类型为 StatusBarNotification。 但是,对我来说,onHandleIntent()没有被调用。 此外,我尝试直接从我的toProcess()方法启动服务,并使用它的StatusBarNotification sbn参数作为我的意图额外参数。 这也不起作用。 - REG1
但是实际上没有指定使用哪个.putExtra()的方法,我无法像.putExtraParcelable()那样做。据我理解,您只需使用.putExtra(String str, x),其中'x'可以是任何接受的额外类型,系统将根据对象x的类型匹配正确的API方法。 - REG1
如果您使用Android Studio,有一种方法。只需键入“intent”和点号。然后等待...AS应自动显示您可以使用的所有方法以及所有“putExtra”选项。在这里,您需要选择将第二个参数设置为Parcelable的“putExtra”。 - AADProgramming
好的,我正在做这件事。我特别选择了.putExtra(String name, Parcelable value) API。然而,它实际上并没有改变任何东西。 - REG1
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接