Комунікація між 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:
Тут визначені три текстових поля для введення даних і кнопка.
У класі MainActivity визначимо наступне вміст:
Package com.example.eugene.serializeapp; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.EditText; public class MainActivity extends AppCompatActivity (@Override protected void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); setContentView (R.layout.activity_main);) public void onClick (View v) (final EditText nameText \u003d findViewById (R.id .name); final EditText companyText \u003d findViewById (R.id.company); final EditText priceText \u003d findViewById (R.id.price); String name \u003d nameText.getText (). toString (); String company \u003d companyText.getText ( ) .toString (); int price \u003d Integer.parseInt (priceText.getText (). toString ()); Intent intent \u003d new Intent (this, SecondActivity.class); intent.putExtra ( "name", name); intent. putExtra ( "company", company); intent.putExtra ( "price", price); startActivity (intent);))
У обробнику натиснення кнопки отримуємо введені в текстові поля EditText дані і передаємо їх в об'єкт Intent за допомогою методу putExtra (). Потім запускаємо SecondActivity.
В результаті при натисканні на кнопку запуститься SecondActivity, яка отримає деякі введені в текстові поля дані.
Передача складних об'єктів
В наведеному вище прикладі передавалися прості дані - числа, рядки. Але також ми можемо передавати складніші дані. У цьому випадку використовується механізм серіалізациі.
Наприклад, нехай у нас в проекті буде визначено клас Product:
Package com.example.eugene.serializeapp; import java.io.Serializable; public class Product implements Serializable (private String name; private String company; private int price; public Product (String name, String company, int price) (this.name \u003d name; this.company \u003d company; this.price \u003d price;) public String getName () (return name;) public void setName (String name) (this.name \u003d name;) public String getCompany () (return company;) public void setCompany (String company) (this.company \u003d company;) public int getPrice () (return price;) public void setPrice (int price) (this.price \u003d price;))
Варто зазначити, що даний клас реалізує інтерфейс Serializable. Тепер змінимо код MainActivity:
Package com.example.eugene.serializeapp; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.EditText; public class MainActivity extends AppCompatActivity (@Override protected void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); setContentView (R.layout.activity_main);) public void onClick (View v) (final EditText nameText \u003d findViewById (R.id .name); final EditText companyText \u003d findViewById (R.id.company); final EditText priceText \u003d findViewById (R.id.price); String name \u003d nameText.getText (). toString (); String company \u003d companyText.getText ( ) .toString (); int price \u003d Integer.parseInt (priceText.getText (). toString ()); Product product \u003d new Product (name, company, price); Intent intent \u003d new Intent (this, SecondActivity.class); intent.putExtra (Product.class.getSimpleName (), product); startActivity (intent);))
Тепер замість трьох розрізнених даних передається один об'єкт Product. Як ключ використовується результат методу Product.class.getSimpleName (), який по суті повертає назву класу.
І змінимо клас 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 (); final Product product; if (arguments! \u003d null) (product \u003d (Product) arguments.getSerializable (Product.class.getSimpleName ()); textView.setText ( "Name: "+ product.getName () +" \\ nCompany: "+ product.getCompany () +" \\ nPrice: "+ String.valueOf (product.getPrice ()));) setContentView (textView);))
Для отримання даних застосовується метод getSerializable (), оскільки клас Product реалізує інтерфейс Serializable. Таким чином, ми можемо передати один єдиний об'єкт замість набору розрізнених даних.
Добрий день.
Необхідно передати дані, отримані через UART в Activity. Це можна зробити, створивши в Activity потік, в якому організувати цикл while (! IsInterrupted ()) і вичитувати дані з буфера UART. Після цього, викликавши UI потік Activity - MainActivity.this.runOnUiThread (new Runnable (), виконати необхідні дії з цією Activity. Але якщо з основної Activity ми викликаємо інші Activity, то організований потік не дозволяє передавати дані в новостворені Activity. Якщо я правильно розумію, то для того щоб дані з потоку можна було передати в будь-яку Activity, потік необхідно створювати не в Activity, а в Service.
Питання: по UART прийшли дані, в потоці (який створений в Servce) необхідно передати дані в Activity, яка зараз є активною, як це можна зробити і так це взагалі робиться?
1 відповідь
У кожній Activity створюєте Handler. У методі onResume () цього Activity робить bindService (). Там одним з параметрів виступає interface ServiceConnection. Імплементіте його хоч тим же Activity. Реалізуєте в ньому метод onServiceConnected (). У цьому callback-е одним з параметрів приходить сам Service. Ось і викличте у цього Service свій власний метод setHandler (). Передайте туди той Handler, який саме в поточному Activity. А ось що приходять дані по UART кидайте в Service на цей Handler. До речі, Handler традиційно працює в головному потоці, тому не потрібно буде runOnUiThread виконувати.
Додаток не завжди складається з одного екрану. Наприклад, ми створили дуже корисну програму і користувачеві хочеться дізнатися, хто ж її автор. Він натискає на кнопку «Про програму» і потрапляє на новий екран, Де знаходиться корисна інформація про версії програми, автора, адресу сайту, скільки у автора котів і т.д. Сприймайте екран активності як веб-сторінку з посиланням на іншу сторінку. Якщо ви подивіться на код в файлі MainActivity.java з минулих уроків, то побачите, що наш клас MainActivity теж відноситься до Activity (Або його спадкоємцям) або, якщо говорити точніше, успадковується від нього.
Public class MainActivity extends AppCompatActivity
Як неважко здогадатися, нам слід створити новий клас, який може бути схожий на MainActivity і потім якось перемкнутися на нього при натисканні кнопки.
Для експерименту ми візьмемо програму з першого уроку і будемо використовувати для дослідів кнопку (або створіть новий проект з однією кнопкою на екрані). Далі створимо нову форму для відображення корисної інформації. Наприклад, покажемо користувачеві, що робить кіт, коли йде наліво і направо. Погодьтеся, це дуже важлива інформація, Що дає ключ до розгадки Всесвіту.
Створювати нову активність будемо вручну, хоча в студії є готові шаблони. Але там нічого складного і для кращого розуміння корисно все робити руками.
Створимо новий XML-файл розмітки activity_about.xml в папці res / layout. Клацніть правою кнопкою миші на папці layout і виберіть з контекстного меню New | Layout resource file. З'явиться діалогове вікно. У першому полі вводимо ім'я файлу activity_about. У другому потрібно ввести кореневий елемент. За замовчуванням там стоїть ConstraintLayout. Стираємо текст і вводимо ScrollView. Введення декількох символів досить, щоб студія підказала готові варіанти, можна відразу натиснути Enter, не чекаючи повного введення слова:
Вийде відповідна заготовка, в яку вставимо елемент TextView.
Інформація буде вилучатись з ресурсів, а саме з строкового ресурсу about_text. Зараз він підсвічений червоним кольором, сигналізуючи про відсутність інформації. Можна було натиснути Alt + Enter і ввести текст в діалоговому вікні. Але для нашого прикладу цей спосіб не підійде, так як наш текст буде багаторядковим, з використанням керуючих символів. Тому зробимо по-іншому. відкриємо файл res / values \u200b\u200b/ strings.xml і вводимо наступний текст вручну:
Ми використовували найпростіші HTML-теги форматування тексту типу , , . Для нашого прикладу досить виділити жирним слова, які відносяться до кота і напрямку руху. Для перекладу тексту на новий рядок використовуйте символи \\ n. Додамо ще один строковий ресурс для заголовка нового екрану:
З розміткою розібралися. Далі необхідно створити клас для вікна AboutActivity.java. Вибираємо в меню File | New | Java Class і заповнюємо потрібні поля. На перших порах досить вказати тільки ім'я. Потім розберетеся з іншими полями.
Отримаємо заготовку.
Зараз клас практично порожній. Додамо код вручну. Клас повинен успадковуватися від абстрактного класу Activity або його родичів типу FragmentActivity, AppCompatActivity і т.д. дописуємо extends Activity. У класу активності повинен бути метод onCreate (). Ставимо курсор миші всередині класу і вибираємо в меню Code | Override Methods (Ctrl + O). У діалоговому вікні шукаємо потрібний клас, можна набирати на клавіатурі перші символи для швидкого пошуку. У створеному методі потрібно викликати метод setContentView (), Який довантажити на екран підготовлену розмітку. У нас вийде такий варіант.
Package ru.alexanderklimov.helloworld; import android.app.Activity; import android.os.Bundle; / ** * Created by Alexander Klimov on 01.12.2014. * / Public class AboutActivity extends Activity (@Override protected void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); setContentView (R.layout.activity_about);))
Тепер починається найголовніше. Наше завдання - перейти на новий екран при натисканні кнопки миші кнопки на першому екрані. Переходимо назад до класу MainActivity. Напишемо обробник клацання кнопки:
Public void onClick (View view) (Intent intent \u003d new Intent (MainActivity.this, AboutActivity.class); startActivity (intent);)
Тут я використав спосіб обробки натискання кнопки, про який розповідалося в занятті.
Для запуску нового екрану необхідно створити екземпляр класу Intent і вказати в першому параметрі поточний клас, а в другому - клас для переходу, у нас це AboutActivity. Після цього викликається метод startActivity (), Який і запускає новий екран.
Якщо ви зараз спробуєте перевірити роботу програми в емуляторі, то отримаєте повідомлення про помилку. Що ми зробили неправильно? Ми пропустили один важливий крок. Необхідно зареєструвати новий Activity в маніфесті AndroidManifest.xml. Знайдіть цей файл в своєму проекті і двічі клацніть на ньому. Відкриється вікно редагування файлу. додайте новий тег
Ось і знадобився строковий ресурс about_title. Запускаємо програму, клацаємо на кнопці і отримуємо вікно Про програму. Таким чином ми навчилися створювати нове вікно і викликати його одним помахом кнопки. А в нашому розпорядженні з'явилася мегаудобная програма - тепер завжди під рукою буде підказка, що робить кіт, коли йде наліво.
Ще раз звертаю увагу, що другий створюваний клас активності повинен успадковуватися від класу Activity або йому схожих ( ListActivity і ін.), мати XML-файл розмітки (якщо потрібно) і бути прописаний в маніфесті.
Після виклику методу startActivity () запуститься нова активність (в даному випадку AboutActivity), Вона стане видимою і переміститься на вершину стека, що містить працюють компоненти. При виклику методу finish () з нової активності (або при натисканні клавіші апаратної повернення) вона буде закрита і видалена з стека. Розробник також може переміщатися до попередньої (або до будь-якої іншої) активності, використовуючи все той же метод startActivity ().
Створюємо третій екран - спосіб для ледачих
Програмісти, як і коти, істоти ліниві. Постійно пам'ятати, що для активності потрібно створити розмітку і клас, який успадковується від Activity, А потім не забути прописати клас в маніфесті - да ну нафиг.
У цьому випадку виберіть в меню File | New | Activity | Basic Activity (Або інший шаблон). Далі з'явиться знайоме вам вікно створення нової активності. Заповнюємо необхідні поля.
Натискаємо на кнопку Finish і активність буде готова. Щоб переконатися в цьому, відкрийте файл маніфесту і перевірте наявність новому записі. Про файли класу і розмітки я вже не кажу, вони самі з'являться перед вами.
самостійно додайте нову кнопку на екрані головної активності і напишіть код для переходу на створену активність.
На перших порах я б порадив вам вручну створювати всі необхідні компоненти для нової активності, щоб розуміти взаємозв'язок між класом, розміткою і маніфестом. А коли наб'єте руку, то можете використовувати майстер створення активності для прискорення роботи.
Передача даних між активностями
Ми використали найпростіший приклад для виклику іншого екрану активності. Іноді потрібно не тільки викликати новий екран, але і передати в нього дані. Наприклад, ім'я користувача. В цьому випадку потрібно задіяти спеціальну область extraData, Який є у класу Intent.
область extraData - це список пар ключ / значення, Який передається разом з наміром. Як ключі використовуються рядки, а для значень можна використовувати будь-які примітивні типи даних, масиви примітивів, об'єкти класу Bundle та ін.
Для передачі даних в іншу активність використовується метод putExtra ():
Intent.putExtra ( "Ключ", "Значення");
Приймаюча активність повинна викликати який-небудь відповідний метод: getIntExtra (), getStringExtra () і т.д.:
Int count \u003d getIntent (). GetIntExtra ( "name", 0);
Переробимо попередній приклад. У нас вже є три активності. У першій активності розмістимо два текстових поля і кнопку. Зовнішній вигляд може бути наступним:
У другій активності SecondActivity встановимо елемент TextView, В якому будемо виводити текст, отриманий від першої активності. Напишемо наступний код для методу onCreate () у другій активності.
@Override protected void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); setContentView (R.layout.activity_second); String user \u003d "животное"; String gift \u003d "дірку від бублика"; TextView infoTextView \u003d (TextView) findViewById ( R.id.textViewInfo); infoTextView.setText (user + ", вам передали" + gift);)
Якщо зараз запустити програму і просто викликати друге вікно, як це було описано в першій частині статті, то ми побачимо напис за замовчуванням Животних, вам передали дірку від бублика. Погодьтеся, досить прикро отримувати такі повідомлення.
Виправляємо ситуацію. Додаємо код у першій активності:
Public void onClick (View view) (EditText userEditText \u003d (EditText) findViewById (R.id.editTextUser); EditText giftEditText \u003d (EditText) findViewById (R.id.editTextGift); Intent intent \u003d new Intent (MainActivity.this, SecondActivity. class); // в ключ username пхає текст з першого текстового поля intent.putExtra ( "username", userEditText.getText (). toString ()); // в ключ gift пхає текст з другого текстового поля intent.putExtra ( "gift ", giftEditText.getText (). toString ()); startActivity (intent);)
Ми помістили в спеціальний контейнер об'єкта Intent два ключа зі значеннями, які беруться з текстових полів. Коли користувач введе дані в текстові поля, вони потраплять в цей контейнер і будуть передані другій активності.
Друга активність повинна бути готова до теплого прийому повідомлень наступним чином (виділено жирним).
// Значення за умовчанням String user \u003d "животное"; String gift \u003d "дірку від бублика"; user \u003d getIntent (). getExtras (). getString ( "username"); gift \u003d getIntent (). getExtras (). getString ( "gift"); TextView infoTextView \u003d (TextView) findViewById (R.id.textViewInfo); infoTextView.setText (user + ", вам передали" + gift);
Тепер повідомлення виглядає не настільки образливим, а навіть приємним для декого. У складних прикладах бажано додати перевірку при обробці даних. Можливі ситуації, коли ви запустите другу активність з порожніми даними типу null, Що може привести до краху програми.
У нашому випадку ми знаємо, що чекаємо строкове значення, тому код можна переписати так:
Intent intent \u003d getIntent (); user \u003d intent.getStringExtra ( "username");
User \u003d getIntent (). GetStringExtra ( "username");
У програми є недолік - не зрозуміло, від кого ми отримуємо привіти. Будь-яка добре вихована мавпа не візьме подарунок від анонімного джерела. Тому в якості домашнього завдання додайте ще одне текстове поле для введення імені користувача, який відправляє повідомлення.
Google рекомендує для ключів використовувати наступний формат: ім'я вашого пакета в якості префікса, а потім сам ключ. В цьому випадку можна бути впевненим в унікальності ключа при взаємодії з іншими додатками. Приблизно так:
Public final static String USER \u003d "ru.alexanderklimov.myapp.USER";
Хто підставив кота Ваську - отримуємо результат назад
Не завжди буває достатньо просто передати дані іншої активності. Іноді потрібно отримати інформацію назад від іншої активності при її закритті. Якщо раніше ми використовували метод startActivity (Intent intent), То існує родинний йому метод startActivityForResult (Intent intent, int RequestCode). Різниця між методами полягає в додатковому параметрі RequestCode. По суті це просто ціле число, яке ви можете самі придумати. Воно потрібне для того, щоб відрізняти від кого прийшов результат. Припустимо у вас є п'ять додаткових екранів і ви привласнюєте їм значення від 1 до 5, і за цим кодом ви зможете визначити, чий результат вам потрібно обробляти. Ви можете використовувати значення -1, тоді це буде рівносильно виклику методу startActivity (), Тобто ніякого результату не отримаємо.
Якщо ви використовуєте метод startActivityForResult (), То вам необхідно перевизначити в коді метод для прийому результату onActivityResult () і обробити отриманий результат. Заплуталися? Давайте розберемо приклад.
Припустимо, ви сищик. Надійшла інформація, що в ресторані зі стола впливової людини вкрали два шматочки ковбаси і інші продукти. Підозра лягла на трьох підозрюваних - ворона, сраний песик і кіт Васька.
Один з відвідувачів надав серію фотографій зі свого понтово айфона:
Також є свідчення іншого свідка: А Васька слухає, та їсть.
Створюємо новий проект Sherlock з двома активностями. На першому екрані буде кнопка для перемикання на другий екран і текстова мітка, в якій буде відображено ім'я злодюжки.
На другому екрані буде група перемикачів:
Так як ми будемо очікувати відповідь з другого екрану, то нам необхідно задіяти метод startActivityForResult () на першому екрані, в якому ми передамо змінну CHOOSE_THIEF в якості параметра RequestCode.
Static final private int CHOOSE_THIEF \u003d 0; public void onClick (View v) (Intent questionIntent \u003d new Intent (MainActivity.this, ChooseActivity.class); startActivityForResult (questionIntent, CHOOSE_THIEF);)
Подивіться на код. При натисканні на кнопці ми збираємося працювати з другим екраном ChooseActivity і запускаємо другий екран з очікуванням результату.
Переходимо на другий екран і будемо писати код для другої активності.
Public final static String THIEF \u003d "ru.alexanderklimov.sherlock.THIEF"; public void onRadioClick (View v) (Intent answerIntent \u003d new Intent (); switch (v.getId ()) (case R.id.radioDog: answerIntent.putExtra (THIEF, "Сраний песик"); break; case R.id .radioCrow: answerIntent.putExtra (THIEF, "Ворона"); break; case R.id.radioCat: answerIntent.putExtra (THIEF, "Кінь Пржевальського"); break; default: break;) setResult (RESULT_OK, answerIntent); finish ();)
Тут все просто, коли сищик вибирає ім'я злочинця, то через метод putExtra () ми передаємо ім'я ключа і його значення.
Для зручності, після вибору ми відразу закриваємо друге вікно і перед закриттям передаємо значення RESULT_OK, Щоб було зрозуміло, що вибір зроблено. Якщо користувач закриє екран через кнопку Back, то буде передано значення RESULT_CANCELED.
метод setResult () приймає два параметри: результуючий код і сам результат, представлений у вигляді наміру. Результуючий код говорить про те, з яким результатом завершилася робота активності, як правило, це або Activity.RESULT_OK, або Activity.RESULT_CANCELED. У деяких випадках потрібно використовувати власний код повернення для обробки специфічних для вашого застосування варіантів. метод setResult () підтримує будь цілочисельне значення.
Якщо ви будете передавати дані явно через кнопку, то непогано б додати метод finish (), Щоб закрити другу активність за непотрібністю. Якщо перехід відбувається через кнопку Назад, то це робити не обов'язково.
Якщо активність була закрита користувачем при натисканні апаратної кнопки повернення або якщо метод finish () був викликаний раніше, ніж метод setResult (), Результуючий код встановиться в RESULT_CANCELED, А повернене намір покаже значення null.
Повертаємося на перший екран. Перший екран очікує відповіді від другого екрану, тому потрібно додати в код метод onActivityResult ().
@Override protected void onActivityResult (int requestCode, int resultCode, Intent data) (super.onActivityResult (requestCode, resultCode, data); TextView infoTextView \u003d (TextView) findViewById (R.id.textViewInfo); if (requestCode \u003d\u003d CHOOSE_THIEF) ( if (resultCode \u003d\u003d RESULT_OK) (String thiefname \u003d data.getStringExtra (ChooseActivity.THIEF); infoTextView.setText (thiefname);) else (infoTextView.setText ( ""); // стираємо текст)))
Метод очікує вхідні дані з кодом CHOOSE_THIEF, І якщо такі дані надійдуть, то витягує значення з ключа ChooseActivity.THIEF за допомогою методу getStringExtra. Отримане значення ми виводимо в TextView (змінна infoTextView). Якщо ми повернулися на екран через кнопку Back, то просто стираємо текст.
При закритті дочірньої активності всередині батьківського компонента спрацьовує обробник onActivityResult (). оброблювач onActivityResult () приймає кілька параметрів.
- Код запиту. Код, який використовувався для запуску активності, яка повертає результат
- Результуючий код. Код результату, який встановлюється дочірньої активністю і в якому зазначено, як завершилася її робота. Це може бути будь-який цілочисельне значення, але, як правило, або Activity.RESULT_OK, або Activity.RESULT_CANCELED
- Дані. Намір, що використовується для упаковки повертаються даних. Залежно від призначення дочірньої активності воно може включати шлях URI, який представляє обрану частину вмісту. В якості альтернативи (або доповнення) дочірня активність може повертати інформацію у вигляді простих значень, упакованих в параметр наміри extras
Якщо робота дочірньої активності завершилася непередбачено або якщо перед її закриттям не було зазначено код результату, цей параметр стане дорівнює Activity.RESULT_CANCELED.
Запускаємо проект, натискаємо на кнопку і переходимо на другий екран. Там вибираємо один з варіантів. Якщо вибрати ворону, то екран закриється і ім'я злочинця відобразиться на першому екрані. Якщо вибрати песика, то відобразиться його ім'я.
Між іншим, якщо вибрати котика, то його ім'я не відобразиться! Перевірте і переконайтеся самі. Ви запитаєте чому? Елементарно, Ватсон! Злочинець не врахував одну важливу деталь. У ресторані велося спостереження з відеокамер, і запис показала, хто насправді вкрав ковбаску і підставив кота. Васька, тримайся!
P.S. Якщо спочатку щось здалося незрозумілим, то з практикою багато що проясниться. Передача даних між екранами часто зустрічається в додатках і ви ще не раз вивчіть приклад.
P.P.S. Краща риба - ковбаса. Знаючи цю слабкість, неважко було підставити кота.
використовуємо фільтри
У статті я показував поширений спосіб переходу на іншу активність, коли в методі startActivity () вказується поточний клас і клас для переходу. До речі, клас активності не обов'язково повинен бути частиною вашої програми. Якщо ви знаєте ім'я класу з іншої програми, то можете перейти і на нього. Але можна перейти в іншу активність іншим способом.
На практиці зустрічається рідше, але може стати в нагоді. Припустимо, у вас вже є друга активність. У маніфесті додамо до неї спеціальний фільтр:
І запускаємо другу активність через клацання кнопки таким способом.
Public void onClick (View view) (startActivity (new Intent ( "ru.alexanderklimov.testapplication.SecondActivity"));)
замінимо довгий рядок на константу.
Public static final String ACTION_SECOND_ACTIVITY \u003d "ru.alexanderklimov.testapplication.SecondActivity"; public void onClick (View view) (startActivity (new Intent (ACTION_SECOND_ACTIVITY));)
Отже, що ми зробили. Для другої активності ми прописали фільтр і вказали ім'я для action в атрибуті android: name. Для зручності я просто помістив в нього повне ім'я активності з назвою пакета. конструктор класу Intent має кілька перевантажених версій. В одній з версій можна вказати рядок для дії. Ми вказали своє створене дію, яке прописано у другій активності. Система під час роботи переглядає маніфести всіх встановлених додатків. При пошуку відповідності система знаходить наш фільтр і запускає потрібну активність.
За таким же принципом можна запустити інші активності. Подивіться на приклад. Якщо ви скопіюєте приклад до себе і подивіться на документацію по android.provider.Settings.ACTION_AIRPLANE_MODE_SETTINGS, То побачите, що цим кодом відповідає строкова константа public static final java.lang.String ACTION_AIRPLANE_MODE_SETTINGS \u003d "android.settings.AIRPLANE_MODE_SETTINGS". Порівняйте з нашим кодом. Ви можете припустити, що у активності налаштувань для автономного режиму в фільтрі прописана ця рядок.
Ім'я категорії фільтра android.intent.category.DEFAULT говорить системі, що слід виконати дію за замовчуванням, а саме, запустити активність. Існує й інші імена, які поки нас не цікавлять.
А тепер питання на засипку. Що станеться, якщо створити ще одну активність і вказати такий же фільтр, як у другій активності? А давайте перевіримо. Створіть у себе третю активність і скопіюйте блок з фільтром від другої активності в нього.
Клацаємо по кнопці в першій активності. Система попросить вибрати потрібний варіант.
Якщо ви оберете пункт ALWAYS, То в наступний раз вибирати не доведеться. Щоб скинути вибір, зайдіть в властивості додатка в Настройках і знайдіть кнопку Clear defaults.
Запуск активності по його імені
У конструкторі Intent другим параметром йде клас. Але припустимо, що є якась база даних, де вказані імена активностей і нам потрібно запустити потрібну активність по його імені. Ми можемо на основі строкової змінної отримати сам клас і запустити активність.
Try (// Повна назва класу активності String activityName \u003d "ru.alexanderklimov.testapplication.SecondActivity"; // отримаємо об'єкт Class Class> myClass \u003d Class.forName (activityName); Intent intent \u003d new Intent (this, myClass); startActivity (intent); ) Catch (ClassNotFoundException e) (e.printStackTrace ();)