1. 程式人生 > >Android WebView關於定位部分研究

Android WebView關於定位部分研究

啥是H5定位

HTML5標準中提供Geolocation API,js中呼叫該API,用於獲得使用者的地理位置。 鑑於該特性可能侵犯使用者的隱私,除非使用者同意,否則使用者位置資訊是不可用的。 該API被設計成即支援一次性請求,也支援反覆的位置更新,以及顯示的查詢快取的位置的能力。位置資訊通過經緯度來呈現。需要注意的是:根據世界大地測量系統座標[WGS84]提供地理位置資訊(GPS使用的是WGS84座標)。

The geographic position information is provided in terms of World Geodetic System coordinates [WGS84].

如何使用H5定位

Navigator介面擴充套件

partial interface Navigator {  
    readonly attribute Geolocation geolocation;  
};

Navigator的geolocation屬性提供了與主裝置相關聯的位置資訊的訪問。

The geolocation attribute gives access to location information associated with the hosting device.

Geolocation介面

[NoInterfaceObject]  
interface
Geolocation { void getCurrentPosition(PositionCallback successCallback,optional PositionErrorCallback errorCallback,optional PositionOptions options); long watchPosition(PositionCallback successCallback,optional PositionErrorCallback errorCallback,optional PositionOptions options); void
clearWatch(long watchId); };

PositionOptions介面

dictionary PositionOptions {
  boolean enableHighAccuracy = false;
  [Clamp] unsigned long timeout = 0xFFFFFFFF;
  [Clamp] unsigned long maximumAge = 0;
};

Position介面

[NoInterfaceObject]
interface Position {
  readonly attribute Coordinates coords;
  readonly attribute DOMTimeStamp timestamp;
};

Coordinates介面

[NoInterfaceObject]
interface Coordinates {
  readonly attribute double latitude;
  readonly attribute double longitude;
  readonly attribute double? altitude;
  readonly attribute double accuracy;
  readonly attribute double? altitudeAccuracy;
  readonly attribute double? heading;
  readonly attribute double? speed;
};

he geographic coordinate reference system used by the attributes in this interface is the World Geodetic System (2d) [WGS84]. No other reference system is supported.

PositionError介面

[NoInterfaceObject]
interface PositionError {
  const unsigned short PERMISSION_DENIED = 1;
  const unsigned short POSITION_UNAVAILABLE = 2;
  const unsigned short TIMEOUT = 3;
  readonly attribute unsigned short code;
  readonly attribute DOMString message;
};

The code attribute MUST return the appropriate code from the following list:

  • PERMISSION_DENIED (numeric value 1) The location acquisition process failed because the document does not have permission to use the Geolocation API.
  • POSITION_UNAVAILABLE (numeric value 2) The position of the device could not be determined. For instance, one or more of the location providers used in the location acquisition process reported an internal error that caused the process to fail entirely.
  • TIMEOUT (numeric value 3) The length of time specified by the timeout property has elapsed before the implementation could successfully acquire a new Position object.

下面一些示例展示如何獲取基礎位置資訊

EXAMPLE 1: Example of a “one-shot” position request

function showMap(position) {
  // Show a map centered at (position.coords.latitude, position.coords.longitude).
}

// One-shot position request.
navigator.geolocation.getCurrentPosition(showMap);

EXAMPLE 2: Example of requesting repeated position updates

function scrollMap(position) {
  // Scrolls the map so that it is centered at
  //  (position.coords.latitude, position.coords.longitude).
}

// Request repeated updates.
var watchId = navigator.geolocation.watchPosition(scrollMap);

function buttonClickHandler() {
  // Cancel the updates when the user clicks a button.
  navigator.geolocation.clearWatch(watchId);
}

EXAMPLE 3: Example of requesting repeated position updates and handling errors

function scrollMap(position) {
  // Scrolls the map so that it is centered at
  //  (position.coords.latitude, position.coords.longitude).
}

function handleError(error) {
  // Update a div element with error.message.
}

// Request repeated updates.
var watchId = navigator.geolocation.watchPosition(scrollMap, handleError);

function buttonClickHandler() {
  // Cancel the updates when the user clicks a button.
  navigator.geolocation.clearWatch(watchId);
}

EXAMPLE 4: Example of requesting a potentially cached position

// Request a position. We accept positions whose age is not
// greater than 10 minutes. If the user agent does not have a
// fresh enough cached position object, it will automatically
// acquire a new one.
navigator.geolocation.getCurrentPosition(successCallback,
                                         errorCallback,
                                         {maximumAge:600000});

function successCallback(position) {
  // By using the 'maximumAge' option above, the position
  // object is guaranteed to be at most 10 minutes old.
}

function errorCallback(error) {
  // Update a div element with error.message.
}

EXAMPLE 5: Forcing the user agent to return a fresh cached position

// Request a position. We only accept cached positions whose age is not
// greater than 10 minutes. If the user agent does not have a fresh
// enough cached position object, it will immediately invoke the error
// callback.
navigator.geolocation.getCurrentPosition(successCallback,
                                         errorCallback,
                                         {maximumAge:600000, timeout:0});

function successCallback(position) {
  // By using the 'maximumAge' option above, the position
  // object is guaranteed to be at most 10 minutes old.
  // By using a 'timeout' of 0 milliseconds, if there is
  // no suitable cached position available, the user agent
  // will asynchronously invoke the error callback with code
  // TIMEOUT and will not initiate a new position
  // acquisition process.
}

function errorCallback(error) {
  switch(error.code) {
    case error.TIMEOUT:
      // Quick fallback when no suitable cached position exists.
      doFallback();
      // Acquire a new position object.
      navigator.geolocation.getCurrentPosition(successCallback, errorCallback);
      break;
    case ... // treat the other error cases.
  };
}

function doFallback() {
  // No fresh enough cached position available.
  // Fallback to a default position.
}

EXAMPLE 6: Forcing the user agent to return any available cached position

// Request a position. We only accept cached positions, no matter what
// their age is. If the user agent does not have a cached position at
// all, it will immediately invoke the error callback.
navigator.geolocation.getCurrentPosition(successCallback,
                                         errorCallback,
                                         {maximumAge:Infinity, timeout:0});

function successCallback(position) {
  // By setting the 'maximumAge' to Infinity, the position
  // object is guaranteed to be a cached one.
  // By using a 'timeout' of 0 milliseconds, if there is
  // no cached position available at all, the user agent
  // will immediately invoke the error callback with code
  // TIMEOUT and will not initiate a new position
  // acquisition process.
  if (position.timestamp < freshness_threshold &&
      position.coords.accuracy < accuracy_threshold) {
    // The position is relatively fresh and accurate.
  } else {
    // The position is quite old and/or inaccurate.
  }
}

function errorCallback(error) {
  switch(error.code) {
    case error.TIMEOUT:
      // Quick fallback when no cached position exists at all.
      doFallback();
      // Acquire a new position object.
      navigator.geolocation.getCurrentPosition(successCallback, errorCallback);
      break;
    case ... // treat the other error cases.
  };
}

function doFallback() {
  // No cached position available at all.
  // Fallback to a default position.
}

如何給Android裝置的Webview新增定位功能

  1. 首先在AndroidManifest.xml中新增定位許可權申明
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    

如果應用的target小於Android.M,至此就完成了給WebView新增支援定位功能。因為預設是許可網站獲取位置的。如圖:。But,你看到這篇文章時,肯定target不下於Android.M了,所以,接著看。 2. 給WebView設定setWebChromeClient; 3. 重寫setWebChromeClient的 onGeolocationPermissionsShowPrompt() 方法; 4. 判斷App是否獲得了定位許可權,如果沒有請求定位許可權; 5. 判斷裝置是否開啟了定位服務,在某些情況下如果沒有開啟定位無法獲取到定位的,如下:

  • 獲取不到位置
    • 裝置很長時間沒有開啟定位了。
    • 裝置不久前開啟了定位,現在已經關閉定位了,但是js又要求必須獲取到實時的位置(不允許使用快取位置)。
  • 獲取到了位置
    • 裝置當前定位服務處於開啟狀態。
    • 裝置不久前開啟了定位,現在處於關閉狀態,js中接受快取位置。
  1. 呼叫 GeolocationPermissionsCallback 回撥,設定是否許可js獲取當前裝置的許可權

整體邏輯就是這樣。中間比較複雜的是第4步。這一步需要考慮到這一點:App申請定位許可權時,系統會給使用者三種選擇,分別是:

  • 許可: 使用者選擇之後,我們就可以走第5步了。
  • 禁止: 走第6步,回撥,拒絕js的位置請求。js下一次請求時,會回撥 onGeolocationPermissionsShowPrompt() 方法,繼續走4、5、6。
  • 禁止並不再提示: 本次的呼叫同上,但是,js下一次請求時,流程就有一點點不一樣。首先,系統直接不提示使用者授權了。what?這還玩個P啊。但是,系統會給我們的回撥一個拒絕的結果。所以,在此時,我們可以請求讓使用者到系統的設定去給應用授權。使用者如果願意去設定,就讓他去設定,然後回來時,我們判斷一下,使用者給應用許可權了,我們就到第6步,許可許可權;如果沒有給許可權,對不起,只能到第6步,回撥不許可許可權了。如果使用者不願意去設定,那也是到第6步,回撥不許可許可權。

嗯,但願你沒有暈倒[苦笑]。我也想給你講的簡單點,但是,事實就是這麼複雜,我能怎麼辦,我也很無奈啊[笑哭]。 好了,你們一定想說,別扯淡了,趕緊放碼出來。諾,這是你要的程式碼

會遇到哪些問題

  • H5請求定位,客戶端的onGeolocationPermissionsShowPrompt方法沒有回撥 三點需要注意
    1. Android系統版本是不是大於6.0?
    2. 是不是安全連結? 從Android6.0開始,如果連結不是安全的,該方法不被呼叫。

    Note that for applications targeting Android N and later SDKs (API level > Build.VERSION_CODES.M) this method is only called for requests originating from secure origins such as https. On non-secure origins geolocation requests are automatically denied.

    1. js是否接受快取地址? 如果js接受快取地址,儘管定位服務處於關閉狀態,如果不久前裝置剛剛定了位置,這個方法也不會被呼叫。