Главная страница
Образовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей

Программирование под Android. Для профессионалов


Скачать 19.35 Mb.
НазваниеПрограммирование под Android. Для профессионалов
АнкорBrayn_Khardi_Bill_Fillips_-_Programmirovanie_po.
Дата23.05.2017
Размер19.35 Mb.
Формат файлаpdf
Имя файлаBrayn_Khardi_Bill_Fillips_-_Programmirovanie_po.pdf
оригинальный pdf просмотр
ТипДокументы
#21061
страница50 из 55
КаталогОбразовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей
Образовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей
1   ...   47   48   49   50   51   52   53   54   55
Глава 33. Отслеживание местоположения устройства
Запрашивая информацию местоположения с использованием
PendingIntent
, вы фактически приказываете
LocationManager отправлять вам некую разновидность
Intent в будущем. Таким образом, компоненты приложения (и даже весь процесс) могут прекратить существование, а
LocationManager будет доставлять интенты, пока вы не прикажете ему остановиться, запустив новые компоненты, которые от- реагируют на интенты нужным образом. Например, такая схема позволит предот- вратить поглощение приложением лишних ресурсов, в то время как оно активно отслеживает местоположение устройства.
Чтобы управлять взаимодействием с
LocationManager
, а также дополнительной информацией о текущей серии (см. далее), создайте синглетный класс
RunManager
, приведенный в листинге 33.4.
Листинг 33.4. Заготовка синглетного класса RunManager (RunManager.java)
public class RunManager {
private static final String TAG = "RunManager";
public static final String ACTION_LOCATION =
"com.bignerdranch.android.runtracker.ACTION_LOCATION";
private static RunManager sRunManager;
private Context mAppContext;
private LocationManager mLocationManager;
// Закрытый конструктор заставляет использовать
// RunManager.get(Context)
private RunManager(Context appContext) {
mAppContext = appContext;
mLocationManager = (LocationManager)mAppContext
.getSystemService(Context.LOCATION_SERVICE);
}
public static RunManager get(Context c) {
if (sRunManager == null) {
// Использование контекста приложения для предотвращения
// утечки активностей
sRunManager = new RunManager(c.getApplicationContext());
}
return sRunManager;
}
private PendingIntent getLocationPendingIntent(boolean shouldCreate) {
Intent broadcast = new Intent(ACTION_LOCATION);
int flags = shouldCreate ? 0 : PendingIntent.FLAG_NO_CREATE;
return PendingIntent.getBroadcast(mAppContext, 0, broadcast, flags);
}
public void startLocationUpdates() {
String provider = LocationManager.GPS_PROVIDER;
// Запуск обновлений из LocationManager
PendingIntent pi = getLocationPendingIntent(true);
mLocationManager.requestLocationUpdates(provider, 0, 0, pi);
}

Получение широковещательных обновлений местоположения
537
public void stopLocationUpdates() {
PendingIntent pi = getLocationPendingIntent(false);
if (pi != null) {
mLocationManager.removeUpdates(pi);
pi.cancel();
}
}
public boolean isTrackingRun() {
return getLocationPendingIntent(false) != null;
}
}
Обратите внимание: класс
RunManager содержит три открытых метода экземпляров, образующих его базовый API. Он может запускать обновления данных местопо- ложения, останавливать их и сообщать, отслеживается ли в настоящее время се- рия (а проще говоря, что текущие обновления регистрируются с использованием
LocationManager
).
В startLocationUpdates()
мы приказываем
LocationManager передавать обновлен- ную информацию местоположения через поставщика данных GPS как можно чаще.
Методу requestLocationUpdates(String,
long,
float,
PendingIntent)
в параметрах передается минимальная продолжительность ожидания (в миллисекундах) и ми- нимальное смещение (в метрах) перед отправкой следующего обновления.
Эти параметры должны быть настроены на максимальное значение, которое вы- держит ваше приложение без ухудшения качества обслуживания. В RunTracker пользователь хочет точно знать, где он находится и где он находился прежде, с максимально возможной точностью. Вот почему мы жестко запрограммировали поставщика данных GPS и запросили максимальную частоту обновлений.
С другой стороны, если приложению достаточно хотя бы в общих чертах пред- ставлять, где находится пользователь, большие значения сработают нормально и сэкономят заряд батареи.
Закрытый метод getLocationPendingIntent(boolean)
создает объект
Intent
, который будет рассылаться при обновлении местоположения. Имя действия идентифицирует событие в нашем приложении, а аргумент shouldCreate сообщает
PendingIntent.
getBroadcast(…)
(посредством флагов), нужно ли создавать новый экземпляр
PendingIntent в системе или нет.
Таким образом, реализация метода isTrackingRun()
— вызов getLocationPen- dingIntent(false)
и проверка результата на null
— определяет, зарегистрирован ли объект
PendingIntent в ОС.
Получение широковещательных обновлений
местоположения
Итак, у нас есть код запроса обновлений местоположения в форме широковещатель- ных интентов. Теперь необходимо реализовать механизм их получения. Приложение
RunTracker должно быть способно получать обновления независимо от наличия

538
Глава 33. Отслеживание местоположения устройства или отсутствия, поэтому лучшим местом для их обработки будет автономная реа- лизация
BroadcastReceiver
, зарегистрированная в манифесте.
Чтобы не усложнять код, мы создадим класс
LocationReceiver для регистрации полученных данных местоположения.
Листинг 33.5. Базовая реализация LocationReceiver (LocationReceiver.java)
public class LocationReceiver extends BroadcastReceiver {
private static final String TAG = "LocationReceiver";
@Override
public void onReceive(Context context, Intent intent) {
// Если имеется дополнение Location, использовать его
Location loc = (Location)intent
.getParcelableExtra(LocationManager.KEY_LOCATION_CHANGED);
if (loc != null) {
onLocationReceived(context, loc);
return;
}
// Если мы попали в эту точку, произошло что-то другое
if (intent.hasExtra(LocationManager.KEY_PROVIDER_ENABLED)) {
boolean enabled = intent
.getBooleanExtra(LocationManager.KEY_PROVIDER_ENABLED, false);
onProviderEnabledChanged(enabled);
}
}
protected void onLocationReceived(Context context, Location loc) {
Log.d(TAG, this + " Got location from " + loc.getProvider() + ": "
+ loc.getLatitude() + ", " + loc.getLongitude());
}
protected void onProviderEnabledChanged(boolean enabled) {
Log.d(TAG, "Provider " + (enabled ? "enabled" : "disabled"));
}
}
Как видно из реализации onReceive(Context,
Intent)
,
LocationManager упако- вывает в интент некоторые дополнения с полезной информацией. Ключ
Loca- tionManager.KEY_LOCATION_CHANGED
может задавать экземпляр
Location
, пред- ставляющий последнее обновление. Если он возвращается, мы вызываем метод onLocationReceived(Context,
Location)
для вывода в журнале имени поставщика данных, широты и долготы.
LocationManager также может передавать дополнение логического типа с ключом
KEY_PROVIDER_ENABLED
; в этом случае вызов onProviderEnabled(boolean)
регистри- рует этот факт. Вскоре мы субклассируем
LocationReceiver
, чтобы эти два метода решали более полезные задачи.
Добавьте запись
LocationReceiver в манифест приложения RunTracker. Заодно добавьте разрешение
ACCESS_FINE_LOCATION
и элемент uses-feature для оборудо- вания GPS.

Обновление пользовательского интерфейса данными местоположения
539
Листинг 33.6. Добавление разрешения на получение данных местонахождения
(AndroidManifest.xml)
package="com.bignerdranch.android.runtracker"
android:versionCode="1"
android:versionName="1.0">



android:name="android.hardware.location.gps"/>
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:theme="@style/AppTheme">
android:label="@string/app_name">






android:exported="false">






У нас имеется весь служебный код, необходимый для запроса и получения об- новленных данных местоположения. Остается организовать пользовательский интерфейс для запуска, остановки и вывода этих данных.
Обновление пользовательского интерфейса
данными местоположения
Чтобы убедиться, что все работает как положено, добавьте для кнопок
Start и
Stop слушателей щелчков, взаимодействующих с
RunManager
. Также добавьте вызовы простого метода updateUI()
Листинг 33.7. Запуск и остановка обновлений местоположения (RunFragment.java)
public class RunFragment extends Fragment {
private RunManager mRunManager;
private Button mStartButton, mStopButton;
продолжение


540
Глава 33. Отслеживание местоположения устройства
Листинг 33.7 (продолжение)
private TextView mStartedTextView, mLatitudeTextView, mLongitudeTextView, mAltitudeTextView, mDurationTextView;
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mRunManager = RunManager.get(getActivity());
}
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mStartButton = (Button)view.findViewById(R.id.run_startButton);
mStartButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRunManager.startLocationUpdates();
updateUI();
}
});
mStopButton = (Button)view.findViewById(R.id.run_stopButton);
mStopButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRunManager.stopLocationUpdates();
updateUI();
}
});
updateUI();
return view;
}
private void updateUI() {
boolean started = mRunManager.isTrackingRun();
mStartButton.setEnabled(!started);
mStopButton.setEnabled(started);
}
}
Если запустить RunTracker с этими добавлениями, вы увидите, как в данных LogCat появляются обновленные данные местоположения.
Для получения улучшенных результатов используйте окно
Emulator
Control в DDMS для отправки фиктивных обновлений эмулятору или выйдите с устройством на улицу и дождитесь подключения к GPS. Возможно, вам придется несколько ми- нут ожидать первого обновления. Если вам не терпится увидеть результаты или вы заперты в секретном подземном бункере для программистов, через несколько

Обновление пользовательского интерфейса данными местоположения
541
страниц будет приведена более подробная информация о передаче тестовых данных местоположения.
Вывод данных в LogCat — не самый удобный способ получения информации ме- стоположения. Чтобы вывести данные на экран, реализуйте в
RunFragment субкласс
LocationReceiver
, который будет сохранять
Location и обновлять пользовательский интерфейс. Дополнительные данные, хранимые в экземпляре
Run
, позволят вывести начальную дату и продолжительность текущей серии. Начните с реализации про- стого класса
Run
, который хранит начальную дату, а также умеет вычислять свою продолжительность и форматировать ее в строковом виде.
Листинг 33.8. Класс Run (Run.java)
public class Run {
private Date mStartDate;
public Run() {
mStartDate = new Date();
}
public Date getStartDate() {
return mStartDate;
}
public void setStartDate(Date startDate) {
mStartDate = startDate;
}
public int getDurationSeconds(long endMillis) {
return (int)((endMillis - mStartDate.getTime()) / 1000);
}
public static String formatDuration(int durationSeconds) {
int seconds = durationSeconds % 60;
int minutes = ((durationSeconds - seconds) / 60) % 60;
int hours = (durationSeconds - (minutes * 60) - seconds) / 3600;
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
}
}
Используем класс
Run в сочетании с обновлениями
RunFragment
Листинг 33.9. Вывод обновленных данных местоположения (RunFragment.java)
public class RunFragment extends Fragment {
private BroadcastReceiver mLocationReceiver = new LocationReceiver() {
@Override
protected void onLocationReceived(Context context, Location loc) {
mLastLocation = loc;
if (isVisible())
updateUI();
}
продолжение


542
Глава 33. Отслеживание местоположения устройства
Листинг 33.9 (продолжение)
@Override
protected void onProviderEnabledChanged(boolean enabled) {
int toastText = enabled ? R.string.gps_enabled : R.string.gps_disabled;
Toast.makeText(getActivity(), toastText, Toast.LENGTH_LONG).show();
}
};
private RunManager mRunManager;
private Run mRun;
private Location mLastLocation;
private Button mStartButton, mStopButton;
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mStartButton = (Button)view.findViewById(R.id.run_startButton);
mStartButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
mRunManager.startLocationUpdates();
mRun = new Run();
updateUI();
}
});
}
@Override
public void onStart() {
super.onStart();
getActivity().registerReceiver(mLocationReceiver,
new IntentFilter(RunManager.ACTION_LOCATION));
}
@Override
public void onStop() {
getActivity().unregisterReceiver(mLocationReceiver);
super.onStop();
}
private void updateUI() {
boolean started = mRunManager.isTrackingRun();
if (mRun != null)
mStartedTextView.setText(mRun.getStartDate().toString());
int durationSeconds = 0;
if (mRun != null && mLastLocation != null) {
durationSeconds = mRun.getDurationSeconds(mLastLocation.getTime());

Ускорение отклика: последнее известное местоположение
543
mLatitudeTextView.setText(Double.toString(mLastLocation.getLatitude()));
mLongitudeTextView.setText(Double.toString(mLastLocation.getLongitude()));
mAltitudeTextView.setText(Double.toString(mLastLocation.getAltitude()));
}
mDurationTextView.setText(Run.formatDuration(durationSeconds));
mStartButton.setEnabled(!started);
mStopButton.setEnabled(started);
}
}
Прежде всего обратите внимание на новые переменные экземпляров для
Run и по- следнего полученного объекта
Location
. Эти данные используются при выполнении обнулений пользовательского интерфейса, выполняемых в updateUI()
. Объект
Run инициализируется при включении обновления данных местоположения.
Мы создаем анонимный класс
LocationReceiver и присваиваем его mLocationRe- ceiver
, чтобы сохранить полученную позицию и обновить пользовательский ин- терфейс. Также на экране отображается сообщение с информацией о том, включен поставщик данных GPS или нет.
Наконец, реализации методов onStart()
и onStop()
используются для регистрации и отмены регистрации получателя в сочетании с фрагментом, видимым пользова- телю. Эти операции также могут выполняться в методах onCreate(Bundle)
и onDe- stroy()
, чтобы переменная mLastLocation всегда содержала последнее обновление местоположения, даже если в момент его получения фрагмент находился вне экрана.
Снова запустите приложение RunTracker. На этот раз пользовательский интерфейс должен заполняться реальными данными местоположения.
Ускорение отклика: последнее известное
местоположение
В некоторых ситуациях пользователю совершенно не хочется несколько минут ждать, пока ваше устройство свяжется с таинственными спутниками в космосе, чтобы узнать, где он находится. К счастью, мы можем избавить его от ожидания, используя последнее известное местоположение
LocationManager для любого по- ставщика позиционных данных.
В нашем приложении используется только поставщик GPS, а его последнее ме- стоположение запрашивается достаточно элементарно. Остается лишь как-то передать информацию в пользовательский интерфейс, но для этого можно просто использовать широковещательную рассылку
Intent как от лица
LocationManager
Листинг 33.10. Получение последнего известного местоположения (RunManager.java)
public void startLocationUpdates() {
String provider = LocationManager.GPS_PROVIDER;
// Получение последнего известного местоположения
// и его рассылка (если данные доступны).
продолжение


544
Глава 33. Отслеживание местоположения устройства
Листинг 33.10 (продолжение)
Location lastKnown = mLocationManager.getLastKnownLocation(provider);
if (lastKnown != null) {
// Время инициализируется текущим значением
lastKnown.setTime(System.currentTimeMillis());
broadcastLocation(lastKnown);
}
// Запуск получения обновлений от LocationManager
PendingIntent pi = getLocationPendingIntent(true);
mLocationManager.requestLocationUpdates(provider, 0, 0, pi);
}
private void broadcastLocation(Location location) {
Intent broadcast = new Intent(ACTION_LOCATION);
broadcast.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
mAppContext.sendBroadcast(broadcast);
}
Обратите внимание на сброс временной метки местоположения, полученного от поставщика данных GPS. Возможно, это именно то, что нужно пользователю — а может, и нет; мы оставляем это решение читателю для самостоятельной работы.
У объекта
LocationManager можно запросить данные последнего известного место- положения для любого известного ему поставщика. Также можно запросить список всех известных поставщиков методом getAllProviders()
. Например, вы можете перебрать все известные последние местоположения, проверить их точность и по временной метке определить самые свежие данные. Тем самым предотвращается использование устаревших данных.
Тестирование на реальных
и виртуальных устройствах
Тестирование таких приложений, как RunTracker, может быть непростым делом даже для опытного программиста. Нужно позаботиться о том, чтобы позиционные данные, полученные от системы, правильно отслеживались и сохранялись. Пере- мещения усложняют эту задачу даже на небольших скоростях.
В подобных ситуациях можно передавать
LocationManager тестовые данные, ими- тирующие нахождение устройства в заданном месте.
Для этого проще всего воспользоваться окном
Emulator
Control в DDMS. Данное реше- ние работает только для виртуальных устройств, но оно позволяет задавать новые данные местоположения либо вручную (по одному), либо в формате файла GPX или KML с серией точек, которые посещались за некоторый промежуток времени.
Для тестирования системы определения местоположения на реальном устройстве придется дополнительно потрудиться, но и это вполне возможно. Основная схема выглядит так.
1. Запросить разрешение
ACCESS_MOCK_LOCATION

Тестирование на реальных и виртуальных устройствах
545
2. Добавить тестового поставщика данных методом
LocationManager.addTest-
Provider(…)
3. Разрешить использование поставщика методом setTestProviderEnabled(…)
4. Задать исходный статус поставщика методом setTestProviderStatus(…)
5. Опубликовать данные местоположения методом setTestProviderLocation(…)
6. Удалить тестового поставщика данных методом removeTestProvider(…)
К счастью, мы уже выполнили всю черную работу за вас. На сайте Big Nerd Ranch размещен простой проект TestProvider, который вы можете загрузить, установить на своем устройстве и запустить для управления поставщиком тестовых данных.
Загрузите репозиторий Android Course Resources из хранилища Github по адресу
https://github.com/bignerdranch/AndroidCourseResources
и импортируйте каталог
TestProvider как проект в Eclipse.
Чтобы в приложении RunTracker вместо GPS использовался новый поставщик тестовых данных, необходимо внести изменения, представленные в следующем листинге.
Листинг 33.11. Использование тестового поставщика (RunManager.java)
public class RunManager {
private static final String TAG = "RunManager";
public static final String ACTION_LOCATION =
"com.bignerdranch.android.runtracker.ACTION_LOCATION";
private static final String TEST_PROVIDER = "TEST_PROVIDER";
private static RunManager sRunManager;
private Context mAppContext;
private LocationManager mLocationManager;
public void startLocationUpdates() {
String provider = LocationManager.GPS_PROVIDER;
// Если имеется поставщик тестовых данных и он активен,
// использовать его.
if (mLocationManager.getProvider(TEST_PROVIDER) != null &&
mLocationManager.isProviderEnabled(TEST_PROVIDER)) {
provider = TEST_PROVIDER;
}
Log.d(TAG, "Using provider " + provider);
// Получить последнее известное местоположение
// и отправить его, если данные действительны.
Location lastKnown = mLocationManager.getLastKnownLocation(provider);
if (lastKnown != null) {
// Время инициализируется текущим значением lastKnown.setTime(System.currentTimeMillis());
broadcastLocation(lastKnown);
}

546
1   ...   47   48   49   50   51   52   53   54   55

перейти в каталог файлов
Образовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей
Образовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей