30/09/2018, 23:56

[React Native] Làm thế nào để lấy tọa độ GPS trên Android

Cho mình hỏi cách nào để lấy tọa độ GPS trên Android (lấy 1 lần thôi)

Mình sử dụng geolocation có sẵn của Javascript, thì trên iOS chạy rất tốt, nhưng trên Android thì chờ mãi mà Callback vẫn không chạy (getCurrentLocation thì thi thoảng có chạy nhưng không thể cancel, watchPosition thì chờ mãi mãi luôn, cho dù đã có timeout).

Mình dùng hẳn code Java và dùng Location Manager thì cũng tương tự, GoogleApiClient thì chưa mò hết được, hiện tại cũng không được.

Tất cả mình đều đã thử trên máy thật rồi. App Google Map thì nó lấy GPS rất nhanh, nên chắc là GPS không bị hư.

Nhờ mọi người giúp đỡ ạ.

Nguyễn Phú viết 02:02 ngày 01/10/2018

Mình chạy trên android thấy không có vấn đề. chỉ add <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> vào AndroidManifest.xml là xài bình thường mà. Nếu test trên máy ảo thì phải vào Extended control > Location để send lat/lng thì trong app mới nhận. (send trước khi nó báo timeout)

明玉 viết 02:06 ngày 01/10/2018

Permission đó thì mình có rồi bạn , ở đây mình cần trên máy thật.

Quân viết 02:01 ngày 01/10/2018

1.android >5 sẽ phải request permssion lúc runtime, khai báo trong manifest sẽ bị ignore.
2.get location từ LocationManager bằng instance method

LocationManager.getLastKnownLocation(String provider); // lấy ra ngay last location của loại gps bạn truyền vào nếu có

3.để update location từ LocationManager bằng instance method

LocationManager.requestUpdateLocation(String provider, long time, double distance, LocationListener listener); // invoke khi nào location manager thấy cần thiết chứ không phải sẽ lấy ra ngay location.

Không biết bạn đã làm đúng 3 điều trên chưa mà không lấy được location.

明玉 viết 01:59 ngày 01/10/2018

chắc mình sẽ thử vụ request lúc runtime, nhưng điện thoại của mình là Android 4.2 < 5 mà còn không được đây. Mình đã dùng requestUpdateLocation và đặt hàm đón event đầy đủ cả mà vẫn không bắt được. Còn getLastKnownLocation thì nó trả về dữ liệu cũ, hoặc null nên mình không dùng được.

Phan Hoàng viết 01:58 ngày 01/10/2018

Bạn thử bật log lên xem nào? Chứ nghe triệu chứng đoán bệnh thì khó rồi.
Thử đoán thôi nhé: vào máy thật ->Developer Option, xem có option này khi chạy ở chế độ debug / … cấm truy cập GeoApi hoặc Sensor k nhỉ?

Quân viết 02:01 ngày 01/10/2018

Bạn đọc kĩ 3 điều mình viết nhé, nhất là điều thứ 3 bạn sẽ hiểu vì sao đón event nhưng cũng không ăn thua, android chỉ có thế, quan trọng là kết hợp và Tricks thì lúc nào cũng lấy được location tuy không phải mới nhất nhưng cũng không sai lệch nhiều

明玉 viết 02:03 ngày 01/10/2018

Đây là class của mình, nhờ bạn xem hộ là mình có mắc sai lầm gì không:

package com.test;
// http://www.androidhive.info/2012/07/android-gps-location-manager-tutorial/

import android.app.AlertDialog;
import android.app.Service;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.Settings;
import android.util.Log;
import android.view.WindowManager;


import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;

import java.util.Locale;

public class GPSTracker extends Service implements LocationListener
{
    private final Context mContext;
    private Boolean done = false;

    boolean isGPSEnabled = false;

    boolean canGetLocation = false;

    Location location; // location
    double latitude; // latitude
    double longitude; // longitude
    Long timeout = null;

    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters

    private static final long MIN_TIME_BW_UPDATES = 1000 * 10 * 1; // 1 minute

    protected LocationManager locationManager;

    Callback successCallback;
    Callback errorCallback;
    public GPSTracker(Context context, Callback successCallback, Callback errorCallback, ReadableMap option)
    {
        this.mContext = context;
        this.successCallback = successCallback;
        this.errorCallback = errorCallback;
        if (option.hasKey("timeout"))
            this.timeout = new Long(option.getInt("timeout"));
        this.getLocation();
    }
    private Thread thread = new Thread(new Runnable() {
        public void run()
        {
                try
                {
                    Thread.sleep(timeout);
                    synchronized (done)
                    {
                        if (done == true)
                            return;
                        WritableMap error = Arguments.createMap();
                        error.putString("message", "Hết thời gian.");
                        errorCallback.invoke(error);
                        done = true;
                    }

                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }

        }
    });

    public void getLocation()
    {
        try
        {
            locationManager = (LocationManager)mContext.getSystemService(LOCATION_SERVICE);
            isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);

            if (!isGPSEnabled)
            {
                // no network provider is enabled
            }
            else
            {
                this.canGetLocation = true;
                if (isGPSEnabled && location == null)
                {
                    synchronized (this.done)
                    {
                        locationManager.requestLocationUpdates(
                            LocationManager.GPS_PROVIDER,
                            MIN_TIME_BW_UPDATES,
                            MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
                        Log.d("GPS Enabled", "GPS Enabled");
                        if (this.timeout != null)
                            this.thread.start();
                    }
                }
            }

        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    public void stopUsingGPS()
    {
        if(locationManager != null)
        {
            locationManager.removeUpdates(GPSTracker.this);
        }
    }

    public boolean canGetLocation()
    {
        return this.canGetLocation;
    }

    public void showSettingsAlert()
    {
        AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext.getApplicationContext());

        String currentlang = Locale.getDefault().getLanguage();
        boolean isVN = currentlang.equals(new Locale("vi").getLanguage());

        // Setting Dialog Title
        alertDialog.setTitle(isVN ? "Thông báo" : "Notice");

        // Setting Dialog Message
        alertDialog.setMessage(isVN ? "GPS hiện chưa được bật. Bạn có muốn vào menu thiết lập để bật không?"
                : "GPS is not enabled. Do you want to go to settings menu?");
        alertDialog.setPositiveButton(isVN ? "Thiết lập" : "Settings",
                new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog,int which)
            {
                Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                mContext.getApplicationContext().startActivity(intent);
            }
        });

        alertDialog.setNegativeButton(isVN? "Bỏ qua" : "Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which)
            {
                dialog.cancel();
            }
        });

        // Showing Alert Message
        AlertDialog alert = alertDialog.create();
        alert.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
        alert.show();
    }

    // event này không hề được gọi
    @Override
    public void onLocationChanged(Location location)
    {
        synchronized (this.done) {
            if (this.done == true) return;
            double lat = location.getLatitude();
            double lng = location.getLongitude();
            if (thread.isAlive())
                thread.interrupt();
            WritableMap pos = Arguments.createMap();
            WritableMap coords = Arguments.createMap();
            coords.putDouble("latitude", lat);
            coords.putDouble("longitude", lng);
            pos.putMap("coords", coords);
            successCallback.invoke(pos);
            this.done = true;
        }
    }

    @Override
    public void onProviderDisabled(String provider)
    {
    }

    @Override
    public void onProviderEnabled(String provider)
    {
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras)
    {
        synchronized (this.done) {
            if (this.done == true) return;
            if (status == LocationProvider.OUT_OF_SERVICE ||
                    status == LocationProvider.TEMPORARILY_UNAVAILABLE) {
                if (thread.isAlive())
                    thread.interrupt();
                WritableMap error = Arguments.createMap();
                error.putString("message", "Không lấy được gps");
                errorCallback.invoke(error);
            }
            this.done = true;
        }
    }

    @Override
    public IBinder onBind(Intent arg0)
    {
        return null;
    }

}
Phan Hoàng viết 02:00 ngày 01/10/2018

Lạ thật đấy, mình thấy log vẫn chạy function onLocationChanged() mà (bạn đang để 1p hoặc 10m chạy lại 1 lần). Lấy location bình thường luôn.

(code lỗi vì mình đang truyền các callback và option null)

Bạn attach cái log của logcat lúc đó xem nào? Chứ giờ thì chịu rồi.

明玉 viết 02:08 ngày 01/10/2018

Bạn có thử trên máy thật không, chứ trên máy ảo bên mình thì chạy cực kỳ tốt rồi.

Phan Hoàng viết 01:58 ngày 01/10/2018

Máy thật đó bạn. Máy mình dùng là Android L (5.0) Asus Zendphone 2.

Phan Hoàng viết 02:05 ngày 01/10/2018

requestUpdateLocation

À, mình biết lỗi này rồi. Bình thường Fused Location Provider nó sẽ cache cái vị trí cuối cùng nếu có ít nhất một client được kết nối với nó. Có vẻ có mỗi app của bạn hiện đang lấy vị trí nên nó sẽ phải kết nối với vệ tinh GPS. Mà qúa trình này sẽ khá chậm và thi thoảng bị timeout (do GPS bay trên trời nên muốn lấy nó cũng phải có time, rồi do vật cản, … và hàng tý lý do).

Bạn thử bật cả Wifi/3G hoặc Cell network xem tình hình thế nào (lấy từ cả NET hoặc CELL PHONE) xem nó lấy có bị lỗi không? Đem thử ra ngoài trời xem có nhanh hơn không? (một số tòa nhà được thiết kế sao đó mà cứ ngồi nhà là đek thể nào kết nối với ve tinh GPS được).

Trong trường hợp này bạn nên implement cả việc lấy từ Wifi/3G và Cellphone, chứ chỉ dựa vào GPS thì cũng hơi rủi ro.

Phan Hoàng viết 02:10 ngày 01/10/2018

Còn vụ GoogleApiClient lấy vẫn nhanh thì cũng chịu. Bạn đã thử test với vụ chỉ bật GPS và tắt hết Wifi/3G và/hoặc Cellphone chưa? Vì code của bạn hiện giờ mới chỉ lấy location từ GPS, trong khi GoogleApiClient nó wrapper cả 3 trường hợp nên nó lấy nhanh cũng phải.

if (!isGPSEnabled)
            {
                // no network provider is enabled
            }
            else
            {
                this.canGetLocation = true;
                if (isGPSEnabled && location == null)
                {
                    synchronized (this.done)
                    {
                        locationManager.requestLocationUpdates(
                            LocationManager.GPS_PROVIDER,
                            MIN_TIME_BW_UPDATES,
                            MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
                        Log.d("GPS Enabled", "GPS Enabled");
                        if (this.timeout != null)
                            this.thread.start();
                    }
                }
            }

Nếu k detect được GPS thì nên backup lấy từ wifi/3g, chứ if của bạn hiện giờ chưa làm gì cả.

明玉 viết 02:00 ngày 01/10/2018

Còn vụ GoogleApiClient lấy vẫn nhanh thì cũng chịu. Bạn đã thử test với vụ chỉ bật GPS và tắt hết Wifi/3G và/hoặc Cellphone chưa? Vì code của bạn hiện giờ mới chỉ lấy location từ GPS, trong khi GoogleApiClient nó wrapper cả 3 trường hợp nên nó lấy nhanh cũng phải.

Không, ý mình là App Google Maps lấy nhanh, chứ GoogleApiClient thì vẫn chưa được, mình vẫn đang nghiên cứu nó.

Bài liên quan
0