วิธีที่ดีในการรับตำแหน่งของผู้ใช้ใน Android


211

ปัญหา:

การรับตำแหน่งปัจจุบันของผู้ใช้ภายในขีด จำกัด ASAP และในเวลาเดียวกันก็ประหยัดแบตเตอรี่

ทำไมปัญหาเป็นปัญหา:

ก่อนอื่น Android มีผู้ให้บริการสองราย เครือข่ายและ GPS บางครั้งเครือข่ายดีกว่าและบางครั้ง GPS ก็ดีกว่า

โดย "ดีกว่า" ฉันหมายถึงอัตราส่วนความเร็วกับความแม่นยำ
ฉันยินดีที่จะเสียสละความแม่นยำเพียงไม่กี่เมตรถ้าฉันสามารถหาที่ตั้งได้เกือบจะทันทีและไม่ต้องเปิด GPS

ประการที่สองถ้าคุณขอให้อัปเดตสำหรับการเปลี่ยนแปลงตำแหน่งจะไม่มีการส่งอะไรเลยหากตำแหน่งปัจจุบันมีเสถียรภาพ

Google มีตัวอย่างของการระบุตำแหน่ง "ดีที่สุด" ที่นี่: http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
แต่ฉันคิดว่ามันไม่ได้อยู่ใกล้ที่ดีเท่าที่ควร /อาจจะเป็น.

ฉันสับสนเพราะเหตุใด google จึงไม่ได้ API ตามปกติสำหรับสถานที่นักพัฒนาไม่ควรสนใจว่าสถานที่นั้นมาจากที่ใดคุณควรระบุสิ่งที่คุณต้องการและโทรศัพท์ควรเลือกให้คุณ

สิ่งที่ฉันต้องการความช่วยเหลือ:

ฉันต้องการหาวิธีที่ดีในการระบุตำแหน่ง "ที่ดีที่สุด" บางทีอาจจะมีบางฮิวริสติกหรืออาจผ่านห้องสมุดบุคคลที่สาม

นี่ไม่ได้หมายความว่าเป็นผู้ให้บริการที่ดีที่สุด!
ฉันอาจจะใช้ผู้ให้บริการทั้งหมดและเลือกสิ่งที่ดีที่สุด

พื้นหลังของแอพ:

แอพจะรวบรวมตำแหน่งของผู้ใช้ในช่วงเวลาที่กำหนด (ให้บอกทุก ๆ 10 นาที) แล้วส่งไปที่เซิร์ฟเวอร์
แอปควรประหยัดแบตเตอรี่ให้มากที่สุดและตำแหน่งควรมีความแม่นยำระดับ X (50-100?)

เป้าหมายคือในภายหลังสามารถพล็อตเส้นทางของผู้ใช้ในระหว่างวันบนแผนที่ดังนั้นฉันต้องการความแม่นยำที่เพียงพอสำหรับสิ่งนั้น

อื่น ๆ:

คุณคิดว่าอะไรคือคุณค่าที่สมเหตุสมผลสำหรับความถูกต้องที่ต้องการและเป็นที่ยอมรับ?
ฉันใช้ 100m เป็นที่ยอมรับและ 30m ตามที่ต้องการนี่เป็นการถามมากมายหรือไม่?
ฉันต้องการที่จะสามารถวางแผนเส้นทางของผู้ใช้บนแผนที่ในภายหลัง
100m สำหรับความต้องการและ 500m สำหรับการยอมรับที่ดีขึ้น?

นอกจากนี้ตอนนี้ฉันมี GPS เปิดสูงสุด 60 วินาทีต่อการอัปเดตตำแหน่งมันสั้นเกินไปที่จะรับตำแหน่งถ้าคุณอยู่ในอาคารที่มีความแม่นยำ 200m หรือไม่


นี่คือรหัสปัจจุบันของฉันข้อเสนอแนะใด ๆ ที่ชื่นชม (นอกเหนือจากการขาดการตรวจสอบข้อผิดพลาดซึ่งเป็นสิ่งที่ต้องทำ):

protected void runTask() {
    final LocationManager locationManager = (LocationManager) context
            .getSystemService(Context.LOCATION_SERVICE);
    updateBestLocation(locationManager
            .getLastKnownLocation(LocationManager.GPS_PROVIDER));
    updateBestLocation(locationManager
            .getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
    if (getLocationQuality(bestLocation) != LocationQuality.GOOD) {
        Looper.prepare();
        setLooper(Looper.myLooper());
        // Define a listener that responds to location updates
        LocationListener locationListener = new LocationListener() {

            public void onLocationChanged(Location location) {
                updateBestLocation(location);
                if (getLocationQuality(bestLocation) != LocationQuality.GOOD)
                    return;
                // We're done
                Looper l = getLooper();
                if (l != null) l.quit();
            }

            public void onProviderEnabled(String provider) {}

            public void onProviderDisabled(String provider) {}

            public void onStatusChanged(String provider, int status,
                    Bundle extras) {
                // TODO Auto-generated method stub
                Log.i("LocationCollector", "Fail");
                Looper l = getLooper();
                if (l != null) l.quit();
            }
        };
        // Register the listener with the Location Manager to receive
        // location updates
        locationManager.requestLocationUpdates(
                LocationManager.GPS_PROVIDER, 1000, 1, locationListener,
                Looper.myLooper());
        locationManager.requestLocationUpdates(
                LocationManager.NETWORK_PROVIDER, 1000, 1,
                locationListener, Looper.myLooper());
        Timer t = new Timer();
        t.schedule(new TimerTask() {

            @Override
            public void run() {
                Looper l = getLooper();
                if (l != null) l.quit();
                // Log.i("LocationCollector",
                // "Stopping collector due to timeout");
            }
        }, MAX_POLLING_TIME);
        Looper.loop();
        t.cancel();
        locationManager.removeUpdates(locationListener);
        setLooper(null);
    }
    if (getLocationQuality(bestLocation) != LocationQuality.BAD) 
        sendUpdate(locationToString(bestLocation));
    else Log.w("LocationCollector", "Failed to get a location");
}

private enum LocationQuality {
    BAD, ACCEPTED, GOOD;

    public String toString() {
        if (this == GOOD) return "Good";
        else if (this == ACCEPTED) return "Accepted";
        else return "Bad";
    }
}

private LocationQuality getLocationQuality(Location location) {
    if (location == null) return LocationQuality.BAD;
    if (!location.hasAccuracy()) return LocationQuality.BAD;
    long currentTime = System.currentTimeMillis();
    if (currentTime - location.getTime() < MAX_AGE
            && location.getAccuracy() <= GOOD_ACCURACY)
        return LocationQuality.GOOD;
    if (location.getAccuracy() <= ACCEPTED_ACCURACY)
        return LocationQuality.ACCEPTED;
    return LocationQuality.BAD;
}

private synchronized void updateBestLocation(Location location) {
    bestLocation = getBestLocation(location, bestLocation);
}

// Pretty much an unmodified version of googles example
protected Location getBestLocation(Location location,
        Location currentBestLocation) {
    if (currentBestLocation == null) {
        // A new location is always better than no location
        return location;
    }
    if (location == null) return currentBestLocation;
    // Check whether the new location fix is newer or older
    long timeDelta = location.getTime() - currentBestLocation.getTime();
    boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
    boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
    boolean isNewer = timeDelta > 0;
    // If it's been more than two minutes since the current location, use
    // the new location
    // because the user has likely moved
    if (isSignificantlyNewer) {
        return location;
        // If the new location is more than two minutes older, it must be
        // worse
    } else if (isSignificantlyOlder) {
        return currentBestLocation;
    }
    // Check whether the new location fix is more or less accurate
    int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation
            .getAccuracy());
    boolean isLessAccurate = accuracyDelta > 0;
    boolean isMoreAccurate = accuracyDelta < 0;
    boolean isSignificantlyLessAccurate = accuracyDelta > 200;
    // Check if the old and new location are from the same provider
    boolean isFromSameProvider = isSameProvider(location.getProvider(),
            currentBestLocation.getProvider());
    // Determine location quality using a combination of timeliness and
    // accuracy
    if (isMoreAccurate) {
        return location;
    } else if (isNewer && !isLessAccurate) {
        return location;
    } else if (isNewer && !isSignificantlyLessAccurate
            && isFromSameProvider) {
        return location;
    }
    return bestLocation;
}

/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
    if (provider1 == null) {
        return provider2 == null;
    }
    return provider1.equals(provider2);
}

7
กำลังส่งสายช้า ๆ แต่ "ผู้ให้บริการตำแหน่งที่รวม" ซึ่งเพิ่งประกาศเมื่อไม่นานมานี้ที่ IO 2013 ดูเหมือนว่าจะตอบสนองความต้องการของคุณได้มากมาย - developer.android.com/google/play-services/location.html
Matt

ไม่ควรบรรทัดสุดท้ายของ getBestLocation () ควรเป็น: return currentBestLocation; แทนที่จะส่งคืนที่ดีที่สุดที่ตั้ง;
Gavriel

คำตอบ:


164

ดูเหมือนว่าเรากำลังเข้ารหัสแอปพลิเคชันเดียวกัน ;-)
นี่คือการใช้งานปัจจุบันของฉัน ฉันยังอยู่ในช่วงทดสอบเบต้าของแอปอัปโหลด GPS ของฉันดังนั้นอาจมีการปรับปรุงมากมาย แต่ดูเหมือนว่าจะทำงานได้ดีจนถึงตอนนี้

/**
 * try to get the 'best' location selected from all providers
 */
private Location getBestLocation() {
    Location gpslocation = getLocationByProvider(LocationManager.GPS_PROVIDER);
    Location networkLocation =
            getLocationByProvider(LocationManager.NETWORK_PROVIDER);
    // if we have only one location available, the choice is easy
    if (gpslocation == null) {
        Log.d(TAG, "No GPS Location available.");
        return networkLocation;
    }
    if (networkLocation == null) {
        Log.d(TAG, "No Network Location available");
        return gpslocation;
    }
    // a locationupdate is considered 'old' if its older than the configured
    // update interval. this means, we didn't get a
    // update from this provider since the last check
    long old = System.currentTimeMillis() - getGPSCheckMilliSecsFromPrefs();
    boolean gpsIsOld = (gpslocation.getTime() < old);
    boolean networkIsOld = (networkLocation.getTime() < old);
    // gps is current and available, gps is better than network
    if (!gpsIsOld) {
        Log.d(TAG, "Returning current GPS Location");
        return gpslocation;
    }
    // gps is old, we can't trust it. use network location
    if (!networkIsOld) {
        Log.d(TAG, "GPS is old, Network is current, returning network");
        return networkLocation;
    }
    // both are old return the newer of those two
    if (gpslocation.getTime() > networkLocation.getTime()) {
        Log.d(TAG, "Both are old, returning gps(newer)");
        return gpslocation;
    } else {
        Log.d(TAG, "Both are old, returning network(newer)");
        return networkLocation;
    }
}

/**
 * get the last known location from a specific provider (network/gps)
 */
private Location getLocationByProvider(String provider) {
    Location location = null;
    if (!isProviderSupported(provider)) {
        return null;
    }
    LocationManager locationManager = (LocationManager) getApplicationContext()
            .getSystemService(Context.LOCATION_SERVICE);
    try {
        if (locationManager.isProviderEnabled(provider)) {
            location = locationManager.getLastKnownLocation(provider);
        }
    } catch (IllegalArgumentException e) {
        Log.d(TAG, "Cannot acces Provider " + provider);
    }
    return location;
}

แก้ไข:นี่คือส่วนที่ร้องขอการอัพเดทเป็นระยะจากผู้ให้บริการตำแหน่ง:

public void startRecording() {
    gpsTimer.cancel();
    gpsTimer = new Timer();
    long checkInterval = getGPSCheckMilliSecsFromPrefs();
    long minDistance = getMinDistanceFromPrefs();
    // receive updates
    LocationManager locationManager = (LocationManager) getApplicationContext()
            .getSystemService(Context.LOCATION_SERVICE);
    for (String s : locationManager.getAllProviders()) {
        locationManager.requestLocationUpdates(s, checkInterval,
                minDistance, new LocationListener() {

                    @Override
                    public void onStatusChanged(String provider,
                            int status, Bundle extras) {}

                    @Override
                    public void onProviderEnabled(String provider) {}

                    @Override
                    public void onProviderDisabled(String provider) {}

                    @Override
                    public void onLocationChanged(Location location) {
                        // if this is a gps location, we can use it
                        if (location.getProvider().equals(
                                LocationManager.GPS_PROVIDER)) {
                            doLocationUpdate(location, true);
                        }
                    }
                });
        // //Toast.makeText(this, "GPS Service STARTED",
        // Toast.LENGTH_LONG).show();
        gps_recorder_running = true;
    }
    // start the gps receiver thread
    gpsTimer.scheduleAtFixedRate(new TimerTask() {

        @Override
        public void run() {
            Location location = getBestLocation();
            doLocationUpdate(location, false);
        }
    }, 0, checkInterval);
}

public void doLocationUpdate(Location l, boolean force) {
    long minDistance = getMinDistanceFromPrefs();
    Log.d(TAG, "update received:" + l);
    if (l == null) {
        Log.d(TAG, "Empty location");
        if (force)
            Toast.makeText(this, "Current location not available",
                    Toast.LENGTH_SHORT).show();
        return;
    }
    if (lastLocation != null) {
        float distance = l.distanceTo(lastLocation);
        Log.d(TAG, "Distance to last: " + distance);
        if (l.distanceTo(lastLocation) < minDistance && !force) {
            Log.d(TAG, "Position didn't change");
            return;
        }
        if (l.getAccuracy() >= lastLocation.getAccuracy()
                && l.distanceTo(lastLocation) < l.getAccuracy() && !force) {
            Log.d(TAG,
                    "Accuracy got worse and we are still "
                      + "within the accuracy range.. Not updating");
            return;
        }
        if (l.getTime() <= lastprovidertimestamp && !force) {
            Log.d(TAG, "Timestamp not never than last");
            return;
        }
    }
    // upload/store your location here
}

สิ่งที่ต้องพิจารณา:

  • ไม่ขออัปเดต GPS บ่อยเกินไปเพราะจะสิ้นเปลืองพลังงานแบตเตอรี่ ปัจจุบันฉันใช้เวลา 30 นาทีเป็นค่าเริ่มต้นสำหรับแอปพลิเคชันของฉัน

  • เพิ่มการตรวจสอบ 'ระยะห่างขั้นต่ำไปยังตำแหน่งที่ทราบล่าสุด' หากไม่มีสิ่งนี้คะแนนของคุณจะ "กระโดดไปรอบ ๆ " เมื่อไม่สามารถใช้งาน GPS ได้และตำแหน่งจะถูกวิเคราะห์จากเสาเซลล์ หรือคุณสามารถตรวจสอบว่าตำแหน่งใหม่นั้นอยู่นอกค่าความแม่นยำจากตำแหน่งที่ทราบล่าสุด


2
คุณไม่เคยได้รับตำแหน่งใหม่จริง ๆ คุณใช้เฉพาะตำแหน่งที่เกิดขึ้นจากการอัปเดตก่อนหน้า ฉันคิดว่ารหัสนี้จะเป็นประโยชน์อย่างมากโดยการเพิ่มผู้ฟังที่อัปเดตตำแหน่งโดยเปิด GPS เป็นครั้งคราว
Nicklas A.

2
ขออภัยฉันคิดว่าคุณสนใจเฉพาะส่วนที่เลือกที่ดีที่สุดจากสถานที่ที่มีอยู่ทั้งหมด ฉันเพิ่มรหัสด้านบนที่ขอสิ่งเหล่านี้เช่นกัน หากได้รับตำแหน่ง gps ใหม่จะถูกจัดเก็บ / อัพโหลดทันที หากฉันได้รับการอัปเดตตำแหน่งเครือข่ายฉันจะเก็บไว้เพื่อการอ้างอิงและ 'หวังว่า' ฉันจะได้รับการอัปเดต gps เช่นกันจนกว่าจะมีการตรวจสอบตำแหน่งถัดไป
Gryphius

2
ฉันยังมีเมธอด stopRecording () ซึ่งยกเลิกตัวจับเวลา ในที่สุดฉันก็เปลี่ยนจากตัวจับเวลาเป็น ScheduledThreadPoolExecutor ดังนั้น stopRecording ตอนนี้โดยทั่วไปจะเรียก executor.shutdown () และยกเลิกการลงทะเบียนฟังการอัพเดทตำแหน่งทั้งหมด
Gryphius

1
ตาม scm ของฉัน stopRecording เรียกว่า gpsTimer.cancel () เท่านั้นและตั้ง gps_recorder_running = false ดังนั้นเช่นในกรณีของคุณไม่มีการล้างข้อมูลของผู้ฟังในตอนนั้น รหัสปัจจุบันติดตามผู้ฟังที่ใช้งานอยู่ทั้งหมดในเวกเตอร์ฉันไม่มีสิ่งนี้เมื่อฉันเขียนคำตอบนี้เมื่อ 1.5 ปีที่แล้ว
Gryphius

1
มันใช้งานบน GitHubแล้ว แต่ฉันไม่แน่ใจว่านี่ยังเป็นวิธีที่ดีที่สุดในการทำสิ่ง GPS ในปัจจุบัน Afaik พวกเขาได้ทำการปรับปรุง API ตำแหน่งตั้งแต่ฉันเขียนรหัสนี้
Gryphius

33

ในการเลือกผู้ให้บริการตำแหน่งที่เหมาะสมสำหรับแอปของคุณคุณสามารถใช้วัตถุเกณฑ์ :

Criteria myCriteria = new Criteria();
myCriteria.setAccuracy(Criteria.ACCURACY_HIGH);
myCriteria.setPowerRequirement(Criteria.POWER_LOW);
// let Android select the right location provider for you
String myProvider = locationManager.getBestProvider(myCriteria, true); 

// finally require updates at -at least- the desired rate
long minTimeMillis = 600000; // 600,000 milliseconds make 10 minutes
locationManager.requestLocationUpdates(myProvider,minTimeMillis,0,locationListener); 

อ่านเอกสารสำหรับrequestLocationUpdatesสำหรับรายละเอียดเพิ่มเติมเกี่ยวกับวิธีพิจารณาข้อโต้แย้ง:

ความถี่ของการแจ้งเตือนอาจถูกควบคุมโดยใช้พารามิเตอร์ minTime และ minDistance หาก minTime มีค่ามากกว่า 0 LocationManager อาจหยุดพักเป็นเวลา minTime มิลลิวินาทีระหว่างการอัปเดตตำแหน่งเพื่อประหยัดพลังงาน หาก minDistance มากกว่า 0 ตำแหน่งจะถูกถ่ายทอดหากอุปกรณ์เคลื่อนที่โดย minDistance meters เท่านั้น หากต้องการรับการแจ้งเตือนบ่อยที่สุดให้ตั้งค่าพารามิเตอร์ทั้งสองเป็น 0

ความคิดเพิ่มเติม

  • คุณสามารถตรวจสอบความถูกต้องของวัตถุตำแหน่งด้วยLocation.getAccuracy ()ซึ่งจะคืนค่าความแม่นยำโดยประมาณของตำแหน่งเป็นเมตร
  • Criteria.ACCURACY_HIGHเกณฑ์ควรให้ข้อผิดพลาดด้านล่าง 100 เมตรซึ่งเป็นไม่ดีเท่าที่จีพีเอสได้ แต่ตรงกับความต้องการของคุณ
  • คุณต้องตรวจสอบสถานะของผู้ให้บริการตำแหน่งของคุณและเปลี่ยนไปใช้ผู้ให้บริการรายอื่นหากผู้ใช้ไม่สามารถใช้งานหรือปิดใช้งาน
  • ผู้ให้บริการเรื่อย ๆก็อาจจะเป็นคู่ที่ดีสำหรับชนิดของโปรแกรมนี้: ความคิดคือการใช้การปรับปรุงสถานที่เมื่อใดก็ตามที่พวกเขาจะได้รับการร้องขอจากแอปและการออกอากาศอีก systemwide

ฉันได้ตรวจสอบแล้วCriteriaแต่ถ้าตำแหน่งเครือข่ายล่าสุดนั้นยอดเยี่ยม (อาจรู้ผ่าน wifi) และไม่ต้องใช้เวลาหรือแบตเตอรี่ในการรับ (getLastKnown) จากนั้นเกณฑ์อาจไม่สนใจสิ่งนั้นและส่งคืน GPS แทน ฉันไม่อยากจะเชื่อว่า google ทำให้เรื่องนี้ยากสำหรับนักพัฒนา
Nicklas A.

นอกเหนือจากการใช้เกณฑ์คุณสามารถทำได้ในการอัปเดตตำแหน่งแต่ละครั้งที่ผู้ให้บริการที่คุณเลือกส่งให้ตรวจสอบ lastKnowLocation สำหรับผู้ให้บริการ GPS และเปรียบเทียบ (ความถูกต้องและวันที่) กับตำแหน่งปัจจุบันของคุณ แต่นี่ดูเหมือนจะเป็นสิ่งที่ดีสำหรับฉันมากกว่าความต้องการจากข้อกำหนดของคุณ หากบางครั้งมีความแม่นยำที่ดีขึ้นผู้ใช้ของคุณจะได้รับประโยชน์หรือไม่
Stéphane

นั่นคือสิ่งที่ฉันทำตอนนี้ปัญหาคือฉันมีเวลาหายากหากรู้ว่าพอรู้ดีพอ ฉันยังสามารถเพิ่มได้ว่าฉันไม่จำเป็นต้อง จำกัด ตัวเองให้กับผู้ให้บริการรายใดยิ่งฉันใช้ยิ่งฉันก็ยิ่งล็อคเร็วขึ้นเท่านั้น
Nicklas A.

โปรดทราบว่า PASSIVE_PROVIDER ต้องใช้ API ระดับ 8 ขึ้นไป
Eduardo

@ Stéphaneขอโทษสำหรับการแก้ไข อย่าดูแลมัน โพสต์ของคุณถูกต้อง ฉันแก้ไขข้อผิดพลาดนั้น ขอโทษ ความนับถือ.
Gaucho

10

ตอบสองคะแนนแรก :

  • จีพีเอสจะเสมอให้คุณได้ตำแหน่งที่แม่นยำมากขึ้นหากมีการเปิดใช้งานและหากไม่มีกำแพงหนารอบ

  • หากตำแหน่งไม่เปลี่ยนแปลงคุณสามารถโทรหาgetLastKnownLocation (String)และดึงข้อมูลตำแหน่งได้ทันที

ใช้วิธีการทางเลือก :

คุณสามารถลองรับรหัสเซลล์ที่ใช้งานหรือเซลล์ใกล้เคียงทั้งหมด

TelephonyManager mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
GsmCellLocation loc = (GsmCellLocation) mTelephonyManager.getCellLocation(); 
Log.d ("CID", Integer.toString(loc.getCid()));
Log.d ("LAC", Integer.toString(loc.getLac()));
// or 
List<NeighboringCellInfo> list = mTelephonyManager.getNeighboringCellInfo ();
for (NeighboringCellInfo cell : list) {
    Log.d ("CID", Integer.toString(cell.getCid()));
    Log.d ("LAC", Integer.toString(cell.getLac()));
}

คุณสามารถอ้างถึงตำแหน่งของเซลล์ผ่านฐานข้อมูลที่เปิดอยู่หลายแห่ง (เช่นhttp://www.location-api.com/หรือhttp://opencellid.org/ )


กลยุทธ์จะอ่านรายการรหัสทาวเวอร์เมื่ออ่านตำแหน่ง จากนั้นในการค้นหาถัดไป (10 นาทีในแอปของคุณ) อ่านอีกครั้ง getLastKnownLocation(String)ถ้าอย่างน้อยอาคารบางคนเดียวกันแล้วมันปลอดภัยที่จะใช้ onLocationChanged()หากพวกเขาไม่ได้แล้วรอ นี่เป็นการหลีกเลี่ยงความต้องการของฐานข้อมูลบุคคลที่สามสำหรับตำแหน่งที่ตั้ง คุณสามารถลองวิธีนี้


ใช่ แต่ฉันมีปัญหาเกิดขึ้นถ้าคนสุดท้ายที่รู้จักที่ตั้งแย่จริงๆ ฉันต้องการวิธีที่ดีในการตัดสินใจเลือกทำเลที่ดีที่สุดสองแห่ง
Nicklas A.

คุณสามารถจัดเก็บข้อมูลหอคอยและตรวจสอบว่ามีการเปลี่ยนแปลงเสาเหล่านั้นหรือไม่ หากเป็นเช่นนั้นให้รอตำแหน่งใหม่หากไม่ใช่ (หรือหากมีการเปลี่ยนแปลงเพียงบางส่วน ) ให้นำมาใช้ใหม่ ด้วยวิธีนี้คุณหลีกเลี่ยงการเปรียบเทียบตำแหน่งหอคอยกับฐานข้อมูล
Aleadam

การใช้หอคอยดูเหมือนจะเกินความคิดขนาดใหญ่สำหรับฉันเป็นความคิดที่ดี
Nicklas A.

@Nicklas โค้ดไม่ได้ซับซ้อนอะไรไปกว่านี้อีกแล้ว คุณจะต้องใช้ android.Manifest.permission # ACCESS_COARSE_UPDATES
Aleadam

ใช่ แต่ฉันยังต้องใช้บริการของบุคคลที่สามและฉันต้องการวิธีการตัดสินใจว่าเมื่อใดที่จะใช้ข้อมูลหอคอยเหนือข้อมูลตำแหน่งนี่เป็นเพียงการเพิ่มความซับซ้อนอีกชั้นหนึ่ง
Nicklas A.

9

นี่คือทางออกของฉันที่ทำงานค่อนข้างดี:

private Location bestLocation = null;
private Looper looper;
private boolean networkEnabled = false, gpsEnabled = false;

private synchronized void setLooper(Looper looper) {
    this.looper = looper;
}

private synchronized void stopLooper() {
    if (looper == null) return;
    looper.quit();
}

@Override
protected void runTask() {
    final LocationManager locationManager = (LocationManager) service
            .getSystemService(Context.LOCATION_SERVICE);
    final SharedPreferences prefs = getPreferences();
    final int maxPollingTime = Integer.parseInt(prefs.getString(
            POLLING_KEY, "0"));
    final int desiredAccuracy = Integer.parseInt(prefs.getString(
            DESIRED_KEY, "0"));
    final int acceptedAccuracy = Integer.parseInt(prefs.getString(
            ACCEPTED_KEY, "0"));
    final int maxAge = Integer.parseInt(prefs.getString(AGE_KEY, "0"));
    final String whichProvider = prefs.getString(PROVIDER_KEY, "any");
    final boolean canUseGps = whichProvider.equals("gps")
            || whichProvider.equals("any");
    final boolean canUseNetwork = whichProvider.equals("network")
            || whichProvider.equals("any");
    if (canUseNetwork)
        networkEnabled = locationManager
                .isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    if (canUseGps)
        gpsEnabled = locationManager
                .isProviderEnabled(LocationManager.GPS_PROVIDER);
    // If any provider is enabled now and we displayed a notification clear it.
    if (gpsEnabled || networkEnabled) removeErrorNotification();
    if (gpsEnabled)
        updateBestLocation(locationManager
                .getLastKnownLocation(LocationManager.GPS_PROVIDER));
    if (networkEnabled)
        updateBestLocation(locationManager
                .getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
    if (desiredAccuracy == 0
            || getLocationQuality(desiredAccuracy, acceptedAccuracy,
                    maxAge, bestLocation) != LocationQuality.GOOD) {
        // Define a listener that responds to location updates
        LocationListener locationListener = new LocationListener() {

            public void onLocationChanged(Location location) {
                updateBestLocation(location);
                if (desiredAccuracy != 0
                        && getLocationQuality(desiredAccuracy,
                                acceptedAccuracy, maxAge, bestLocation)
                                == LocationQuality.GOOD)
                    stopLooper();
            }

            public void onProviderEnabled(String provider) {
                if (isSameProvider(provider,
                        LocationManager.NETWORK_PROVIDER))networkEnabled =true;
                else if (isSameProvider(provider,
                        LocationManager.GPS_PROVIDER)) gpsEnabled = true;
                // The user has enabled a location, remove any error
                // notification
                if (canUseGps && gpsEnabled || canUseNetwork
                        && networkEnabled) removeErrorNotification();
            }

            public void onProviderDisabled(String provider) {
                if (isSameProvider(provider,
                        LocationManager.NETWORK_PROVIDER))networkEnabled=false;
                else if (isSameProvider(provider,
                        LocationManager.GPS_PROVIDER)) gpsEnabled = false;
                if (!gpsEnabled && !networkEnabled) {
                    showErrorNotification();
                    stopLooper();
                }
            }

            public void onStatusChanged(String provider, int status,
                    Bundle extras) {
                Log.i(LOG_TAG, "Provider " + provider + " statusChanged");
                if (isSameProvider(provider,
                        LocationManager.NETWORK_PROVIDER)) networkEnabled = 
                        status == LocationProvider.AVAILABLE
                        || status == LocationProvider.TEMPORARILY_UNAVAILABLE;
                else if (isSameProvider(provider,
                        LocationManager.GPS_PROVIDER))
                    gpsEnabled = status == LocationProvider.AVAILABLE
                      || status == LocationProvider.TEMPORARILY_UNAVAILABLE;
                // None of them are available, stop listening
                if (!networkEnabled && !gpsEnabled) {
                    showErrorNotification();
                    stopLooper();
                }
                // The user has enabled a location, remove any error
                // notification
                else if (canUseGps && gpsEnabled || canUseNetwork
                        && networkEnabled) removeErrorNotification();
            }
        };
        if (networkEnabled || gpsEnabled) {
            Looper.prepare();
            setLooper(Looper.myLooper());
            // Register the listener with the Location Manager to receive
            // location updates
            if (canUseGps)
                locationManager.requestLocationUpdates(
                        LocationManager.GPS_PROVIDER, 1000, 1,
                        locationListener, Looper.myLooper());
            if (canUseNetwork)
                locationManager.requestLocationUpdates(
                        LocationManager.NETWORK_PROVIDER, 1000, 1,
                        locationListener, Looper.myLooper());
            Timer t = new Timer();
            t.schedule(new TimerTask() {

                @Override
                public void run() {
                    stopLooper();
                }
            }, maxPollingTime * 1000);
            Looper.loop();
            t.cancel();
            setLooper(null);
            locationManager.removeUpdates(locationListener);
        } else // No provider is enabled, show a notification
        showErrorNotification();
    }
    if (getLocationQuality(desiredAccuracy, acceptedAccuracy, maxAge,
            bestLocation) != LocationQuality.BAD) {
        sendUpdate(new Event(EVENT_TYPE, locationToString(desiredAccuracy,
                acceptedAccuracy, maxAge, bestLocation)));
    } else Log.w(LOG_TAG, "LocationCollector failed to get a location");
}

private synchronized void showErrorNotification() {
    if (notifId != 0) return;
    ServiceHandler handler = service.getHandler();
    NotificationInfo ni = NotificationInfo.createSingleNotification(
            R.string.locationcollector_notif_ticker,
            R.string.locationcollector_notif_title,
            R.string.locationcollector_notif_text,
            android.R.drawable.stat_notify_error);
    Intent intent = new Intent(
            android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
    ni.pendingIntent = PendingIntent.getActivity(service, 0, intent,
            PendingIntent.FLAG_UPDATE_CURRENT);
    Message msg = handler.obtainMessage(ServiceHandler.SHOW_NOTIFICATION);
    msg.obj = ni;
    handler.sendMessage(msg);
    notifId = ni.id;
}

private void removeErrorNotification() {
    if (notifId == 0) return;
    ServiceHandler handler = service.getHandler();
    if (handler != null) {
        Message msg = handler.obtainMessage(
                ServiceHandler.CLEAR_NOTIFICATION, notifId, 0);
        handler.sendMessage(msg);
        notifId = 0;
    }
}

@Override
public void interrupt() {
    stopLooper();
    super.interrupt();
}

private String locationToString(int desiredAccuracy, int acceptedAccuracy,
        int maxAge, Location location) {
    StringBuilder sb = new StringBuilder();
    sb.append(String.format(
            "qual=%s time=%d prov=%s acc=%.1f lat=%f long=%f",
            getLocationQuality(desiredAccuracy, acceptedAccuracy, maxAge,
                    location), location.getTime() / 1000, // Millis to
                                                            // seconds
            location.getProvider(), location.getAccuracy(), location
                    .getLatitude(), location.getLongitude()));
    if (location.hasAltitude())
        sb.append(String.format(" alt=%.1f", location.getAltitude()));
    if (location.hasBearing())
        sb.append(String.format(" bearing=%.2f", location.getBearing()));
    return sb.toString();
}

private enum LocationQuality {
    BAD, ACCEPTED, GOOD;

    public String toString() {
        if (this == GOOD) return "Good";
        else if (this == ACCEPTED) return "Accepted";
        else return "Bad";
    }
}

private LocationQuality getLocationQuality(int desiredAccuracy,
        int acceptedAccuracy, int maxAge, Location location) {
    if (location == null) return LocationQuality.BAD;
    if (!location.hasAccuracy()) return LocationQuality.BAD;
    long currentTime = System.currentTimeMillis();
    if (currentTime - location.getTime() < maxAge * 1000
            && location.getAccuracy() <= desiredAccuracy)
        return LocationQuality.GOOD;
    if (acceptedAccuracy == -1
            || location.getAccuracy() <= acceptedAccuracy)
        return LocationQuality.ACCEPTED;
    return LocationQuality.BAD;
}

private synchronized void updateBestLocation(Location location) {
    bestLocation = getBestLocation(location, bestLocation);
}

protected Location getBestLocation(Location location,
        Location currentBestLocation) {
    if (currentBestLocation == null) {
        // A new location is always better than no location
        return location;
    }
    if (location == null) return currentBestLocation;
    // Check whether the new location fix is newer or older
    long timeDelta = location.getTime() - currentBestLocation.getTime();
    boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
    boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
    boolean isNewer = timeDelta > 0;
    // If it's been more than two minutes since the current location, use
    // the new location
    // because the user has likely moved
    if (isSignificantlyNewer) {
        return location;
        // If the new location is more than two minutes older, it must be
        // worse
    } else if (isSignificantlyOlder) {
        return currentBestLocation;
    }
    // Check whether the new location fix is more or less accurate
    int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation
            .getAccuracy());
    boolean isLessAccurate = accuracyDelta > 0;
    boolean isMoreAccurate = accuracyDelta < 0;
    boolean isSignificantlyLessAccurate = accuracyDelta > 200;
    // Check if the old and new location are from the same provider
    boolean isFromSameProvider = isSameProvider(location.getProvider(),
            currentBestLocation.getProvider());
    // Determine location quality using a combination of timeliness and
    // accuracy
    if (isMoreAccurate) {
        return location;
    } else if (isNewer && !isLessAccurate) {
        return location;
    } else if (isNewer && !isSignificantlyLessAccurate
            && isFromSameProvider) {
        return location;
    }
    return bestLocation;
}

/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
    if (provider1 == null) return provider2 == null;
    return provider1.equals(provider2);
}

สวัสดี Nicklas ฉันมีความเท่าเทียมกันดังนั้นฉันสามารถสื่อสารกับคุณด้วยวิธีการใด ๆ .. ฉันจะขอบคุณเต็มถ้าคุณสามารถช่วยเรา ..
School Boy

คุณช่วยโพสต์โค้ดทั้งหมดได้ไหม ขอบคุณขอบคุณจริง ๆ
rodi

นั่นคือรหัสทั้งหมด ฉันไม่สามารถเข้าถึงโครงการได้อีกต่อไป
Nicklas A.

1
คุณดูเหมือนจะใช้รหัสของโครงการนี้ "android-protips-location" และมันยังมีชีวิตอยู่ ผู้คนสามารถเห็นวิธีการทำงานที่นี่code.google.com/p/android-protips-location/source/browse/trunk/
......

7

ความแม่นยำของตำแหน่งขึ้นอยู่กับผู้ให้บริการตำแหน่งส่วนใหญ่ที่ใช้:

  1. GPS - คุณจะได้รับความแม่นยำหลายเมตร (สมมติว่าคุณรับสัญญาณ GPS)
  2. Wifi - คุณจะได้รับความแม่นยำเพียงไม่กี่ร้อยเมตร
  3. เครือข่ายมือถือ - คุณจะได้รับผลลัพธ์ที่ไม่ถูกต้องมาก (ฉันเห็นค่าเบี่ยงเบนสูงสุด 4 กม. ... )

หากเป็นความแม่นยำที่คุณต้องการ GPS คือตัวเลือกเดียวของคุณ

ฉันได้อ่านบทความที่ให้ข้อมูลเกี่ยวกับที่นี่มาก

สำหรับการหมดเวลา GPS - 60 วินาทีควรเพียงพอและในกรณีส่วนใหญ่ก็มากเกินไป ฉันคิดว่า 30 วินาทีก็โอเคและบางครั้งก็น้อยกว่า 5 วินาที ...

หากคุณต้องการเพียงตำแหน่งเดียวฉันขอแนะนำว่าในonLocationChangedวิธีการของคุณเมื่อคุณได้รับการอัปเดตคุณจะยกเลิกการลงทะเบียนผู้ฟังและหลีกเลี่ยงการใช้งาน GPS โดยไม่จำเป็น


ฉันไม่แคร์จากที่ที่ฉันได้รับตำแหน่งของฉันฉันไม่ต้องการ จำกัด ฉันให้กับผู้ให้บริการรายเดียว
Nicklas A.

คุณสามารถลงทะเบียนผู้ให้บริการตำแหน่งทั้งหมดที่มีอยู่ในอุปกรณ์ (คุณสามารถรับรายชื่อผู้ให้บริการทั้งหมดจาก LocationManager.getProviders ()) แต่ถ้าคุณกำลังมองหาวิธีแก้ไขที่ถูกต้องในกรณีส่วนใหญ่ผู้ให้บริการเครือข่ายจะไม่เป็นประโยชน์สำหรับคุณ
Muzikant

ใช่ แต่นี่ไม่ใช่คำถามเกี่ยวกับการเลือกระหว่างผู้ให้บริการนี่เป็นคำถามเกี่ยวกับการได้รับตำแหน่งที่ดีที่สุดโดยทั่วไป (แม้ว่าจะมีผู้ให้บริการหลายรายที่เกี่ยวข้อง)
Nicklas A.

4

ขณะนี้ฉันกำลังใช้เนื่องจากเป็นที่เชื่อถือได้ในการหาที่ตั้งและคำนวณระยะทางสำหรับแอปพลิเคชันของฉัน ...... ฉันใช้สิ่งนี้กับแอปพลิเคชันรถแท็กซี่ของฉัน

ใช้ฟิวชั่น API ที่นักพัฒนาของ Google ได้พัฒนาด้วยฟิวชั่นของเซ็นเซอร์ GPS, Magnetometer, Accelerometer นอกจากนี้ยังใช้ Wifi หรือตำแหน่งเซลล์เพื่อคำนวณหรือประมาณตำแหน่ง นอกจากนี้ยังสามารถให้ข้อมูลอัปเดตตำแหน่งภายในอาคารได้อย่างถูกต้อง เพื่อดูรายละเอียดไปที่ลิงก์ https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderApi

import android.app.Activity;
import android.location.Location;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;


public class MainActivity extends Activity implements LocationListener,
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

    private static final long ONE_MIN = 500;
    private static final long TWO_MIN = 500;
    private static final long FIVE_MIN = 500;
    private static final long POLLING_FREQ = 1000 * 20;
    private static final long FASTEST_UPDATE_FREQ = 1000 * 5;
    private static final float MIN_ACCURACY = 1.0f;
    private static final float MIN_LAST_READ_ACCURACY = 1;

    private LocationRequest mLocationRequest;
    private Location mBestReading;
TextView tv;
    private GoogleApiClient mGoogleApiClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (!servicesAvailable()) {
            finish();
        }

        setContentView(R.layout.activity_main);
tv= (TextView) findViewById(R.id.tv1);
        mLocationRequest = LocationRequest.create();
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        mLocationRequest.setInterval(POLLING_FREQ);
        mLocationRequest.setFastestInterval(FASTEST_UPDATE_FREQ);

        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addApi(LocationServices.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();


        if (mGoogleApiClient != null) {
            mGoogleApiClient.connect();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();

        if (mGoogleApiClient != null) {
            mGoogleApiClient.connect();
        }
    }

    @Override
    protected void onPause() {d
        super.onPause();

        if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
            mGoogleApiClient.disconnect();
        }
    }


        tv.setText(location + "");
        // Determine whether new location is better than current best
        // estimate
        if (null == mBestReading || location.getAccuracy() < mBestReading.getAccuracy()) {
            mBestReading = location;


            if (mBestReading.getAccuracy() < MIN_ACCURACY) {
                LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
            }
        }
    }

    @Override
    public void onConnected(Bundle dataBundle) {
        // Get first reading. Get additional location updates if necessary
        if (servicesAvailable()) {

            // Get best last location measurement meeting criteria
            mBestReading = bestLastKnownLocation(MIN_LAST_READ_ACCURACY, FIVE_MIN);

            if (null == mBestReading
                    || mBestReading.getAccuracy() > MIN_LAST_READ_ACCURACY
                    || mBestReading.getTime() < System.currentTimeMillis() - TWO_MIN) {

                LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);

               //Schedule a runnable to unregister location listeners

                    @Override
                    public void run() {
                        LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, MainActivity.this);

                    }

                }, ONE_MIN, TimeUnit.MILLISECONDS);

            }

        }
    }

    @Override
    public void onConnectionSuspended(int i) {

    }


    private Location bestLastKnownLocation(float minAccuracy, long minTime) {
        Location bestResult = null;
        float bestAccuracy = Float.MAX_VALUE;
        long bestTime = Long.MIN_VALUE;

        // Get the best most recent location currently available
        Location mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        //tv.setText(mCurrentLocation+"");
        if (mCurrentLocation != null) {
            float accuracy = mCurrentLocation.getAccuracy();
            long time = mCurrentLocation.getTime();

            if (accuracy < bestAccuracy) {
                bestResult = mCurrentLocation;
                bestAccuracy = accuracy;
                bestTime = time;
            }
        }

        // Return best reading or null
        if (bestAccuracy > minAccuracy || bestTime < minTime) {
            return null;
        }
        else {
            return bestResult;
        }
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {

    }

    private boolean servicesAvailable() {
        int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);

        if (ConnectionResult.SUCCESS == resultCode) {
            return true;
        }
        else {
            GooglePlayServicesUtil.getErrorDialog(resultCode, this, 0).show();
            return false;
        }
    }
}

2

ฉันขัดจังหวะอินเทอร์เน็ตเพื่อหาคำตอบ (ปีที่ผ่านมา) ที่ได้รับการปรับปรุงโดยใช้วิธีการดึงตำแหน่งล่าสุดที่ Google แนะนำ (เพื่อใช้ FusedLocationProviderClient) ในที่สุดฉันก็ลงจอดบนนี้:

https://github.com/googlesamples/android-play-location/tree/master/LocationUpdates

ฉันสร้างโครงการใหม่และคัดลอกในส่วนใหญ่ของรหัสนี้ ความเจริญ มันได้ผล. และฉันคิดว่าไม่มีบรรทัดที่คัดค้าน

นอกจากนี้เครื่องมือจำลองดูเหมือนจะไม่ได้รับตำแหน่ง GPS ที่ฉันรู้ มันได้รับเท่าที่รายงานในบันทึก: "การตั้งค่าตำแหน่งทั้งหมดพอใจ"

และในที่สุดในกรณีที่คุณต้องการที่จะรู้ว่า (ฉันได้) คุณไม่จำเป็นต้องมีคีย์ Google Maps api จากคอนโซลนักพัฒนาซอฟต์แวร์ของ Google หากคุณต้องการเพียงแค่ตำแหน่ง GPS

ยังมีประโยชน์คือการสอนของพวกเขา แต่ฉันต้องการตัวอย่างการสอน / โค้ดหนึ่งหน้าแบบเต็มและนั่นก็คือ บทช่วยสอนของพวกเขาเรียงซ้อน แต่สับสนเมื่อคุณยังใหม่กับสิ่งนี้เพราะคุณไม่รู้ว่าคุณต้องการชิ้นส่วนใดจากหน้าก่อนหน้า

https://developer.android.com/training/location/index.html

และสุดท้ายจำสิ่งต่าง ๆ เช่นนี้:

ฉันไม่เพียง แต่ต้องแก้ไข mainActivity.Java ฉันยังต้องแก้ไข Strings.xml, androidmanifest.xml และ build.gradle ที่ถูกต้อง และ activity_Main.xml ของคุณ (แต่ส่วนนั้นง่ายสำหรับฉัน)

ฉันต้องการเพิ่มการพึ่งพาเช่นนี้: การนำไปใช้ 'com.google.android.gms: play-services-location: 11.8.0' และอัปเดตการตั้งค่าของ SDK สตูดิโอ Android ของฉันเพื่อรวมบริการ Google Play (การตั้งค่าไฟล์ลักษณะการตั้งค่าระบบ Android SDK SDK เครื่องมือตรวจสอบบริการ Google Play)

อัปเดต: ตัวจำลอง android ดูเหมือนจะได้รับเหตุการณ์การเปลี่ยนตำแหน่งและตำแหน่งที่ตั้ง (เมื่อฉันเปลี่ยนค่าในการตั้งค่าของซิม) แต่ผลลัพธ์ที่ดีที่สุดและแรกของฉันอยู่บนอุปกรณ์จริง ดังนั้นอาจทดสอบอุปกรณ์จริงได้ง่ายที่สุด


1

เมื่อไม่นานมานี้ได้รับการปรับโครงสร้างใหม่เพื่อให้ได้ตำแหน่งของโค้ดเรียนรู้แนวคิดที่ดีและในที่สุดก็ประสบความสำเร็จในการสร้างไลบรารี่และการสาธิตที่สมบูรณ์แบบ

@ คำตอบของ Gryphius นั้นดี

    //request all valid provider(network/gps)
private boolean requestAllProviderUpdates() {
    checkRuntimeEnvironment();
    checkPermission();

    if (isRequesting) {
        EasyLog.d("Request location update is busy");
        return false;
    }


    long minTime = getCheckTimeInterval();
    float minDistance = getCheckMinDistance();

    if (mMapLocationListeners == null) {
        mMapLocationListeners = new HashMap<>();
    }

    mValidProviders = getValidProviders();
    if (mValidProviders == null || mValidProviders.isEmpty()) {
        throw new IllegalArgumentException("Not available provider.");
    }

    for (String provider : mValidProviders) {
        LocationListener locationListener = new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {
                if (location == null) {
                    EasyLog.e("LocationListener callback location is null.");
                    return;
                }
                printf(location);
                mLastProviderTimestamp = location.getTime();

                if (location.getProvider().equals(LocationManager.GPS_PROVIDER)) {
                    finishResult(location);
                } else {
                    doLocationResult(location);
                }

                removeProvider(location.getProvider());
                if (isEmptyValidProviders()) {
                    requestTimeoutMsgInit();
                    removeUpdates();
                }
            }

            @Override
            public void onStatusChanged(String provider, int status, Bundle extras) {
            }

            @Override
            public void onProviderEnabled(String provider) {
            }

            @Override
            public void onProviderDisabled(String provider) {
            }
        };
        getLocationManager().requestLocationUpdates(provider, minTime, minDistance, locationListener);
        mMapLocationListeners.put(provider, locationListener);
        EasyLog.d("Location request %s provider update.", provider);
    }
    isRequesting = true;
    return true;
}

//remove request update
public void removeUpdates() {
    checkRuntimeEnvironment();

    LocationManager locationManager = getLocationManager();
    if (mMapLocationListeners != null) {
        Set<String> keys = mMapLocationListeners.keySet();
        for (String key : keys) {
            LocationListener locationListener = mMapLocationListeners.get(key);
            if (locationListener != null) {
                locationManager.removeUpdates(locationListener);
                EasyLog.d("Remove location update, provider is " + key);
            }
        }
        mMapLocationListeners.clear();
        isRequesting = false;
    }
}

//Compared with the last successful position, to determine whether you need to filter
private boolean isNeedFilter(Location location) {
    checkLocation(location);

    if (mLastLocation != null) {
        float distance = location.distanceTo(mLastLocation);
        if (distance < getCheckMinDistance()) {
            return true;
        }
        if (location.getAccuracy() >= mLastLocation.getAccuracy()
                && distance < location.getAccuracy()) {
            return true;
        }
        if (location.getTime() <= mLastProviderTimestamp) {
            return true;
        }
    }
    return false;
}

private void doLocationResult(Location location) {
    checkLocation(location);

    if (isNeedFilter(location)) {
        EasyLog.d("location need to filtered out, timestamp is " + location.getTime());
        finishResult(mLastLocation);
    } else {
        finishResult(location);
    }
}

//Return to the finished position
private void finishResult(Location location) {
    checkLocation(location);

    double latitude = location.getLatitude();
    double longitude = location.getLongitude();
    float accuracy = location.getAccuracy();
    long time = location.getTime();
    String provider = location.getProvider();

    if (mLocationResultListeners != null && !mLocationResultListeners.isEmpty()) {
        String format = "Location result:<%f, %f> Accuracy:%f Time:%d Provider:%s";
        EasyLog.i(String.format(format, latitude, longitude, accuracy, time, provider));

        mLastLocation = location;
        synchronized (this) {
            Iterator<LocationResultListener> iterator =  mLocationResultListeners.iterator();
            while (iterator.hasNext()) {
                LocationResultListener listener = iterator.next();
                if (listener != null) {
                    listener.onResult(location);
                }
                iterator.remove();
            }
        }
    }
}

การติดตั้งให้สมบูรณ์: https://github.com/bingerz/FastLocation/blob/master/fastlocationlib/src/main/java/cn/bingerz/fastlocation/FastLocation.java

1. ขอบคุณ @Gryphius แนวคิดการแก้ปัญหาฉันยังแบ่งปันรหัสที่สมบูรณ์

2. คำขอแต่ละครั้งเพื่อให้ตำแหน่งเสร็จสมบูรณ์ดีที่สุดคือลบการอัปเดตมิฉะนั้นแถบสถานะโทรศัพท์จะแสดงไอคอนตำแหน่ง


0

จากประสบการณ์ของฉันฉันพบว่าดีที่สุดที่จะไปกับการแก้ไข GPS เว้นแต่จะไม่สามารถใช้ได้ ฉันไม่รู้อะไรมากเกี่ยวกับผู้ให้บริการตำแหน่งรายอื่น แต่ฉันรู้ว่าสำหรับ GPS มีเทคนิคเล็กน้อยที่สามารถใช้เพื่อวัดความแม่นยำของสลัม ระดับความสูงมักเป็นสัญญาณดังนั้นคุณสามารถตรวจสอบค่าที่ไร้สาระได้ มีการวัดความแม่นยำในการแก้ไขตำแหน่งของ Android นอกจากนี้หากคุณเห็นจำนวนดาวเทียมที่ใช้สิ่งนี้ก็สามารถบ่งบอกถึงความแม่นยำได้เช่นกัน

วิธีที่น่าสนใจที่จะได้ความคิดที่ดีขึ้นเกี่ยวกับความถูกต้องคือการขอชุดของการแก้ไขอย่างรวดเร็วเช่น ~ 1 / วินาทีเป็นเวลา 10 วินาทีแล้วนอนเป็นเวลาหนึ่งหรือสองนาที การพูดคุยอย่างหนึ่งที่ฉันเคยทำทำให้เชื่อว่าอุปกรณ์ Android บางอย่างจะทำเช่นนี้ต่อไป จากนั้นคุณก็จะกำจัดผู้ที่ผิด (ฉันเคยได้ยินตัวกรองคาลมานที่กล่าวถึงที่นี่) และใช้กลยุทธ์การจัดกึ่งกลางบางอย่างเพื่อรับการแก้ไขเดียว

เห็นได้ชัดว่าความลึกที่คุณได้รับที่นี่ขึ้นอยู่กับความต้องการของคุณ หากคุณมีข้อกำหนดที่เข้มงวดเป็นพิเศษเพื่อให้ได้ตำแหน่งที่ดีที่สุดฉันคิดว่าคุณจะพบว่าตำแหน่ง GPS และเครือข่ายนั้นใกล้เคียงกับแอปเปิ้ลและส้ม GPS สามารถแตกต่างกันอย่างมากจากอุปกรณ์ไปยังอุปกรณ์


มันไม่สำคัญว่ามันจะดีที่สุดแค่ว่ามันดีพอที่จะลงจุดบนแผนที่และฉันไม่ระบายแบตเตอรี่เพราะนี่เป็นภารกิจพื้นหลัง
Nicklas A.

-3

Skyhook (http://www.skyhookwireless.com/) มีผู้ให้บริการตำแหน่งที่เร็วกว่ามาตรฐานที่ Google ให้มามาก อาจเป็นสิ่งที่คุณกำลังมองหา ฉันไม่ได้มีส่วนเกี่ยวข้องกับพวกเขา


น่าสนใจจริง ๆ พวกเขาดูเหมือนจะใช้ WiFi แต่มันก็สวยมาก แต่ฉันยังคงต้องการใช้งานเมื่อไม่มีการเชื่อมต่อ wifi หรือ 3G / 2G 3G รอบนี้จะเพิ่มสิ่งที่เป็นนามธรรมอีกชั้นหนึ่ง จับดีแม้ว่า
Nicklas A.

1
Skyhook ดูเหมือนจะใช้การรวมกันของ WiFi, GPS และเสาสัญญาณโทรศัพท์ ดูskyhookwireless.com/howitworksสำหรับรายละเอียดทางเทคนิค พวกเขาได้รับชัยชนะหลายครั้งเมื่อไม่นานมานี้เช่น Mapquest, Twydroid, ShopSavvy และ Sony NGP โปรดทราบว่าการดาวน์โหลดและทดลองใช้ SDK ของพวกเขานั้นฟรี แต่คุณต้องติดต่อพวกเขาเกี่ยวกับใบอนุญาตสำหรับการเผยแพร่ในแอปของคุณ น่าเสียดายที่พวกเขาไม่ได้ทำรายการราคาบนเว็บไซต์
Ed Burnette

อ้อเข้าใจแล้ว. ถ้ามันไม่สามารถใช้ในเชิงพาณิชย์ได้ฟรีฉันกลัวว่าฉันจะใช้มันไม่ได้
Nicklas A.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.