Контакти

Комунікація між Activity і Service. Перемикання між екранами додатка Передача даних між активують

Якось виникла у мене завдання передавати дані з сервісу в активують. Почалися пошуки рішення в стандартному SDK, але так як часу не було, то сваял погане рішення у вигляді використання бази даних. Але питання було відкрито і через деякий час я розібрався з більш вірним способом, який є в SDK - використання класів Message, Handler, Messenger.

ідея

Нам потрібно передавати дані з активують в сервіс і назад. Як нам це зробити? Для вирішення нашої задачі у нас вже є все необхідне. Все що потрібно - це прив'язати сервіс до атівіті, використовуючи bindService, передати потрібні параметри і трохи магії у вигляді використання класів Message. А магія полягає в тому, щоб використовувати змінні екземпляра Message і зокрема, replyTo. Дана змінна потрібна нам, щоб ми могли звернутися до екземпляру Messanger сервісу з активують і в сервісі до примірника Messanger-а активують. Насправді, не так вже й просто. Принаймні для мого не найбільш обдарованого розуму. Частково я просто покращують документацію, яка вже є - Services Також, є гарний приклад на StackOverflow. У будь-якому випадку, сподіваюся стаття буде корисна хоч комусь і я потрудився не дарма.

приклад

Як приклад реалізуємо сервіс, який будемо збільшувати і зменшувати значення лічильника і повертати результат в активують, в TextView. Код макета опущу, бо там дві кнопки і текстове поле - все просто.

Реалізація

Наведу повністю код активують:

Public class MainActivity extends Activity (public static final String TAG \u003d "TestService"; TestServiceConnection testServConn; TextView testTxt; final Messenger messenger \u003d new Messenger (new IncomingHandler ()); Messenger toServiceMessenger; @Override public void onCreate (Bundle savedInstanceState) (super. onCreate (savedInstanceState); setContentView (R.layout.activity_main); testTxt \u003d (TextView) findViewById (R.id.test_txt); bindService (new Intent (this, TestService.class), (testServConn \u003d new TestServiceConnection ()), Context .BIND_AUTO_CREATE);) @Override public void onDestroy () (super.onDestroy (); unbindService (testServConn);) public void countIncrClick (View button) (Message msg \u003d Message.obtain (, TestService.COUNT_PLUS); msg.replyTo \u003d messenger; try (toServiceMessenger.send (msg);) catch (RemoteException e) (e.printStackTrace ();)) public void countDecrClick (View button) (Message msg \u003d Message.obtain (, TestService.COUNT_MINUS); msg .replyTo \u003d Messenger; try (toServiceMessenger.send (msg);) catch (RemoteException e) (e.printStackTrace ();)) private class IncomingHandler extends Handler (@Override public void handleMessage (Message msg) (switch (msg.what) (case TestService. GET_COUNT: Log.d (TAG, "(activity) ... get count"); testTxt.setText ( "" + msg.arg1); break;))) private class TestServiceConnection implements ServiceConnection (@Override public void onServiceConnected (ComponentName name, IBinder service) (toServiceMessenger \u003d new Messenger (service); // відправляємо початкове значення лічильника Message msg \u003d Message.obtain (, TestService.SET_COUNT); msg.replyTo \u003d messenger; msg.arg1 \u003d 0; // наш лічильник try (toServiceMessenger.send (msg);) catch (RemoteException e) (e.printStackTrace ();)) @Override public void onServiceDisconnected (ComponentName name) ()))

Поясню. При створенні активують ми відразу прив'язуємося до сервісу, реалізуючи інтерфейс ServiceConnection і в ньому оговтується повідомлення сервісу «встановити значення лічильника», передаючи нуль і створюючи toServiceMessanger, передаючи в конструктор інтерфейс IBinder. До речі, в сервісі обов'язково потрібно повернути цей екемпляр, інакше буде NPE. За допомогою цього класу ми і відправляємо повідомлення сервісу. І ось вона магія - в змінну replyTo ми зберігаємо наш другий примірник Messenger - той який отримує відповідь від сервера і саме через нього і буде здійснюватися зв'язок з активують.

Для отримання повідомлення від сервісу використовуємо свій Handler і просто шукаємо потрібні нам змінні і робимо по ним дії. За переходами на кнопки (методи countIncrClick, countDecrClick) відправляємо запити до сервісу, вказуючи потрібну дію в змінної msg.what.

Package com.example.servicetest; import android.app.Service; import android.content. *; import android.os. *; import android.os.Process; import android.util.Log; public class TestService extends Service (public static final int COUNT_PLUS \u003d 1; public static final int COUNT_MINUS \u003d 2; public static final int SET_COUNT \u003d 0; public static final int GET_COUNT \u003d 3; int count \u003d 0; IncomingHandler inHandler; Messenger messanger; Messenger toActivityMessenger; @Override public void onCreate () (super.onCreate (); HandlerThread thread \u003d new HandlerThread ( "ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start (); inHandler \u003d new IncomingHandler (thread.getLooper ()); messanger \u003d new Messenger (inHandler);) @Override public IBinder onBind (Intent arg0) (return messanger.getBinder ();) @Override public int onStartCommand (Intent intent, int flags, int startId) (return START_STICKY;) // обробник повідомлень активують private class IncomingHandler extends Handler (public IncomingHandler (Looper looper) (super (looper);) @Override public void handleMessage (Message msg) (//super.handleMessage(msg); toActivityMess enger \u003d msg.replyTo; switch (msg.what) (case SET_COUNT: count \u003d msg.arg1; Log.d (MainActivity.TAG, "(service) ... set count"); break; case COUNT_PLUS: count ++; Log.d (MainActivity.TAG , "(service) ... count plus"); break; case COUNT_MINUS: Log.d (MainActivity.TAG, "(service) ... count minus"); count--; break;) // відправляємо значення лічильника в активують Message outMsg \u003d Message.obtain (inHandler, GET_COUNT); outMsg.arg1 \u003d count; outMsg.replyTo \u003d messanger; try (if (toActivityMessenger! \u003d null) toActivityMessenger.send (outMsg);) catch (RemoteException e) (e.printStackTrace ();))))

Все по аналогії з логікою в активують. Навіть не знаю, чи потрібно щось пояснювати. Єдиний момент - це те, що я відразу відправляю запит назад в активують в handleMessage, використовуючи для цього чарівну змінну replyTo і витягуючи вище потрібний Messenger. І другий момент про який я вже говорив - це:

@Override public IBinder onBind (Intent arg0) (return messanger.getBinder ();)

без якого все впаде. Саме цей екземпляр інтерфейсу і буде переданий в ServiceConnection

висновок

Вцілому все. Такий ось надуманий приклад взаємодії активують і сервісу. Мені здається, досить таки нетривіальне взаємодія, хоча комусь може здатися інакше.

Питання, уточнення та інше в личку. Можуть бути неточності з приводу будь-яких аспектів, тому сміливо пишіть і виправляйте.
Сподіваюся, пост був корисний читачам.

Останнє оновлення: 03.04.2018

Для передачі даних між двома Activity використовується об'єкт Intent. Через його метод putExtra () можна додати ключ і пов'язане з ним значення.

Наприклад, передача з поточної activity в SecondActivity рядки "Hello World" з ключем "hello":

// створення об'єкта Intent для запуску SecondActivity Intent intent \u003d new Intent (this, SecondActivity.class); // передача об'єкта з ключем "hello" і значенням "Hello World" intent.putExtra ( "hello", "Hello World"); // запуск SecondActivity startActivity (intent);

Для передачі даних застосовується метод putExtra (), який в якості значення дозволяє передати дані найпростіших типів - String, int, float, double, long, short, byte, char, масиви цих типів, або об'єкт інтерфейсу Serializable.

Щоб отримати відправлені дані при завантаженні SecondActivity, можна скористатися методом get(), В який передається ключ об'єкта:

Bundle arguments \u003d getIntent (). GetExtras (); String name \u003d arguments.get ( "hello"). ToString (); // Hello World

Залежно від типу даних, що при їх отриманні ми можемо використовувати ряд методів об'єкта Bundle. Всі вони в якості параметра приймають ключ об'єкта. Основні з них:

    get (): універсальний метод, Який повертає значення типу Object. Відповідно поле отримання дане значення необхідно перетворити до потрібного типу

    getString (): повертає об'єкт типу String

    getInt (): повертає значення типу int

    getByte (): повертає значення типу byte

    getChar (): повертає значення типу char

    getShort (): повертає значення типу short

    getLong (): повертає значення типу long

    getFloat (): повертає значення типу float

    getDouble (): повертає значення типу double

    getBoolean (): повертає значення типу boolean

    getCharArray (): повертає масив об'єктів char

    getIntArray (): повертає масив об'єктів int

    getFloatArray (): повертає масив об'єктів float

    getSerializable (): повертає об'єкт інтерфейсу Serializable

Нехай у нас в проекті буде визначено дві activity: MainActivity і SecondActivity.

У коді SecondActivity визначимо отримання даних:

Package com.example.eugene.serializeapp; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; public class SecondActivity extends AppCompatActivity (@Override protected void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); TextView textView \u003d new TextView (this); textView.setTextSize (20); textView.setPadding (16, 16, 16, 16 ); Bundle arguments \u003d getIntent (). getExtras (); if (arguments! \u003d null) (String name \u003d arguments.get ( "name"). toString (); String company \u003d arguments.getString ( "company"); int price \u003d arguments.getInt ( "price"); textView.setText ( "Name:" + name + "\\ nCompany:" + company + "\\ nPrice:" + price);) setContentView (textView);))

В даному випадку в SecondActivity отримуємо все даних з об'єкта Bundle і виводимо їх в текстове поле TextView. Передбачається, що даної activity будуть передаватися три елементи - два рядки з ключами name і company і число з ключем price.

Тепер визначимо передачу в SecondActivity даних. Наприклад, визначимо для MainActivity наступний інтерфейс в файлі activity_main.xml:

Сподобалася стаття? поділіться їй