【问题标题】:How do I keep a service bound to the right activity instance?如何将服务绑定到正确的活动实例?
【发布时间】:2012-07-21 23:24:30
【问题描述】:

由于各种原因,我无法让我的应用正常工作。

设计概述: 我的目标是一个具有两个按钮(连接、断开连接)和一个显示从连接接收到的数据的文本视图的活动。我编写了一个“MySocket”服务来处理 WebSocket 连接(Gottox 的 Java 实现),每当 tit 从服务器接收到某些内容时,该服务就会向活动发布一条消息,然后活动将该数据附加到 textview 上。该服务必须从活动接收连接/断开连接提示,并能够将消息传递给活动。 MySocket 还需要作为前台服务运行,以便无限期地保持连接,并向用户提供粘性通知。

我在各个地方都遇到了重大问题。首先,我注意到我可以启动另一个活动实例,而原始实例仍在更新。我通过在清单中指定android:launchMode="singleInstance"> 解决了这个问题。我遇到的问题是,当活动被销毁并创建新活动时,服务继续发布更新,但重点活动没有收到它们。如果原始活动被破坏并且服务处理程序没有更新,那么一旦活动被破坏,它肯定会抛出空指针异常,但应用程序继续运行。然后,我尝试在我的 onDestroy 方法中运行 unbindService(mConnection),但这引发了 java.lang.IllegalArgumentException: Service not registered: 该服务正在发送消息 something 这应该是重点活动,但似乎没有收到它。我怀疑我的前台服务执行不力是罪魁祸首,但我真的不知道。

有问题的两个类都是 LONG,所以我删除了 Logcat 行、全局变量和一些我确定不相关的方法。我意识到架构是可怕的,但这与我的工作一样接近。如果代码太可怕而无法阅读,我很乐意有人提出替代结构,前提是它达到了预期的结果。

活动类:

@Override
public void onCreate(Bundle savedInstanceState)
{
      ...
      mMessenger = new Messenger(mHandler);
      connect.setOnClickListener(new View.OnClickListener()
    {
        @Override
        public void onClick(View v)
        {
            messageSocket("ACTION", "CONNECT");
        }
    });

     @Override
    protected void onDestroy()
    {
      super.onDestroy();
      doUnbindService();
    }

    private ServiceConnection conn = new ServiceConnection() 
    {
    public void onServiceConnected(ComponentName className, IBinder binder) 
    {
        Log.d(debug, "Service Connected");
        messenger = new Messenger(binder);
        boundToService = true;
    }

    public void onServiceDisconnected(ComponentName className) 
    {
        Log.d(debug, "Service Disconnected");
        messenger = null;
        boundToService = false;
    }
    };

    void doBindService() 
    {
    final Intent intent = new Intent(this, MySocket.class);
    intent.putExtra("MESSENGER", messenger);
    Thread t = new Thread()
    {
        public void run()
        {
        boundToService =
                getApplicationContext().bindService(
                intent,
                conn,
                Context.BIND_AUTO_CREATE
            );
        }
    };
    t.start();
}

还有 MySocket 类:

class IncomingHandler extends Handler 
{
    @Override
    public void onCreate() 
{...}

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        startForeground( id, showNotification() );
    connect();
        return START_STICKY;
}

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        startForeground( id, showNotification() );
        connect();
        return START_STICKY;
}


private Notification showNotification()
{
    nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    CharSequence text = "WebSocket currently active";
    Notification notification = new Notification(R.drawable.ic_launcher,
            text, System.currentTimeMillis());
    PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
            new Intent(this, SocketTester.class), 0);
    notification.setLatestEventInfo(this, "WebSocket Test", text,
            contentIntent);
    notification.flags = Notification.FLAG_FOREGROUND_SERVICE;
    return notification;
}
        @Override
    public IBinder onBind(Intent intent)
    {
        Bundle extras = intent.getExtras();
        Log.d(debug, "onBind called");
        if (intent.getExtras() != null)
        {   
            Log.d(debug, "Setting messenger");
            outMessenger = (Messenger) extras.get("MESSENGER");
        }
        return inMessenger.getBinder();
        }   

    @Override
    public void handleMessage(Message msg) 
            {           
        Bundle data = msg.getData();            
        Message backMsg = Message.obtain();
        Bundle bundle = new Bundle();
        backMsg.setData(bundle);

        if ("CONNECT".equals(data.getString(ACTION)))
        {   
            Log.d(debug, "Connecting");
            startService(new Intent(MySocket.this, MySocket.class));
            /*startService now calls connect();*/
        }
        else if ("DISCONNECT".equals(data.getString(ACTION)))
        {
            if (socket != null)
            {   
                socket.disconnect();
            }
            stopForeground( true );
            stopSelf();
        }
    }
}

【问题讨论】:

  • 我将为您提供另一种体系结构,但为此您必须说明您愿意在应用程序中做什么,并且应该切中要害。谢谢:)
  • 需要有一个活动显示通过 websocket 的数据,并提供用户打开和关闭连接。一旦打开,连接应保持打开状态,直到用户关闭。还必须有一个粘性通知,让用户知道连接仍处于打开状态。
  • ok 你为什么不保存你的数据,已经被记录了。通过服务类中 SharedPreference 中的 websocket 连接,因此,您可以收集该数据并在您想要的任何活动中显示?
  • 接收到的数据是可变的,使用 SharedPreferences 存储未指定数量的数据似乎非常混乱。我也不想使用数据库。我的应用程序目前几乎完全正常运行,我只是无法将服务绑定到正确的活动实例。我非常热衷于了解 android 服务绑定和使用 SharedPreferences 似乎是一种廉价的出路。 (对不起,如果这听起来很粗鲁,我不打算这样做)

标签: android android-service android-handler


【解决方案1】:

“如果您使用 startService(..) 启动一个 android Service,该 Service 将一直运行,直到您显式调用 stopService(..)。系统可以运行服务有两个原因。如果有人调用 Context. startService() 然后系统将检索服务(创建它并在需要时调用其 onCreate() 方法)然后使用客户端提供的参数调用其 onStartCommand(Intent, int, int) 方法。此时服务将继续运行直到 Context.stopService() 或 stopSelf() 被调用。请注意,多次调用 Context.startService() 不会嵌套(尽管它们确实会导致多次相应地调用 onStartCommand()),所以不管它多少次启动后,一旦调用 Context.stopService() 或 stopSelf(),服务就会停止;但是,服务可以使用它们的 stopSelf(int) 方法来确保在处理启动的意图之前不会停止服务。

客户端也可以使用 Context.bindService() 来获得到服务的持久连接。如果服务尚未运行(在执行此操作时调用 onCreate()),这同样会创建服务,但不会调用 onStartCommand()。客户端将接收服务从其 onBind(Intent) 方法返回的 IBinder 对象,然后允许客户端调用回服务。只要建立连接,服务就会保持运行(无论客户端是否保留对服务的 IBinder 的引用)。通常返回的IBinder是针对已经用aidl编写的复杂接口的。

服务既可以启动也可以绑定连接。在这种情况下,只要服务已启动或有一个或多个使用 Context.BIND_AUTO_CREATE 标志的连接,系统就会保持服务运行。一旦这两种情况都不成立,就会调用服务的 onDestroy() 方法并有效地终止服务。从 onDestroy() 返回后,所有清理(停止线程、注销接收器)都应该完成。”

我希望这将有助于您完成工作。

谢谢:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-27
    • 1970-01-01
    • 2013-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多