Первоначально этот пост задумывался как статья про работу с датчиком местоположения в Android, но по мере написания текста оказалось, что здесь собраны несколько моментов по взаимодействию с Android в Delphi XE5, которые не столь очевидны как, например, определение широты/долготы при использовании готового компонента TLocationSensor или получение списка всех доступных датчиков в устройстве, однако могут быть полезны при работе над проектом. Поэтому я и решил сменить название и представить вам несколько, на мой взгляд, полезных примеров по работе с Android, которые были частично написаны мной, а частично, собраны из разных уголков в Сети. Вполне возможно, что какая-то часть примеров может оказаться полезной и для участников конкурса мобильных приложений на Delphi.
Пример №1. Как получить список доступных поставщиков местоположения устройства?
В Android координаты местоположения (широта/долгота) могут определяться следующими способами:
- С использованием служб Google для определения координат по Wi-fi и мобильным сетям
- С использованием данных со спутников GPS
- В Delphi — с использованием компонента TLocationSensor
Настройка сервиса определения местоположения в Android выглядит следующим образом:
Чтобы определить поставщиков местоположения (gps, сеть и т.д.) необходимо получить доступ к объекту LocationManager и запросить список поставщиков, используя методы getAllProviders или getProviders. Первый метод вернет перечень всех поставщиков,. второй — только доступных для использования.
Пример получения всех провайдеров выглядит следующим образом:
uses FMX.Helpers.Android, Androidapi.JNI.Location, Androidapi.JNIBridge, Androidapi.JNI.GraphicsContentViewText, Androidapi.JNI.JavaTypes; .... var LocManagerObj: JObject; LocationManager: JLocationManager; AllProviders: JList; I: Integer; begin //запрашиваем сервис Location LocManagerObj:=SharedActivityContext.getSystemService(TJContext.JavaClass.LOCATION_SERVICE); if not Assigned(LocManagerObj) then raise Exception.Create('Could not locate Location Service'); //получаем LocationManager LocationManager:=TJLocationManager.Wrap((LocManagerObj as ILocalObject).GetObjectID); if not Assigned(LocationManager) then raise Exception.Create('Could not access Location Manager'); //запрашиваем список всех провайдеров AllProviders:=LocationManager.getAllProviders; //выводим список if Assigned(AllProviders) then begin for I := 0 to AllProviders.size-1 do Memo1.Lines.Add(JStringToString(AllProviders.get(i).toString)); end; end;
Пример №2. Как получить сведения о текущем местоположении устройства (город, улица, дом и т.д.)
Пример использования готовых классов в Delphi XE5 вы можете изучить на сайте Embarcadero — здесь. Если же вам хочется воспользоваться для этой цели Android API, то, опять же, потребуется получить доступ к LocationManager (как в первом примере) и выполнить следующие операции:
var LocManagerObj: JObject; LocationManager: JLocationManager; LastLocation: JLocation; Geocoder: JGeocoder; Address: JAddress; AddressList: JList; begin //запрашиваем сервис Location LocManagerObj:=SharedActivityContext.getSystemService(TJContext.JavaClass.LOCATION_SERVICE); if not Assigned(LocManagerObj) then raise Exception.Create('Could not locate Location Service'); //получаем LocationManager LocationManager:=TJLocationManager.Wrap((LocManagerObj as ILocalObject).GetObjectID); if not Assigned(LocationManager) then raise Exception.Create('Could not access Location Manager'); //получаем последнее местоположение зафиксированное с помощью координат wi-fi и мобильных сетей LastLocation:=LocationManager.getLastKnownLocation(TJLocationManager.JavaClass.NETWORK_PROVIDER); if Assigned(LastLocation) then begin geocoder:= TJGeocoder.JavaClass.init(SharedActivityContext); if not Assigned(geocoder) then raise Exception.Create('Could not access Geocoder'); //пробуем определить 1 возможный адрес местоположения AddressList:=geocoder.getFromLocation(LastLocation.getLatitude, LastLocation.getLongitude,1); if AddressList.size>0 then begin Address:=TJAddress.Wrap((AddressList.get(0) as ILocalObject).GetObjectID); if not Assigned(Address) then raise Exception.Create('Could not access Address'); //выводим данные в memo Memo1.Lines.Add('City: '+JStringToString(Address.getAddressLine(1))); Memo1.Lines.Add('Street: '+JStringToString(Address.getAddressLine(0))); Memo1.Lines.Add('PostalCode: '+JStringToString(Address.getAddressLine(4))); end; end; end;
Для того, чтобы использовать Geocoder приложение должно иметь доступ в интернет и доступ к данным о местоположении. Для работы с разрешениями для приложения необходимо воспользоваться следующими настройками в IDE: Project -> Options -> Uses Permissions
Пример №3. Как узнать имеется ли доступ в интернет (включен ли wi-fi или мобильный интернет)
За эту информацию в Android отвечает объект ConnectivityManager. К сожалению готовых оберток для этого менеджера я в Delphi не нашел, поэтому пришлось воспользоваться чужим кодом, в котором разрабатываются собственные интерфейсы для доступа к ConnectivityManager и функции для получения сведений о состоянии подключений к интернету:
unit Network; interface function IsConnected: Boolean; function IsWiFiConnected: Boolean; function IsMobileConnected: Boolean; implementation uses System.SysUtils, Androidapi.JNIBridge, Androidapi.JNI.GraphicsContentViewText, Androidapi.JNI.JavaTypes, FMX.Helpers.Android, Misc; type JConnectivityManager = interface; JNetworkInfo = interface; JNetworkInfoClass = interface(JObjectClass) ['{E92E86E8-0BDE-4D5F-B44E-3148BD63A14C}'] end; [JavaSignature('android/net/NetworkInfo')] JNetworkInfo = interface(JObject) ['{6DF61A40-8D17-4E51-8EF2-32CDC81AC372}'] {Methods} function isAvailable: Boolean; cdecl; function isConnected: Boolean; cdecl; function isConnectedOrConnecting: Boolean; cdecl; end; TJNetworkInfo = class(TJavaGenericImport<JNetworkInfoClass, JNetworkInfo>) end; JConnectivityManagerClass = interface(JObjectClass) ['{E03A261F-59A4-4236-8CDF-0068FC6C5FA1}'] {Property methods} function _GetTYPE_WIFI: Integer; cdecl; function _GetTYPE_WIMAX: Integer; cdecl; function _GetTYPE_MOBILE: Integer; cdecl; {Properties} property TYPE_WIFI: Integer read _GetTYPE_WIFI; property TYPE_WIMAX: Integer read _GetTYPE_WIMAX; property TYPE_MOBILE: Integer read _GetTYPE_MOBILE; end; [JavaSignature('android/net/ConnectivityManager')] JConnectivityManager = interface(JObject) ['{1C4C1873-65AE-4722-8EEF-36BBF423C9C5}'] {Methods} function getActiveNetworkInfo: JNetworkInfo; cdecl; function getNetworkInfo(networkType: Integer): JNetworkInfo; cdecl; end; TJConnectivityManager = class(TJavaGenericImport<JConnectivityManagerClass, JConnectivityManager>) end; function GetConnectivityManager: JConnectivityManager; var ConnectivityServiceNative: JObject; begin ConnectivityServiceNative := SharedActivityContext.getSystemService(TJContext.JavaClass.CONNECTIVITY_SERVICE); if not Assigned(ConnectivityServiceNative) then raise Exception.Create('Could not locate Connectivity Service'); Result := TJConnectivityManager.Wrap( (ConnectivityServiceNative as ILocalObject).GetObjectID); if not Assigned(Result) then raise Exception.Create('Could not access Connectivity Manager'); end; function IsConnected: Boolean; var ConnectivityManager: JConnectivityManager; ActiveNetwork: JNetworkInfo; begin ConnectivityManager := GetConnectivityManager; ActiveNetwork := ConnectivityManager.getActiveNetworkInfo; Result := Assigned(ActiveNetwork) and ActiveNetwork.isConnected; end; function IsWiFiConnected: Boolean; var ConnectivityManager: JConnectivityManager; WiFiNetwork: JNetworkInfo; begin ConnectivityManager := GetConnectivityManager; WiFiNetwork := ConnectivityManager.getNetworkInfo(TJConnectivityManager.JavaClass.TYPE_WIFI); Result := WiFiNetwork.isConnected; end; function IsMobileConnected: Boolean; var ConnectivityManager: JConnectivityManager; MobileNetwork: JNetworkInfo; begin ConnectivityManager := GetConnectivityManager; MobileNetwork := ConnectivityManager.getNetworkInfo(TJConnectivityManager.JavaClass.TYPE_MOBILE); Result := MobileNetwork.isConnected; end; end.
Пример №4. Как открыть ссылку или документ в приложении по умолчанию?
В Windows для этой цели имеется ShellExecute. В Android можно написать свой аналог, используя следующий код:
Открытие URL в браузере по умолчанию:
var Intent: JIntent; begin Intent := TJIntent.Create; Intent.setAction(TJIntent.JavaClass.ACTION_VIEW); Intent.setData(StrToJURI('http://www.google.com')); SharedActivity.startActivity(Intent); end;
И в заключении, на мой взгляд, довольно интересный пост в Google+ от нашего зарубежного коллеги о том как использовать библиотеки Java при работе в XE5.
По мере возможностей буду этот список примеров расширять, так что, как говориться — оставайтесь с нами :)
А зачем понадобилось какое-то JNI? Ведь TLocationSensor и так есть — пример в папке MobileCodeSnippets.
Просто ещё один вариант работы с Location Service и не более. Про MobileCodeSnippets я в курсе, и про все примеры на Embarcadero.com относительно работы с датчиками — тоже :)
понял. Но я бы упомянул в посте. Потому что многие (и даже я) не в курсе штатного метода, пока носом не ткнут :-)
А так они будут считать, что для LocationService надо обязательно подключать JNI (кстати, что это, я вообще не в курсе), и т.п.
Так я даже, вроде бы и сказал куда надо сходить за примерами :) Первоначально этот пост задумывался как статья про работу с датчиком местоположения в Android, но по мере написания текста оказалось, что здесь собраны несколько моментов по взаимодействию с Android в Delphi XE5, которые не столь очевидны как, например, определение широты/долготы при использовании готового компонента TLocationSensor [….] Пример использования готовых классов в Delphi XE5 вы можете изучить на сайте Embarcadero — здесь. Кстати, по поводу приведенной выше ссылки на пример с Embarcadero — классный пример. В JNI я полез после того как не смог найти навскидку проверки подключения по… Подробнее »
спасибо, значит я по диагонали читал…
да не за что. Статьи по программированию обычно по диагонали и читаются :)
А сколько весят результирующие apk? Также как на десктопе?
Примерно. Может чуть по-меньше
В примере-2 вы использовали:LastLocation:=LocationManager.getLastKnownLocation(TJLocationManager.JavaClass.NETWORK_PROVIDER);
а если написать:LastLocation:=LocationManager.getLastKnownLocation(TJLocationManager.JavaClass.GPS_PROVIDER);
GPS всетаки GPS сработает? Я хочу узнать точное местоположения!! заранее спасибо!!!
Приветствую.
Если вы напишете
LastLocation:=LocationManager.getLastKnownLocation(TJLocationManager.JavaClass.GPS_PROVIDER);
то координаты по GPS вернуться в том, случае, если датчик GPS включен.
LocManagerObj:=SharedActivityContext.getSystemService(TJContext.JavaClass.LOCATION_SERVICE);
LocationManager:=TJLocationManager.Wrap((LocManagerObj as ILocalObject).GetObjectID);
LastLocation:=LocationManager.getLastKnownLocation(TJLocationManager.JavaClass.GPS_PROVIDER);
memo1.Lines.Add(‘lat=’+floattostr(Lastlocation.getLatitude));
memo1.Lines.Add(‘lon=’+floattostr(Lastlocation.getLongitude));
Андроид выдает ошибку «Access Violation at address 42AA2F4E….»
GPS датчик влючен!!!
Или я неправильно сделал??
Access Violation at address .... вываливается, потому что LastLocation не определяется. Просто ваш GPS-датчик на момент запроса ещё ничего не «поймал» со спутника и, следовательно, возвращает nil — в точности с документацией по Android. Включил на своем смарте только GPS — координаты не вернул ни код выше, ни компонент TLocationManager из состава Delphi. Накидал небольшую демку — завтра попробую её запустить под открытым небом — о результатах отпишусь :) Update: не пришлось даже выходить на улицу. Код работает нормально, НО получение координат с GPS надо ждать долго (во всяком случае дольше, чем от других провайдеров) + желательно ждать по ближе… Подробнее »
Не подскажете как пример №2 (там где получение координат) переписать для C++? Пробую на XE6. Спасибо.
Пытаюсь откомпилировать Ваш проект, но не получается, выдает 10 ошибок. Что не так делаю? Все вставляю тупо копи пастом.
Какой проект вы здесь нашли? Куда вы его вставляете? Версия Delphi какая? 10 ошибок каких?
В общем пока вы делаете не так вот что:
«Все вставляю тупо копи пастом»
Каждый из приведенных выше примеров был проверен на Delphi XE7 — ни одной ошибки нет.
Пример №3. Как узнать имеется ли доступ в интернет
НЕ РАБОЧИЙ!!!
1. Буржуйский автор вставил какой-то модуль Misc, но это пол беды и без него компилится.
2. При вызове любой из ф-ций программа зависает. Где-то в дебрях кода выполнение уходит в бесконечный цикл по ассемблерным инструкциям.
Vlad вы же программист! Бросьте эти блогерские повадки накопипастить материала не заботясь о его содержании.
К тому же dfit Ctrl+C — Ctrl+V вас подвели:
ваша строка: TJNetworkInfo = class(TJavaGenericImport<JNetworkInfoClass, JNetworkInfo>) end;
строка автора TJNetworkInfo = class(TJavaGenericImport) end;
Хорошо хоть ссылку на оригинал привели.