Android: 绑定服务无法发送第一条消息,所有其他消息通过。为什么会发生这种情况,如何解决?

Android: bound service can't send first message, all other messages go through. Why does this happen, how to fix it?

我有一个 android activity 和一个相应的服务,其中 activity 只是一个 UI 并且该服务计算要显示的内容。该服务绑定到 activity.

首先我建立一个连接:

在 Activity.java 文件中:

final Messenger _messenger = new Messenger(new IncomingHandler(new WeakReference<>(this)));
Messenger _serviceMessenger = null;

private ServiceConnection _connection = new ServiceConnection()
{
    @Override
    public void onServiceConnected(ComponentName name, IBinder service)
    {
        _serviceMessenger = new Messenger(service);
        try
        {
            // sending the initial welcome message
            Message m = Message.obtain(null, ForegroundService.BIND_SERVICE);
            m.replyTo = _messenger;
            _serviceMessenger.send(m);
        }
        catch(RemoteException ex)
        {
            _serviceMessenger = null;
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name)
    {
        _serviceMessenger = null;
    }
};

private static class IncomingHandler extends Handler
{
    private WeakReference<MyActivity> _parent;

    IncomingHandler(WeakReference<MyActivity> parent)
    {
        super();
        _parent = parent;
    }

    @Override
    public void handleMessage(Message msg)
    {
        Log.i(LOG_TAG, "Activity: Received message");
        MyActivity ta = _parent.get();
        switch(msg.what)
        {
            case ForegroundService.LOCATION_UPDATED:
                if(msg.obj == null)
                {
                    Log.e(LOG_TAG, "Activity: msg null");
                    ta.setTexts("", null, null);
                }
                else
                {
                    Log.i(LOG_TAG, "Activity: msg ok");
                    LocWithName loc = (LocWithName)msg.obj;

                    ta.setTexts(loc.getName(), Double.toString(loc.getHossz()), Double.toString(loc.getSzel()));

                    Chronometer chronometer = ta.findViewById(R.id.chronom);
                    chronometer.setBase(SystemClock.elapsedRealtime());
                    chronometer.start();
                }
                break;
            case ForegroundService.LOST_GPS:
                ta.setTexts("", "unknown", "unknown");
                break;
            default:
                super.handleMessage(msg);
                break;
        }
    }
}

在 activity onCreate:

Intent startIntent = new Intent(MyActivity.this, ForegroundService.class);
            startIntent.setAction(Constants.ACTION.STARTFOREGROUND_ACTION);

            bindService(startIntent, _connection, Context.BIND_AUTO_CREATE);
            startService(startIntent);

并在服务 java 文件中:

final Messenger _messenger = new Messenger(new IncomingHandler(new WeakReference<>(this)));
Messenger _activityMessenger = null;

private static class IncomingHandler extends Handler
{
    WeakReference<ForegroundService> _parent;

    IncomingHandler(WeakReference<ForegroundService> parent)
    {
        super();
        _parent = parent;
    }

    @Override
    public void handleMessage(Message msg) {
        // received welcome message, now we know who to reply to
        if(msg.what == ForegroundService.BIND_SERVICE)
        {
            _parent.get()._activityMessenger = msg.replyTo;
            Log.d(LOG_TAG, "received reply address for messenger"); // after 1st edit
        }
        else
        {
            super.handleMessage(msg);
        }
    }
}

private void sendMessageToUI(int message, LocWithName newLoc)
{
    Log.i(LOG_TAG, "Service: sending message to UI");
    if(_activityMessenger != null)
    {
        Log.d(LOG_TAG, "messenger not null"); // after 1st edit
        try
        {
            _activityMessenger.send(Message.obtain(null, message, newLoc));
            Log.i(LOG_TAG, "Service: message sent");
        }
        catch(RemoteException ex)
        {
            // activity is dead
            _activityMessenger = null;
        }
    }
}

然后我开始通过服务的 sendMessageToUI() 函数定期发送消息,即每 5 秒一次。服务的 onStartCommand 运行 是第一个 UI 立即更新,它会为每隔一次迭代重新安排自己。

我知道的:

我尝试过的:

由于第一条消息和第二条消息之间有五秒的间隔,我怀疑这是初始化速度的问题,但我不能再进一步了。那么这里到底发生了什么,为什么不起作用?

编辑 1:在@pskink 的建议下,我添加了 Log.d()-s。事实证明 activity 仅在 的第一个 运行 之后发送回复地址为 的 "welcome message" UI 更新程序尽管在 startService.

之前被调用

此外,在@pskink 询问后发送消息的代码:

在役 class:

final Handler handler = new Handler();
Runnable updateUI = new Runnable()
{
    // do work to get the information to display
    // in this code I set "int message" to one of the constants handled by the activity's IncomingHandler and "LocWithName newLoc" to a useful value or null

    sendMessageToUI(message, newLoc);
    handler.removeCallbacks(updateUI);
    handler.postDelayed(updateUI, 5000);
}

在启动命令服务中:

handler.post(updateUI);

您的错误是假设 bindService()startService() 调用阻塞,直到服务分别为 "bound" 或 "started"。实际情况是 onServiceConnected() 直到 onCreate() returns 之后的某个时间才会被调用。同样,您调用它们的顺序基本上没有意义;在这种情况下,OS 不保证服务将首先或第二处理绑定或 onStartCommand()

要修复,请删除对 startService() 的调用(正如@pskink 所建议的);该服务是由于您绑定到它而启动的。 onStartCommand() 将不再被调用。相反,让服务在收到 ForegroundService.BIND_SERVICE 消息时启动 updateUI Runnable。这允许您建立适当的 "happens before" 关系——即您开始尝试使用 _activityMessenger 发送消息的 ServiceConnection 绑定 "happens before"。