Как сохранить привязку службы к нужному экземпляру активности?

Я не могу заставить свое приложение работать правильно по разным причинам.

Обзор дизайна: то, к чему я стремлюсь, - это действие, которое имеет две кнопки (подключение, отключение) и текстовое представление, которое отображает данные, полученные от соединения. Я написал службу «MySocket», которая обрабатывает соединение WebSocket (реализация Gottox на Java), и эта служба отправляет сообщение активности всякий раз, когда она получает что-то с сервера, затем активность добавляет эти данные в текстовое представление. Служба должна получать запросы на подключение/отключение от активности и иметь возможность передавать сообщения активности. MySocket также должен работать в качестве службы переднего плана, чтобы он оставался подключенным на неопределенный срок, а пользователю предоставлялось липкое уведомление.

У меня серьезные проблемы с этим в разных местах. Во-первых, я заметил, что могу запустить другой экземпляр активности, в то время как оригинал все еще обновляется. Я исправил это, указав android:launchMode="singleInstance"> в манифесте. Проблема, с которой я столкнулся, заключалась в том, что когда активность была уничтожена и создана новая, служба продолжала публиковать обновления, но сфокусированная активность их не получала. Если бы исходная активность была уничтожена, а обработчик служб не был обновлен, то, безусловно, после уничтожения активности было бы выдано исключение нулевого указателя, но приложение продолжало работать. Затем я попытался запустить unbindService(mConnection) в своем методе onDestroy, но это выдало java.lang.IllegalArgumentException: Service not registered: Служба отправляет сообщение что-то, что должно быть сфокусировано, но, похоже, не получает его. Я подозреваю, что виновата моя плохая реализация службы переднего плана, но я действительно понятия не имею.

Два рассматриваемых класса являются ДЛИННЫМИ, поэтому я удалил строки 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();
        }
    }
}

person Chironex    schedule 21.07.2012    source источник
comment
Я предоставлю вам другую архитектуру, но для этого вы должны объяснить, что вы хотите делать в своем приложении и что должно быть по делу. Спасибо :)   -  person SALMAN    schedule 22.07.2012
comment
Должна быть активность, которая показывает данные, проходящие через веб-сокет, и предлагает пользователям открывать и закрывать соединение. После открытия соединение должно оставаться открытым, пока пользователь не закроет его. Также должно быть прикрепленное уведомление, чтобы пользователь знал, что соединение все еще открыто.   -  person Chironex    schedule 22.07.2012
comment
хорошо, почему бы вам не сохранить свои данные, которые были записаны. через соединение через веб-сокет в SharedPreference в классе обслуживания, и, следовательно, вы можете собирать эти данные и показывать их в любом действии, которое вы хотите?   -  person SALMAN    schedule 22.07.2012
comment
Полученные данные являются переменными, и использование SharedPreferences для хранения неопределенного количества данных кажется крайне запутанным. Я тоже не хочу использовать базы данных. Мое приложение на данный момент почти полностью функционально, у меня просто возникают проблемы с привязкой службы к нужному экземпляру активности. Я очень хочу узнать о привязке службы Android, и использование SharedPreferences кажется дешевым выходом. (Извините, если это звучит грубо, я не собираюсь этого делать)   -  person Chironex    schedule 22.07.2012


Ответы (1)


«Если вы запускаете службу Android с помощью startService (..), эта служба будет работать до тех пор, пока вы явно не вызовете stopService (..). Есть две причины, по которым служба может запускаться системой. Если кто-то вызывает Context.startService () затем система извлечет службу (создав ее и вызвав ее метод onCreate(), если это необходимо), а затем вызовет ее метод onStartCommand(Intent, int, int) с аргументами, предоставленными клиентом. В этот момент служба будет продолжать работать до тех пор, пока Вызывается Context.stopService() или stopSelf().Обратите внимание, что несколько вызовов Context.startService() не являются вложенными (хотя они приводят к нескольким соответствующим вызовам onStartCommand()), поэтому независимо от того, сколько раз он запускается, служба будет остановлена ​​после вызова Context.stopService() или stopSelf(), однако службы могут использовать свой метод stopSelf(int), чтобы гарантировать, что служба не будет остановлена ​​до тех пор, пока не будут обработаны запущенные намерения.

Клиенты также могут использовать Context.bindService() для получения постоянного подключения к службе. Это также создает службу, если она еще не запущена (вызов onCreate() при этом), но не вызывает onStartCommand(). Клиент получит объект IBinder, который служба возвращает из своего метода onBind(Intent), что позволяет клиенту затем выполнять обратные вызовы службы. Служба будет работать до тех пор, пока установлено соединение (независимо от того, сохраняет ли клиент ссылку на IBinder службы). Обычно возвращаемый IBinder предназначен для сложного интерфейса, написанного на helpl.

Служба может быть как запущена, так и иметь привязанные к ней соединения. В таком случае система будет поддерживать работу службы до тех пор, пока она запущена или к ней есть одно или несколько подключений с флагом Context.BIND_AUTO_CREATE. Если ни одна из этих ситуаций не выполняется, вызывается метод службы onDestroy(), и служба эффективно завершается. Вся очистка (остановка потоков, отмена регистрации получателей) должна быть завершена после возврата из onDestroy()."

Я надеюсь, что это поможет вам выполнить вашу работу.

Спасибо :)

person SALMAN    schedule 22.07.2012