【问题标题】:Service being killed while holding wake lock and after calling startForeground服务在持有唤醒锁和调用 startForeground 后被杀死
【发布时间】:2011-09-13 03:30:57
【问题描述】:

我遇到了一个问题,即使我持有唤醒锁并且我已调用 startForeground,我的服务也会被终止。当平板电脑 (ASUS Transformer TF101) 发生这种情况时,停止服务而不调用 onDestroy。没有其他可见的应用程序,并且 log cat 没有显示任何异常(没有“内存不足”消息等)。被杀死后,服务立即重新启动。

我正在开发的应用程序是一个聊天客户端,需要持续连接,它也是基于插件的,所以我的应用程序是这样开发的:Client - HostService - Multiple child 'Services'。

主机服务是粘性的,持有唤醒锁并调用 startForeground(并显示这样的通知),子服务不粘性,不持有唤醒锁并且是后台服务。

如果客户端本身是打开的,则不会出现问题,但我想要的模型是用户可以使用设备并保持连接(接收消息等),而无需始终打开客户端本身。

任何人都可以解释为什么服务会以这种方式被杀死,如果是这样的话,可以防止它发生吗?正如聊天客户端在用户登录和注销时显示的那样,并且服务终止会杀死所有打开的连接,这会使聊天客户端“反弹”。目前,它似乎每 15 到 45 分钟发生一次。

另外,如果有人知道一种保持套接字连接持续打开而不在整个连接期间保持唤醒锁的方法,我很想听听!

主机服务源的修剪测试用例版本如下。

public class HostService extends Service
{
    PowerManager m_powerManager = null;
    PowerManager.WakeLock m_wakeLock = null;

    @Override
    public IBinder onBind( Intent intent )
    {
        return m_serviceImplementation;
    }

    @Override
    public void onCreate()
    {
        super.onCreate();       
    }

    @Override
    public void onDestroy()
    {
        if( m_wakeLock != null )
        {
            m_wakeLock.release();
            m_wakeLock = null;
        }

        stopForeground( true );

        super.onDestroy();
    }

    @Override
    public int onStartCommand( Intent intent, int flags, int startId )
    {
        // Display a notification about us starting. We put an icon in the
        // status bar.
        Notification notification = createNotification();

        startForeground( R.string.service_running, notification );

        if( m_powerManager == null )
        {
            m_powerManager = (PowerManager)getSystemService(Context.POWER_SERVICE);
        }

        if( m_wakeLock == null )
        {
            m_wakeLock = m_powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Keep background services running");
            m_wakeLock.acquire();
        }

        // We want this service to continue running until it is explicitly
        // stopped, so return sticky.
        return START_STICKY;
    }

    /**
     * Create a notification to show the service is running
     */
    private Notification createNotification()
    {
        CharSequence text = getText( R.string.service_running );
        CharSequence title = getText( R.string.app_name );

        // The PendingIntent to launch our activity if the user selects this
        // notification
        PendingIntent contentIntent = PendingIntent.getActivity( this, 0, new Intent(this, MainChat.class) , 0 );

        Notification notification = new Notification( android.R.drawable.sym_action_chat, title, System.currentTimeMillis() );  
        notification.setLatestEventInfo( this, title, text, contentIntent );

        return notification;
    }

    private final IMessageInterface.Stub m_serviceImplementation = new IMessageInterface.Stub()
    {
        ...
    };
}

Android Manifest(相关位):

<uses-sdk android:minSdkVersion="11" android:targetSdkVersion="11" />

<service android:name="com.mydomain.chatClient.server.HostService" android:exported="true" android:enabled="true" android:process=":remote"/>

<uses-permission android:name="android.permission.WAKE_LOCK" />

【问题讨论】:

    标签: java android android-service


    【解决方案1】:

    我遇到了一个问题,即我的服务被终止,即使我持有唤醒锁并且我已经调用了 startForeground。

    startForeground() 降低了服务被终止的可能性,但并不能阻止它。

    我正在开发的应用程序是一个聊天客户端,需要持续连接,它也是基于插件的,所以我的应用程序是这样开发的:Client - HostService - Multiple child 'Services'。

    我建议去掉其中一层。即使操作系统没有关闭您,许多用户也会(例如,任务杀手、在设置中运行服务)认为您正在运行太多服务。

    如果客户端本身是打开的,则不会出现问题,但我想要的模型是用户可以使用设备并保持连接(接收消息等),而无需始终打开客户端本身。

    我建议将其设为可选。你可能认为它很性感。您的一些用户会因为您浪费电池而攻击您。

    任何人都可以解释为什么服务会以这种方式被杀死,如果是这样的话,可以防止它发生吗?

    我首先要摆脱android:process=":remote"。你不需要它。你不想要它。拥有它可能会伤害自己,因为它可能会加速 Android 对摆脱您的服务的兴趣。你拥有它绝对是在伤害用户,因为你无缘无故地浪费内存。

    然后,如果您将插件实现为单独的应用程序,我会摆脱这些插件。在这种情况下,它们中的每一个都将在 自己的进程中运行,从而浪费更多的 RAM。此外,您当前的实现将存在缺陷,因为您将无法将服务命名为 com.mydomain.chatClient.server.HostService 直到时间结束,因为您没有使用 &lt;intent-filter&gt; 来区分“服务在内部命名的内容”的关注点”和“其他希望使用它的单独安装的应用程序调用该服务的内容”。如果您没有将插件实现为单独的应用程序,那么我看不到将它们放在单独的服务中而不是将它们全部折叠到一个服务中的价值。

    另外,如果有人知道一种保持套接字连接持续打开而不在整个连接期间保持唤醒锁的方法,我很想听听!

    如果套接字使用无线数据而不是 WiFi,则您不需要一直使用WakeLock。套接字将保持打开状态,该套接字上的传入数据包将唤醒您的代码。那时,您需要获取足够长的WakeLock,以便在数据到达时对数据执行任何操作,然后释放WakeLock

    但是,如果您使用的是 WiFi,则此技巧不起作用,因此需要 WakeLock(可能还有 WifiLock)。

    【讨论】:

    • 感谢您的快速回复!
    • 哎呀,它保存了输入时的评论!拥有额外层的原因是允许第三方开发位于客户端内部的服务。例如,我没有资源为深奥的(或专有的)IM 服务创建功能,但其他人可能会在以后看到添加它的价值。我将尽可能发布“通用”IM 服务的功能,并使用公共 API 供其他人做同样的事情。 “永远在线”后台功能确实是可选的,但我希望许多用户(包括我自己)使用这种模式。
    • 我已经从服务中删除了远程标志,如果这有帮助,如果有人遇到类似问题,我会报告。我还应该提到清单文件还有更多内容,包括一些未列出的意图过滤器 - 我删除了这个,因为我认为它与测试用例无关。
    • @icStatic:“我删掉了这个,因为我认为它与测试用例无关”——好的。不过,在这种情况下,您也可以摆脱android:exported。如果Service 具有&lt;intent-filter&gt;,则会自动导出它。你有android:exported 而没有&lt;intent-filter&gt; 的事实让我相信你没有&lt;intent-filter&gt;
    • @icStatic:还要注意 SystemPanel Lite 并不完全可靠。例如,一时兴起,我刚刚安装了它并运行了我的一本使用startForeground() 的书样。虽然设置应用程序在运行服务中显示我的服务,但 SystemPanel Lite 根本不显示它,稍后会多次刷新。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-14
    • 2011-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多