Денис Колисниченко



Pdf просмотр
страница7/8
Дата05.11.2016
Размер5.05 Kb.
Просмотров1921
Скачиваний0
1   2   3   4   5   6   7   8

Java-код для отображения графического файла будет следующим:
// Задаем имя файла на SD-карте
String fname = "/sdcard/myImages/my_photo.jpg";
// Создаем объект класса File
File imageFile = new File(fname);
// Если файл существует... if(imageFile.exists()){
// Загружаем изображение
Bitmap bmp = BitmapFactory.decodeFile(fname);
// Находим область для отображения картинки
ImageView myImage = (ImageView) findViewById(R.id.imageToShow);
// Устанавливаем картинку myImage.setImageBitmap(myBitmap);
}
Последовательность действий для сохранения изображения будет следующей:
// Это наше изображение, например, полученное с камеры
Bitmap takenPicture;
// Устанавливаем имя файла, в который нужно сохранить изображение
FileOutputStream out = openFileOutput("mypic.png",
Context.MODE_WORLD_WRITEABLE);

Глава 9. Методы хранения данных
179
// Устанавливаем метод сжатия — в нашем случае PNG takenPicture.compress(CompressFormat.PNG, 100, out);
// Сбрасываем содержимое буферов, чтобы убедиться, что
// файл физически записан на SD-карту out.flush();
// Закрываем поток out.close();
9.3. Работа с URI
Не всегда приходится работать с локальными файлами. Иногда файлы нужно за- гружать из Интернета или из других источников. А для этого нужно задать URI файла. Для работы с URI в Android используется класс
Uri
, который мы сейчас и рассмотрим.
Собственно URI состоит из трех частей: схемы, зависимой от схемы части и пути.
Возьмем для примера вот такой URI: http://dkws.org.ua/pages/index.php
. Схемой здесь является http
. Зависимая от схемы часть (она отличается для разных
Uri
) здесь: dkws.org.ua/pages/index.php
. Последняя часть:
/pages
— это и есть путь (об- ратите внимание, что index.php
— это не путь).
У протокола HTTP (как и у протокола FTP) есть свои параметры, но сейчас мы не будем их рассматривать.
У класса
Uri самый важный метод — parse()
. Именно он занимается разбором URI, переданного программистом или пользователем.
С интернет-адресами все ясно. Далее мы рассмотрим несколько нестандартных
URI, которые можно использовать только в Android.
Представим, что нам нужно загрузить видеоролик (формат 3GP) в компонент
VideoView
. С помощью URI можно указать программе, откуда загрузить ролик.
Представим, что ролик находится в ресурсах программы. Тогда
Uri будет следую- щим:
Uri cURI; cURI = Uri.parse("android.resource://your.app.package/" + R.raw.video);
Как видите, в качестве URI может выступать не только интернет-адрес. В нашем случае схема здесь: android.resource
А теперь представим, что видео находится в каталоге video на SD-карте:
Uri cURI; cURI = Uri.parse("file:///sdcard/video/video.3gp");
Далее нужно использовать метод setVideoURI(URI uri)
компонента
VideoView для загрузки видео. Обратите внимание, что этому методу нельзя напрямую передать
URI. Ему нужно передавать объект класса
Uri
, который мы создали ранее! Такое поведение свойственно для многих других компонентов/классов в Android. Непо- средственно URI практически никогда не передаются. Нужно выполнить разбор
Uri

180
Часть II. Базовое программирование для Android с помощью метода parse()
, а затем передать полученный объект для последующей обработки.
Итак, вот код загрузки видео:
Uri cURI; cURI = Uri.parse("file:///sdcard/video/video.3gp");
VideoView videoView; videoView.setURI(cURI);
Но и это еще не все! Метод parse()
можно использовать для парсинга географиче- ских координат, номеров телефонов и даже контактов из адресной книги:
Uri uri = Uri.parse("geo:45.357183,67.65021");
Uri uri = Uri.parse("tel:3334455");
Uri uri = Uri.parse("content://contacts/people/Den");
Подробнее о классе
Uri можно прочитать по адресу: http://developer.android.com/reference/android/net/Uri.html
9.4. Предпочтения: сохранение настроек приложения
Предпочтения — это специальный механизм чтения и записи пар «ключ-значение» для примитивных типов данных. Предпочтения обычно используются для сохране- ния и восстановления параметров приложения — например, имени графической темы (скина), текста приветствия, названия шрифта и т. д.
Информация сохраняется в XML-файле на Android-устройстве. Например, если приложение com.samples.hello создает разделяемое предпочтение, Android создает новый XML-файл в каталоге
/data/data/com.samples.hello/shared_prefs
Получить доступ к предпочтениям можно с помощью метода
Activity. getPreferences()
. Позже мы рассмотрим этот метод, как и метод getSharedPreferences()
Существуют разные типы доступа к предпочтениям:
MODE_PRIVATE
— предпочтение доступно только создавшему его приложению;
MODE_WORLD_READABLE
— предпочтение доступно всем приложениям для чтения;
MODE_WORLD_WRITEABLE
— предпочтение доступно всем приложениям для записи.
Для хранения настроек приложения используются предпочтения четырех типов:
CheckBoxPreference
— позволяет хранить состояние независимого переключа- теля;
EditTextPreference
— самое универсальное предпочтение, позволяет хранить текстовую строку;
ListPreference
— предпочтение списка;
RingtonePreference
— предпочтение рингтона.

Глава 9. Методы хранения данных
181
Чаще всего используется предпочтение
EditTextPreference
, его мы сейчас и рас- смотрим. Начнем с сохранения предпочтений. Для этого применяется следующий
Java-код:
// Создаем объект SharedPreferences
// Определяем название и режим предпочтений
SharedPreferences p = getSharedPreferences("myAppPrefs", MODE_PRIVATE);
// Начинаем редактирование предпочтений
Editor prefEditor = p.edit();
// Сохраняем две пары — username = den, password = 123456 prefEditor.putString("username", "den"); prefEditor.putString("password", "123456"); prefEditor.commit();
Прочитать предпочтения можно методом getString()
:
SharedPreferences p = getSharedPreferences("myAppPrefs", MODE_PRIVATE);
String username = p.getString("username", "");
String password = p.getString("password", "");
Тут как бы все понятно. Вот только, сами понимаете — это еще не все. Впереди огромная часть работы. Во-первых, вам нужно создать для вашего приложения деятельность — окно настроек. Пусть в приложении должно быть меню, а в нем — пункт Settings, при выборе которого и будет отображаться это окно. Во- вторых, вам надо обеспечить загрузку параметров при запуске приложения и со- хранение параметров при закрытии окна настроек. Согласитесь, довольно большой
«кусок» работы.
Однако должен вас обрадовать. Система Android может выполнить всю рутинную работу за вас. Она создаст деятельность с настройками (которая будет выглядеть унифицированно с аналогичными окнами других приложений), она организует за- грузку и сохранение настроек приложения. Вам же останется обеспечить только запуск этой деятельности (например, через пункт меню или кнопку Settings) и чте- ние настроек (ранее было показано, как это сделать), когда это вам необходимо.
Ведь читать настройки нужно не только для их загрузки в окно настроек, но и для организации работы программы. Так, в нашем случае перед подключением к серве- ру потребуются имя пользователя и пароль. Поэтому сначала их надо прочитать, а уже потом использовать. В любом случае система Android возьмет на себя все, что касается обработки окна настроек и сохранения самих настроек, а от вас потребует- ся минимум усилий.
Теперь рассмотрим практический пример. Создайте файл res/xml/preferences.xml
, описывающий предпочтения (листинг 9.3).
Листинг 9.3. Файл res/xml/preferences.xml


182
Часть II. Базовое программирование для Android




В файле описания предпочтений мы объявили два предпочтения с именами username и password
, оба типа
EditTextPreference
. Для пароля мы задали свойство password
= true
, что обеспечит сокрытие символов пароля в окне приложения.
Далее считаем, что наш проект называется com.samples.prefs
. Поэтому в каталог src/com/samples/prefs нужно добавить файл
MyPreferences.java
(листинг 9.4). Этот файл обеспечивает загрузку предпочтений из XML-файла.
Листинг 9.4. Файл src/com/samples/prefs/MyPreferences.java package com.samples.prefs; import android.os.Bundle; import android.preference.PreferenceActivity; public class MyPreferences extends PreferenceActivity {
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences);
}
}
Однако вы не можете просто так добавить файл
MyPreferences.java в каталог src/com/samples/prefs
— для этого вы должны использовать интерфейс Eclipse, иначе среда не увидит «чужой» файл.
Чтобы добавить новый класс, щелкните правой кнопкой мыши на файле com.samples.prefs
(как показано на рис. 9.1) и выберите команду New | Class. Затем в появившемся окне введите имя нового класса:
MyPreferences
(рис. 9.2). После нажа- тия кнопки Finish вы увидите окно текстового редактора, в котором появился код- заглушка для нового класса. Замените этот код на код из листинга 9.4.
Теперь перейдем к редактированию основного файла кода приложения. Основная деятельность (основное окно) должно запустить деятельность
PreferenceActivity в случае необходимости (например, при выборе пункта Settings в меню прило- жения). Для простоты примера в листинге 9.5 мы запускаем деятельность
PreferenceActivity при запуске основной деятельности.

Глава 9. Методы хранения данных
183
Рис. 9.1. Команда добавления нового класса
Листинг 9.5. Запуск деятельности предпочтений package com.samples.prefs; import android.app.Activity; import android.content.Intent; import android.os.Bundle; public class Prefs extends Activity {
/** Called when the activity is first created. */
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main);
// Запускаем окно с настройками
Intent i = new Intent(this, MyPreferences.class); startActivity(i);
}
}

184
Часть II. Базовое программирование для Android
Рис. 9.2. Окно создания нового класса
Практически все. Надо лишь отредактировать файл манифеста, добавив в него све- дения об активности предпочтений (листинг 9.6).
Листинг 9.6. Файл AndroidManifest.xml










Глава 9. Методы хранения данных
185





Осталось только все протестировать. Запустите приложение, и вы увидите окно установки настроек (рис. 9.3). Выберите первый параметр — User Name. Появится диалоговое окно ввода строки — укажите любое имя пользователя (рис. 9.4).

Рис. 9.3. Окно установки параметров приложения

Установите значение второго параметра. Поскольку мы указали, что второй пара- метр — это пароль, все его символы будут закрыты точками (рис. 9.5).
Если нажать в эмуляторе кнопку Назад, то мы увидим само приложение (рис. 9.6).
Теперь проверим, сохранились ли настройки. Для этого закройте окно эмулятора и запустите проект снова на выполнение. После этого выберите первый параметр — появится то же диалоговое окно редактирования параметра, но в нем уже будет введенное ранее значение. Что и требовалось доказать!
На этом заканчивается вторая часть книги. В следующей части мы поговорим о бо- лее сложных материях: службах, доступе к базе данных, подключению к Интернету и т. д. Самое интересное еще впереди!

186
Часть II. Базовое программирование для Android
Рис. 9.4. Окно ввода текстового значения
Рис. 9.5. Ввод пароля

Глава 9. Методы хранения данных
187
Рис. 9.6. Основное окно приложения








188
Часть II. Базовое программирование для Android







ЧАСТЬ
III

Профессиональное программирование
Глава 10. Деятельности и намерения. Передача данных между деятельностями
Глава 11. Потоки, службы и широковещательные приемники
Глава 12. Создание анимации
Глава 13. База данных SQLite
Глава 14. Соединение с внешним миром
Глава 15. Платформа Titanium Mobile
Глава 16. Взаимодействие с аппаратными средствами
Глава 17. Работа с Google Play Маркет
Глава 18. Отладка приложений
















ГЛ А В А
10

Деятельности и намерения.
Передача данных между деятельностями
10.1. Еще раз о компонентах приложения
В главе 4 мы познакомились с компонентами Android-приложения. Настало время взглянуть на них в другом свете — с высоты уже полученных знаний.
Итак, любое приложение может содержать следующие компоненты:
Activity
(дея- тельность),
Service
(служба),
BroadcastReceiver
(приемник широковещательных намерений) и
ContentProvider
(контент-провайдер).
Приложение должно состоять как минимум из одного такого компонента. В самых сложных случаях в приложении будут присутствовать все четыре компонента.
Операционная система (и другие Android-приложения при соответствующих раз- решениях) может вызывать необходимый ей компонент приложения.
Какие компоненты должны быть именно в вашем приложении? Все зависит от са- мого приложения, т. е. от того, что вам нужно в нем сделать.
Если надо организовать интерфейс пользователя (например, получить от поль- зователя ввод), тогда в вашем приложении обязательно должна быть деятель- ность (
Activity
).
При разработке фонового процесса — например, для воспроизведения музыки или фонового обновления деятельности, вам пригодится служба (
Service
).
Для обращения к ресурсам телефона — например, доступа к контактам телефо- на, вам нужен контент-провайдер (
ContentProvider
).
Для получения же широковещательных сообщений (с целью реакции на какие- то события телефона) используется
BroadcastReceiver
Теперь разберемся, что такое намерения. Намерение (
Intent
) — это действие. Ком- поненты приложения вызываются с помощью намерений. Например, вы можете вызвать браузер с помощью намерения:


192
Часть III. Профессиональное программирование
Intent browser = Intent(Intent.ACTION_VIEW); browser.setData(Uri.parse("http://www.dkws.org.ua")); startActivity(browser);
Аналогично, вы можете вызвать компонент своего собственного приложения.
Пусть у вас есть приложение, в котором имеются две (или более) деятельности
(два или более окна). Вызвать вспомогательное окно можно только с помощью на- мерения.
Представим, что вы разрабатываете игру. В вашем приложении будет главное окно
(главная деятельность) и, собственно, деятельность игры. Деятельность игры за- пускается по нажатию кнопки Play в главном окне (или элемента главного меню приложения — без разницы). Обработчик нажатия кнопки будет такой:
Intent startGame = new Intent(this, PlayGame.class); startActivity(startGame);
В нашем проекте должен быть класс
PlayGame
. «Болванка» для этого класса приве- дена в листинге 10.1. Наш класс ничего не делает — он просто находит в файле разметки деятельности кнопку end_game и устанавливает для нее обработчик — функцию finish()
. Напомню, чтобы добавить класс
PlayGame в проект, нужно щелкнуть на проекте (в области Package Explorer) правой кнопкой мыши и вы- брать команду New | Class.
Листинг 10.1. Файл PlayGame.java package com.samples.startgame; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Button; public class PlayGame extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
// Файл разметки называется game.xml setContentView(R.layout.game);
// Обработка кнопки завершения игры
Button endButton = (Button) findViewById(R.id.end_game); endButton.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { finish();
}
});
}
}
Файл разметки для деятельности
PlayGame представлен в листинге 10.2.

Глава 10. Деятельности и намерения. Передача данных между деятельностями
193
Листинг 10.2. Файл разметки для деятельности PlayGame (res/layout/game.xml)

>

194
Часть III. Профессиональное программирование
10.2. Однозадачный режим
Приложение может быть запущено пользователем несколько раз, в результате в памяти устройства окажутся запущенными несколько экземпляров одной и той же деятельности, что приведет к ее перерасходу. Чтобы избежать подобной ситуации, разработчик может контролировать поведение каждой деятельности с помощью файла манифеста. Для каждого элемента

, обладающего intent
- фильтрами
MAIN
и
LAUNCHER
, добавьте следующий параметр: android:launchMode="singleInstance"
Должно получиться примерно так:






В результате будет запущен только один экземпляр деятельности. Но вы можете задать еще более жесткое ограничение. По умолчанию каждая дочерняя деятель- ность запускается как отдельная задача. Чтобы все деятельности запускались как одна задача, используется следующий режим запуска: android:launchMode="singleTask"
10.3. Ориентация экрана
Любое Android-устройство, оснащенное акселерометром, способно определить, в каком положении сейчас находится устройство. В зависимости от показаний ак- селерометра, может изменяться ориентация экрана: с альбомной на портретную
(книжную) и наоборот. Но не всегда это хорошо. Одно дело, если вы разрабатывае- те офисное приложение, но совсем другое, когда разрабатывается игра. В таком случае изменение ориентации экрана может быть не совсем желательным. Но вы можете принудительно задать ориентацию экрана для каждой деятельности. Для этого в файле манифеста в элемент

нужно добавить параметр orientation
: android:screenOrientation="portrait" android:screenOrientation="landscape"
Значение portrait означает портретную (книжную) ориентацию, значение landscape
— альбомную.
Скрыть клавиатуру можно путем добавления следующего параметра: android:configChanges="orientation|keyboardHidden"

Глава 10. Деятельности и намерения. Передача данных между деятельностями
195
Иногда необходимо знать, когда скрыта клавиатура или когда изменена ориентация экрана. Тогда вам нужно переопределить метод onConfigurationChanged()
(лис- тинг 10.4).
Листинг 10.4. Переопределение метода onConfigurationChanged()
@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig);
// Проверяем ориентацию экрана if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
}
// Проверяем видимость клавиатуры if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
Toast.makeText(this, "keyboard visible",
Toast.LENGTH_SHORT).show();
} else if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
Toast.makeText(this, "keyboard hidden",
Toast.LENGTH_SHORT).show();
}
}
10.4. Сохранение и восстановление состояния деятельности
В главе 9 мы рассмотрели способ хранения настроек приложения — предпочтения.
Сейчас мы рассмотрим способ сохранения и восстановления состояния деятель- ности. Перед завершением работы деятельности вызывается функция onSaveInstanceState()
, сохраняющая состояние деятельности. При повторном соз- дании деятельности вызывается функция onRestoreInstanceState()
. Она использу- ется для восстановления состояния деятельности.
Рассмотрим, как реализовать функции onSaveInstanceState()
и onRestoreInstanceState()
(листинг 10.5).
Листинг 10.5. Сохранение и восстановление состояния деятельности
String my_string = "Some text"; float[] my_array = {1.0, 1.2, 1.3};
// Сохранение состояния деятельности
@Override

196
Часть III. Профессиональное программирование protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState);
// Сохраняем связанную информацию outState.putString("str", my_string); outState.putFloatArray("array", my_array);
}
// Восстановление состояния деятельности
@Override public void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState);
// Восстанавливаем информацию my_string = savedInstanceState.getString("str"); my_array = savedInstanceState.getFloatArray("array");
}
10.5. Передача данных между деятельностями
Существует несколько способов передачи данных между деятельностями. Первый заключается в использовании публичных членов класса — примерно так: public int flag;
MainActivity.flag = 0;
В этом способе нет ничего нового, и если вы знаете Java (искренне надеюсь, что это так), то я уверен, вы уже использовали этот способ. Но хочется чего-то, ориентиро- ванного на Android.
Со вторым способом — предпочтениями — вы также уже знакомы из главы 3. Ос- талось только два способа: экстраданные и использование базы данных SQLite.
Сейчас мы узнаем, как задействовать экстраданные, а затем — в главе 13 — будет подробно рассмотрено использование базы данных SQLite.
Ранее был приведен пример, когда одна деятельность вызывает другую:
Intent startGame = new Intent(this, PlayGame.class); startActivity(startGame);
Представим, что нам нужно передать вызываемой деятельности данные. Для этого служит метод putExtra()
: int flag = 0; int coins = 100;
Intent startGame = new Intent(this, PlayGame.class); startGame.putExtra("flag", flag); startGame.putExtra("coins", coins); startActivity(startGame);

Глава 10. Деятельности и намерения. Передача данных между деятельностями
197
Первый параметр метода putExtra()
— это название экстрапеременной, по которо- му можно обратиться к ней с целью получения ее значения. Второй параметр — это переменная, значение которой передается.
Для получения значения экстрапеременной (в коде класса
PlayGame
, файл
PlayGame.java
) вызывается метод getExtra()
:
// Функция getIntent() возвращает намерение, запустившее эту деятельность
Intent i = getIntent();
// Получаем экстрапеременную с именем flag,
// если таковая не найдена, возвращаем значение по умолчанию — 0 flag = i.getIntExtra("flag", 0);
// Получаем экстрапеременную с именем coins, значение по
// умолчанию не задано coins = i.getStringExtra("coins");
На этой ноте завершается десятая глава. В следующей главе мы поговорим о пото- ках, службах и широковещательных приемниках.



ГЛ А В А
11

Потоки, службы и широковещательные приемники
11.1. Потоки
Потоки позволяют выполнять несколько задач одновременно, что дает возмож- ность более эффективно использовать системные ресурсы. Потоки удобно созда- вать для фонового выполнения какой-либо части программы. Самый простой пример многопоточной программы — это музыкальный проигрыватель. Такая про- грамма запускается и отображает список файлов и кнопки управления воспроизве- дением. Вы нажимаете кнопку Play, и запускается отдельный поток — поток вос- произведения композиции. Но ведь программа должна обрабатывать и нажатия других кнопок — например, кнопок Stop и Pause. Иначе бы после нажатия кнопки
Play не было бы возможности остановить, приостановить воспроизведение или пе- реключиться на другую композицию. Благодаря потокам такая возможность у нас есть.
11.1.1. Запуск потока
Представим, что мы создаем тот же музыкальный проигрыватель. В листинге 11.1 представлен обработчик кнопки Play, вызывающий функцию play()
для воспроиз- ведения музыки. Запуск этой функции происходит без создания нового потока.
Листинг 11.1. Версия обработчика кнопки Play (без потоков)
Button playButton = (Button) findViewById(R.id.play); playButton.setOnClickListener(new View.OnClickListener() { public void onClick(View view){ play();
}
});
Как видите, ничего сверхъестественного нет. Просто вызывается функция play()
Теперь реализуем то же самое, но с запуском функции play()
в отдельном потоке.
Первым делом нужно создать поток:


Глава 11. Потоки, службы и широковещательные приемники
199
Thread myThread = new Thread(
// здесь будет описание объекта Runnable
);
Затем описать объект
Runnable
— это делается в конструкторе потока: new Runnable() { public void run() { play();
}
}
Как можно видеть, функция play() вызывается внутри потока.
Наконец, мы запускаем поток: myThread.start();
Полный код создания потока выглядит так:
Thread myThread = new Thread( new Runnable() { public void run() { play();
}
}
); myThread.start();
Что будет делать функция play()
? Все, что нужно сделать в потоке, — т. е. код этой функции зависит от создаваемой программы. В нашем случае будет создан объект класса
MediaPlayer и вызван метод start()
для воспроизведения музыки. Примерно так:
MediaPlayer media = new MediaPlayer(); media = MediaPlayer.create(this, R.raw.music1); media.start();
«Усыпить» на время поток можно методом sleep()
:
// засыпаем на 1 секунду myThread.sleep(1000);
11.1.2. Установка приоритета потока
Для установки приоритета процесса используется метод setPriority()
, который нужно вызвать до метода start()
. Значение приоритета может лежать в диапазоне от
Thread.MIN_PRIORITY (1)
до
Thread.MAX_PRIORITY (10)
: myThread.setPriority(10); myThread.start();

200
Часть III. Профессиональное программирование
11.1.3. Отмена выполнения потока
Мы должны позаботиться и об останове потока. Использовать метод stop()
не ре- комендуется, поскольку он оставляет приложение в неопределенном состоянии.
Правильнее поток myThread завершать так (измените идентификаторы на идентифи- каторы вашего потока): if(myThread != null) {
Thread dummy = myThread; myThread = null; dummy.interrupt();
}
Существует и другой способ. Он заключается в том, что все запускаемые потоки вы объявляете демонами. В этом случае все запущенные потоки будут автоматически завершены при завершении основного потока приложения. На мой взгляд, это са- мый удобный вариант: myThread.setDaemon(true); myThread.start();
11.1.4. Обработчики Runnable-объектов: класс Handler
При сложных вычислениях может понадобиться очередь
Runnable
-объектов. Поме- щая такой объект в очередь, вы можете задать время его запуска, — например, спустя какое-то количество миллисекунд после помещения в очередь, или же ука- зать точное время запуска объекта.
Для демонстрации обработчика потока мы разработаем программу, запускающую фоновый процесс, который будет каждые 200 мс получать текущее время и обнов- лять текстовую надпись в главной деятельности приложения. Кроме того, мы орга- низуем кнопку Start, которую вы сможете нажимать. После первого нажатия надпись «Start» заменяется числом — количеством нажатий кнопки. По нажатию кнопки Start будет выполняться какое-либо действие, в нашем случае — это про- сто изменение надписи на кнопке. В то же время в фоновом режиме наш поток будет обновлять текстовую надпись.
Создайте новый проект. Код разметки приложения приведен в листинге 11.2.
Листинг 11.2. Разметка приложения

>

Глава 11. Потоки, службы и широковещательные приемники
201 android:layout_height="wrap_content" android:text="@string/hello"
/>

202
Часть III. Профессиональное программирование
Button startButton = (Button) findViewById(R.id.start); startButton.setOnClickListener(new View.OnClickListener() {
// Обработчик нажатия кнопки public void onClick(View view){ buttonLabel.setText(++buttonPressed);
}
});
}
// Описание Runnable-объекта — нашего потока private Runnable TimeUpdater = new Runnable() { public void run() {
// "Вычисляем время" final long start = sTime; long millis = SystemClock.uptimeMillis() — start; int sec = (int) (millis / 1000); int min = seconds / 60; seconds = seconds % 60;
// Выводим время myTimeLabel.setText("" + min + ":"
+ String.format("%02d",sec));
// Задержка в 200 мс myHandler.postDelayed(this, 200);
} // void run()
};
@Override protected void onPause() {
// Удаляем Runnable-объект myHandler.removeCallbacks(TimeUpdater); super.onPause();
}
@Override protected void onResume() { super.onResume();
// Добавляем Runnable-объект myHandler.postDelayed(TimeUpdater, 100);
}
}
Кроме метода postDelayed()
вы можете использовать метод postAtTime()
: postAtTime(Runnable r, long uptimeMillis)
В этом случае объект r
добавляется в очередь сообщений, запуск объекта произво- дится во время, заданное вторым параметром (в миллисекундах).
Самый простой способ помещения объекта в очередь — метод post()
, когда указы- вается только помещаемый объект, но не указывается время выполнения объекта: post(Runnable r)

Глава 11. Потоки, службы и широковещательные приемники
203
Подробно об обработчиках потока вы можете прочитать в руководстве разработ- чика: http://developer.android.com/reference/android/os/Handler.html
11.2. Службы
Служба — компонент Android-приложения, запускаемый в фоновом режиме без всякого интерфейса пользователя. Служба может быть запущена или остановлена любым компонентом приложения. Пока служба запущена, любой компонент может воспользоваться предоставляемыми службой возможностями. Приведем несколько примеров использования служб.
Первая деятельность запускает службу, загружающую изображения на веб-сайт.
Вторая деятельность подключается к этой службе с целью узнать, сколько файлов уже загружено, чтобы отобразить эту информацию пользователю.
Другой пример — первая деятельность позволяет пользователю выбрать компози- ции для воспроизведения (реализует список воспроизведения). Воспроизведением композиций занимается служба. Вторая деятельность предоставляет пользователю интерфейс управления воспроизведением: кнопки Play, Stop, Pause и т. д. При на- жатии одной из кнопок происходит обращение к службе для выполнения опреде- ленного действия: остановки воспроизведения, приостановки (паузы) и т. п.
Службы отлично подходят для выполнения постоянных или регулярных операций и для обработки различных событий. Службы запускаются, останавливаются и кон- тролируются разными компонентами приложения, в том числе другими службами, деятельностями (активностями), приемниками широковещательных намерений.
Если вам нужно выполнять задачи, которые не зависят от взаимодействия с пользо- вателем, то службы — это лучший выбор.
У запущенных служб приоритет выше, чем у невидимых деятельностей, поэтому менее вероятно, что служба будет при распределении ресурсов завершена прежде- временно. Даже если такое произойдет, то, в отличие от деятельности, ваша служба автоматически перезапустится, как только окажется достаточно доступных ре- сурсов.
Если служба взаимодействует с пользователем, то нужно повысить ее приоритет до уровня деятельностей, которые работают на переднем плане. Это гарантирует, что служба завершится только в крайнем случае, но при этом ваше приложение может немного подтормаживать, что испортит от него впечатление.
Иногда вместо создания собственной службы проще использовать уже имеющиеся системные службы (табл. 11.1). Все зависит от того, какую задачу вам нужно ре- шить.
Дополнительную информацию об этих службах вы сможете получить по ссылке: http://developer.android.com/.
Жизненный цикл службы изображен на рис. 11.1.

204
Часть III. Профессиональное программирование
Таблица 11.1. Системные службы
Служба Описание
Account Service
Управляет пользовательскими учетными записями
Activity Service
Управляет деятельностями
Alarm Service
Отправляет разовые или периодические оповещения
Clipboard Service
Используется для управления буфером обмена
Connectivity Service
Управляет сетевыми соединениями
Download Service
Управляет загрузками
Input Method Service
Управляет текстовым вводом
Layout Inflater Service
Управляет компоновкой экрана
Location Service
Занимается отслеживанием координат
NFC Service
Управляет NFC
Notification Service
Управляет уведомлениями
Power Service
Управляет энергопотреблением
Search Service
Управляет поиском
Sensor Service
Используется для доступа к датчикам
Telephony Service
Управляет телефонными функциями
Vibrator Service
Управляет доступом к виброзвонку
Wallpaper Service
Управляет обоями домашнего экрана
WiFi Service
Управляет соединениями Wi-Fi
Рассмотрим общий алгоритм создания службы. Первым делом нужно создать класс, расширяющий класс
Service
. В Eclipse для этого надо щелкнуть правой кнопкой мыши на проекте и выбрать команду New | Class. В качестве суперкласса следует указать класс android.app.Service
(рис. 11.2).
По умолчанию будет создана «болванка» класса службы, представленная в листин- ге 11.4.
Листинг 11.4. «Болванка» класса службы package com.samples.my_first_service; import android.app.Service; import android.content.Intent; import android.os.IBinder; public class MyService extends Service {
@Override public IBinder onBind(Intent arg0) {

Глава 11. Потоки, службы и широковещательные приемники
205

// TODO Auto-generated method stub return null;
}
}
В файле манифеста приложения служба описывается так:

Затем элемент

добавляется в элемент

. Пример кода мани- феста представлен в листинге 11.5.
Служба создана с помощью startService()
onStart()
onCreate()
onDestroy()
Служба запущена
Служба остановлена
Работа службы завершена
Служба создана с помощью bindService()
onBind()
onCreate()
onDestroy()
Клиент взаимодействует со службой onUnbind()
Работа службы завершена onRebind()
Рис. 11.1. Жизненный цикл службы
1   2   3   4   5   6   7   8


База данных защищена авторским правом ©nethash.ru 2019
обратиться к администрации

войти | регистрация
    Главная страница


загрузить материал