【问题标题】:IntentService not firingIntentService 未触发
【发布时间】:2012-07-05 14:59:27
【问题描述】:

我的应用程序通过 Web 服务调用将数据与远程数据库同步。我在 IntentService 中进行这些调用,以便它们可以在后台运行(我称之为 SyncService)。

启动我的 IntentService 的代码如下所示:

Intent intent = new Intent();
intent.setClass(appContext, SyncService.class);

// place additional values in intent
intent.putExtra("data_type", SyncService.ITEM_TRACKING);
intent.putExtra("user_id", intUserId);

// call SyncService
appContext.startService(intent);

这通常看起来很棒。但是,我的一位朋友也是我的应用程序的用户,他经常告诉我他的数据没有同步并显示在我们的网站上。当我在附近时,他的设备恰好显示了这些症状。我将他的设备插入我的电脑,这是我发现的:

  • 启动 SyncService 的代码(即:上面的代码)被命中。
  • 我的 IntentService 的 onHandleIntent 方法中有一个断点,它从不被命中。
  • 我检查了他设备的运行服务列表,SyncService 在那里并且正在运行。有趣的是,它已经运行了大约 20 分钟。我的印象是,当 IntentService 完全无法处理时,IntentService 会自行杀死。
  • 我强行停止了 SyncService(而不是应用程序),突然之间,onHandleIntent 开始反复受到攻击。就像所有 Intent 都在设备上的某个地方排队,然后刚刚被抛出 SyncService。

有人对可能出现的问题有任何想法吗?你认为这是我的应用程序的问题吗?用安卓?

我再次向 Android 发送一条消息,说“启动这个 IntentService 或将消息发送到已经运行的 IntentService。”那个时候,我无法控制。消息永远不会到达 IntentService。一旦我强制退出应用程序,消息就会发送到 IntentService 并完成它的工作。

更新:我认为这段代码很好,但我会把它放上来,因为你们很多人可能想看它。

进入 IntentService 的每个 Intent 都有一个 Extra 表示对我进行的调用的“类型”(即:我是调用 this Web 服务还是 that网络服务等)。当 Intent 进入 IntentService 时,我会检查“类型”,如果队列中已经有该类型的 Intent,我会在其中添加一个名为“skip”的 Extra,因此,当它到达时,我不会t 执行搜索(基本上 IntentService 可以构建很多 Intent,并且在 20 秒前调用 this webservice 时调用 this web service 是没有意义的)。它基本上可以保护应用免于向网站发送垃圾邮件。

请务必注意,无论如何都不会命中此代码(一旦问题开始出现)。 onStartCommand 在应用程序被杀死之前不会被调用

    @Override
    public int onStartCommand (Intent intent, int flags, int startId) {
        // here be dragons
        // overriding this method and adding your own code is dangerous. i've wrapped
        // my code in a try/catch because it is essential that the super method be called
        // every time this method is entered. any errors in my code should not prevent this
        // or the app will explode.
        try {
            if (flags == 0 && intent != null && intent.hasExtra("data_type")) {
                Integer intDataType = intent.getExtras().getInt("data_type");

                    if (!mCurrentTypes.containsKey(intDataType)
                            || !mCurrentTypes.get(intDataType)) {
                        mCurrentTypes.put(intDataType, true);  // put this type in the list and move on
                    }
                    else {
                        intent.putExtra("skip", true);  // mark this Intent to be skipped
                    }
            }
        }
        catch (Exception e) {
            // Log.e("Error onStartCommand", "error: " + e);
        }

        return super.onStartCommand(intent, flags, startId);
    }


private void processIntent(Intent intent) {
        // do stuff if no "skip" Extra
        mCurrentTypes.put(intDataType, false);
    }

【问题讨论】:

  • 您是否在清单文件中注册了您的服务?
  • 是的。同样,该服务通常有效。我的朋友是唯一提到这个问题的人。而且大多数时候它对他来说效果很好,但偶尔会……拥堵?
  • 您的 IntentService 中似乎有一段代码导致应用程序长时间停留。由于 IntentService 一个接一个地排队按顺序运行,这就解释了为什么在杀死当前的 IntentService 后你会一遍又一遍地点击 onHandleIntent。如果您可以发布您的 IntentService 代码,我们或许能够确定它为什么会卡在那里。
  • 嗯...我的 IntentService 代码非常大(我有很多很多不同的 Web 服务)。有没有一种方法可以在 Intent 被处理然后完成时发布日志,并从他的手机中检索该日志以进行分析?
  • 您的网络服务调用是否设置了超时?你在你的服务上创建任何线程吗?您是否有足够的时间调用您的服务以完成之前的执行?看起来有些东西正在阻止服务完成它的执行,并且其余已启动的服务都在排队。

标签: android service intentservice


【解决方案1】:

肯定有一些东西可以让您的服务在您朋友的设备上运行。如果是这样,则对该意图服务的所有后续调用都将排队,直到当前调用完成。如果它没有完成,那么你将得到你所拥有的:下一个服务将不会启动。

您应该仔细检查:

  • 您为网络操作设置了适当的超时时间
  • 您为网络连接操作设置了适当的超时时间
  • 线程之间没有竞争条件。
  • 您记录了服务内可能发生的任何异常,您不想丢失此类信息。

之后,如果您认为一切都是绿色的:只需记录该服务的功能并使用一些错误报告机制从您的朋友设备自动发送它。一个简单的解决方案可能是使用 bugsense 或等效的。

接下来,设置某种看门狗:一个线程将继续运行直到您的服务停止(您只需告诉您的线程在服务停止时停止)。过了一段时间后,线程将不得不停止你的服务。

这个看门狗线程可以放在服务本身内部,也可以放在外部,尽管这可能更复杂。

【讨论】:

  • 另一件可以做的检查是否是这种情况是编写自己的服务,从intentservice源代码中获取灵感(这很简单),并有一个“带有探针的intentservice”来执行更多日志记录和看门狗。
  • 你能给我一个可能导致问题的竞争条件的例子吗?由于 IntentService 将消息排入队列,因此实际上哪个线程调用 startService 并不重要。或者您是在谈论处理 Intent 的 IntentService 中的竞争条件(即:我在 IntentService 中编写的代码)?我的代码都不是异步的,所以我认为这不应该是一个问题。我所有的 Web 服务调用都有 10 秒的超时。假设,有 100 条消息排队,这 10 条消息会加起来,但我已经看到当他有 wifi 和手机时都会发生这种情况。
  • 通过竞争条件,我的意思是你的服务一直在运行,永无止境。由于同一类的所有 Intent 服务都由 Android 排队,因此所有 Intent 服务必须在一定的超时后结束。你对此负责,Android 不会自动执行任何此类操作。还要确保为您的网络请求设置套接字超时和连接超时。
【解决方案2】:

这个答案提出了一个在类似情况下对我有用的解决方案。它不会修复您当前的代码,而是建议另一个可能更简单(且更易于调试)的选项:

  1. BroadcastReceiver 添加到您的呼叫Activity,以侦听来自IntentService 的SUCCESS Intents

  2. 在您的调用 Activity 中,包含有关何时启动 IntentService 的逻辑(并且不要将其包含在 IntentService 中)。逻辑是:

    • 调用startService() 并在调用Activity 中设置一个标志为CANNOT_CALL。
    • 如果Activity'sBroadcastReceiver没有收到来自IntentService的SUCCESS广播,那么startService()就不能被再次调用。
    • Activity 确实接收到SUCCESS 意图时,将标志设置为CAN_CALL,并在计时器再次触发时调用startService()
  3. 在您的IntentService 中,像这样写您的onStartCommand()

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        return START_STICKY;
    }
    
  4. 在您IntentService 中,当您接收、解析并存储 Web 服务响应后,使用自定义操作成功的 Intent 调用 sendBroadcast()

此逻辑只是一个大纲,必须针对来自 Web 服务的错​​误消息进行微调,这些错误消息必须从 IntentService 广播到监听 Activity

希望这会有所帮助。

【讨论】:

    【解决方案3】:

    在我看来,为您的 Intent 设置一组标志可能会解决问题。

    Intent intent = new Intent();
    intent.setClass(appContext, SyncService.class);
    // This way    
    intent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK|Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
    

    您可以在全新任务中使用上述标志使您的服务以全新方式启动。

    【讨论】:

      【解决方案4】:

      还有一条评论。这不是你问题的答案。但是,它可能会影响服务的整体行为。

      您执行以下操作:

       return super.onStartCommand(intent, flags, startId);
      

      内部 Service.onStartCommand() 如下所示

      public int onStartCommand(Intent intent, int flags, int startId) {
              onStart(intent, startId); 
              return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
      }
      

      如果您的应用面向 SDK API 7 或更高版本(很可能是这种情况),则 mStartCompatibility 为 false。

      因此,您的服务将以 START_STICKY 启动。

      这是文档中的一段:

      对于已启动的服务,它们可以决定运行另外两种主要的操作模式,具体取决于它们从 onStartCommand() 返回的值:START_STICKY 用于根据需要显式启动和停止的服务,而 START_NOT_STICKY 或START_REDELIVER_INTENT 用于只应在处理发送给它们的任何命令时保持运行的服务。有关语义的更多详细信息,请参阅链接文档。

      根据您的描述,我建议替换“return super.onStartCommand(intent, flags, startId);”到“返回 START_NOT_STICKY;”

      【讨论】:

      • 我看不到问题的链接。当系统处于内存压力并终止您的应用程序时,启动粘性或启动不粘性会影响服务的行为。它将尽快重新启动任何声明为粘性的服务。在我看来,这与一项服务执行且永不结束这一事实无关,但我可能错了。
      • IntentService 不会总是返回 START_NOT_STICKY 吗?
      • 2Snicolas:我相信使用粘性服务,系统不会停止服务,它会完成处理当前意图,而非粘性会完成它。我只是想指出这一点,以便知道服务可能出于多种原因而运行。
      • 2Andrew:显然不是。我放的这段代码来自Android开源项目。
      猜你喜欢
      • 1970-01-01
      • 2016-10-23
      • 1970-01-01
      • 2015-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多