簡體   English   中英

在android中快速獲取當前位置一次

[英]get the current location fast and once in android

我有一個需要設備當前位置(緯度和經度)的 android 應用程序。 我在網上嘗試了一些教程,特別是一些堆棧溢出的解決方案,但它們對我來說效果不佳。 我的要求非常簡單:首先,我需要速度快,並且在片段啟動時需要一次位置。 其次,我需要它盡可能精確,我的意思是如果 GPS 不可用,它應該首先使用 GPS,然后使用網絡提供商。

例如,我已經嘗試過這個解決方案,但它在 30 秒后返回 null,但我知道有一些一切都可以,因為谷歌地圖和其他應用程序運行良好!!!

幾乎所有答案都建議使用 getLastKnownLocation(),但我想它不是當前的,如果是這樣的話,我不想要它。

任何人都可以向我建議某種簡單快捷的方法來一次性獲得位置嗎?!

在這里,你可以使用這個...

用法示例:

public void foo(Context context) {
  // when you need location
  // if inside activity context = this;

  SingleShotLocationProvider.requestSingleUpdate(context, 
   new SingleShotLocationProvider.LocationCallback() {
     @Override public void onNewLocationAvailable(GPSCoordinates location) {
       Log.d("Location", "my location is " + location.toString());
     }
   });
}

您可能想要驗證緯度/經度是實際值而不是 0 或其他值。 如果我沒記錯的話,這不應該拋出 NPE,但您可能想驗證一下。

public class SingleShotLocationProvider {

  public static interface LocationCallback {
      public void onNewLocationAvailable(GPSCoordinates location);
  }

  // calls back to calling thread, note this is for low grain: if you want higher precision, swap the 
  // contents of the else and if. Also be sure to check gps permission/settings are allowed.
  // call usually takes <10ms
  public static void requestSingleUpdate(final Context context, final LocationCallback callback) {
      final LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
      boolean isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
      if (isNetworkEnabled) {
          Criteria criteria = new Criteria();
          criteria.setAccuracy(Criteria.ACCURACY_COARSE);
          locationManager.requestSingleUpdate(criteria, new LocationListener() {
              @Override
              public void onLocationChanged(Location location) {
                  callback.onNewLocationAvailable(new GPSCoordinates(location.getLatitude(), location.getLongitude()));
              }

              @Override public void onStatusChanged(String provider, int status, Bundle extras) { }
              @Override public void onProviderEnabled(String provider) { }
              @Override public void onProviderDisabled(String provider) { }
          }, null);
      } else {
          boolean isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
          if (isGPSEnabled) {
              Criteria criteria = new Criteria();
              criteria.setAccuracy(Criteria.ACCURACY_FINE);
              locationManager.requestSingleUpdate(criteria, new LocationListener() {
                  @Override
                  public void onLocationChanged(Location location) {
                      callback.onNewLocationAvailable(new GPSCoordinates(location.getLatitude(), location.getLongitude()));
                  }

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


  // consider returning Location instead of this dummy wrapper class
  public static class GPSCoordinates {
      public float longitude = -1;
      public float latitude = -1;

      public GPSCoordinates(float theLatitude, float theLongitude) {
          longitude = theLongitude;
          latitude = theLatitude;
      }

      public GPSCoordinates(double theLatitude, double theLongitude) {
          longitude = (float) theLongitude;
          latitude = (float) theLatitude;
      }
  }  
}

AndroidManifest.xml

 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-feature android:name="android.hardware.location.gps" />

請求用戶權限


build.gradle (模塊:app)

dependencies {
    ...
    implementation 'com.google.android.gms:play-services-location:15.0.0'
    ...
}

如果您收到錯誤,請檢查您的頂級 build.gradle 是否包含對 google() 存儲庫或 maven { url " https://maven.google.com " } 的引用

設置 Google Play 服務


位置服務.kt

import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.location.Location
import android.net.Uri
import android.os.Looper
import android.provider.Settings
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.common.api.ResolvableApiException
import com.google.android.gms.location.*
import org.jetbrains.anko.alert
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.okButton

object LocationService {

    @SuppressLint("StaticFieldLeak")
    private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
    private lateinit var locationRequest: LocationRequest
    private val locationCallback = object : LocationCallback() {
        override fun onLocationResult(locationResult: LocationResult) {
            doAsync {
                location = locationResult.lastLocation
                onSuccess(location)
            }
        }
    }
    private lateinit var onSuccess: (location : Location) -> Unit
    private lateinit var onError: () -> Unit
    lateinit var location: Location

    fun init(activity: Activity) {
        fusedLocationProviderClient = FusedLocationProviderClient(activity)
        locationRequest = LocationRequest().setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY).setInterval(1000).setFastestInterval(1000).setNumUpdates(1)
    }

    private fun checkLocationStatusAndGetLocation(activity: Activity) {
        doAsync {
            when {
                ContextCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED -> LocationServices.getSettingsClient(activity).checkLocationSettings(LocationSettingsRequest.Builder().addLocationRequest(locationRequest).setAlwaysShow(true).build()).addOnCompleteListener { task ->
                    doAsync {
                        try {
                            task.getResult(ApiException::class.java)
                            fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
                        } catch (exception: ApiException) {
                            when (exception.statusCode) {
                                LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> {
                                    try {
                                        (exception as ResolvableApiException).startResolutionForResult(activity, 7025)
                                    } catch (ex: Exception) {
                                        promptShowLocation(activity)
                                    }
                                }
                                LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
                                    promptShowLocation(activity)
                                }
                            }
                        }
                    }
                }
                ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.ACCESS_FINE_LOCATION) -> activity.runOnUiThread {
                    activity.alert("To continue, allow the device to use location, witch uses Google's Location Service") {
                        okButton {
                            val ite = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts("package", activity.packageName, null))
                            ite.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                            activity.startActivity(ite)
                            onError()
                        }
                        negativeButton("Cancelar", { onError() })
                        onCancelled { onError() }
                    }.show()
                }
                else -> ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 7024)
            }
        }
    }

    private fun promptShowLocation(activity: Activity) {
        activity.runOnUiThread {
            activity.alert("To continue, allow the device to use location, witch uses Google's Location Service") {
                okButton {
                    activity.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))
                    onError()
                }
                negativeButton("Cancelar", { onError() })
                onCancelled { onError() }
            }.show()
        }
    }

    fun onRequestPermissionsResult(activity: Activity, requestCode: Int, grantResults: IntArray) {
        if (requestCode == 7024) {
            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                checkLocationStatusAndGetLocation(activity)
            } else {
                onError()
            }
        }
    }

    fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int) {
        if (requestCode == 7025) {
            if (resultCode == Activity.RESULT_OK) {
                checkLocationStatusAndGetLocation(activity)
            } else {
                onError()
            }
        }
    }

    fun getLocation(activity: Activity, onSuccess: () -> Unit, onError: () -> Unit) {
        this.onSuccess = onSuccess
        this.onError = onError
        checkLocationStatusAndGetLocation(activity)
    }

}

你的活動

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    LocationService.init(this)
}

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    LocationService.onRequestPermissionsResult(this, requestCode, grantResults)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    LocationService.onActivityResult(this, requestCode, resultCode)
}

private fun yourFunction() {
    LocationService.getLocation(this, { location ->
        //TODO: use the location
    }, {
        //TODO: display error message
    })
}

AndroidManifest.xml:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:name="android.hardware.location.gps" />

主活動.java:

public class MainActivity extends AppCompatActivity implements LocationListener {

    private LocationManager locationManager;
    private Location onlyOneLocation;
    private final int REQUEST_FINE_LOCATION = 1234;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)
            ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_FINE_LOCATION);
    }

    @Override public void onLocationChanged(Location location) {
        onlyOneLocation = location;
        locationManager.removeUpdates(this);
    }
    @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 onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
        case REQUEST_FINE_LOCATION:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Log.d("gps", "Location permission granted");
                try {
                    locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
                    locationManager.requestLocationUpdates("gps", 0, 0, this);
                }
                catch (SecurityException ex) {
                    Log.d("gps", "Location permission did not work!");
                }
            }
            break;
    }
}

您想要做的是使用LocationManager#requestSingleUpdate來實現。 此方法在給定的 Looper 中附加一個偵聽器(如果您想要或擁有它)並盡快通知它接收到的位置,僅一次。 您建議的方法僅用作在給您真正的位置之前的不精確位置。

在任何情況下,它都會比毫秒快(除非你有幸在某個位置到達設備時開始收聽)。 將 GPS 視為您在等待位置時啟用並在您刪除此偵聽時禁用的元素。 這樣做是為了避免耗盡用戶的電池電量。

所以,總結一下:

  • 您開始收聽和接收位置之間的時間取決於設備的 GPS(制造商、用戶位置、衛星覆蓋范圍...)
  • Android SDK 中有一種方法可以監聽單個更新。
  • 通過提供標准對象,您可以管理哪些標准可以讓您接收位置。 更強的標准意味着有更多的時間來獲得准確的響應。

對於任何有興趣以最佳、慣用方式、使用最新 API 和 Kotlin 的魔力檢索單個位置更新的人,請訪問:

Gradle依賴:

dependencies {
    ...
    implementation "com.google.android.gms:play-services-location:18.0.0"
    ...
}

清單權限:

<manifest>
    ...
    <!-- required only for LocationRequest.PRIORITY_HIGH_ACCURACY -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> 
    <!-- required for all other priorities -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    ...
</manifest>

在您的Extensions文件中的某處:

// To use PRIORITY_HIGH_ACCURACY, you must have ACCESS_FINE_LOCATION permission.
// Any other priority will require just ACCESS_COARSE_LOCATION,
// but will not guarantee a location update
@SuppressLint("MissingPermission")
suspend fun FusedLocationProviderClient.awaitCurrentLocation(priority: Int): Location? {
    return suspendCancellableCoroutine {
        // to use for request cancellation upon coroutine cancellation
        val cts = CancellationTokenSource()
        getCurrentLocation(priority, cts.token)
            .addOnSuccessListener {location ->
                // remember location is nullable, this happens sometimes
                // when the request expires before an update is acquired
                it.resume(location)
            }.addOnFailureListener {e ->
                it.resumeWithException(e)
            }

        it.invokeOnCancellation {
            cts.cancel()
        }
    }
}

在你的片段中:

// need to register this anywhere before onCreateView, idealy as a field
private val permissionRequester = registerForActivityResult(
    // you can use RequestPermission() contract if you only need 1 permission
    ActivityResultContracts.RequestMultiplePermissions()
) { map ->
    // If you requested 1 permission, change `map` to `isGranted`
    // Keys are permissions Strings, values are isGranted Booleans
    // An easy way to check if "any" permission was granted is map.containsValue(true)
    // You can use your own logic for multiple permissions, 
    // but they have to follow the same checks here:
    val response = map.entries.first()
    val permission = response.key
    val isGranted = response.value
    when {
        isGranted -> onPermissionGranted()
        ActivityCompat.shouldShowRequestPermissionRationale(requireContext(), permission) -> {
            // permission denied but not permanently, tell user why you need it. 
            // Idealy provide a button to request it again and another to dismiss
            AlertDialog.Builder(requireContext())
                .setTitle(R.string.perm_request_rationale_title)
                .setMessage(R.string.perm_request_rationale)
                .setPositiveButton(R.string.request_perm_again) { _, _ -> 
                     requirePermission() 
                }
                .setNegativeButton(R.string.dismiss, null)
                .create()
                .show()
        } 
        else -> {
            // permission permanently denied
            // 1) tell user the app won't work as expected, or
            // 2) take him to your app's info screen to manually change permissions, or
            // 3) silently and gracefully degrade user experience
            // I'll leave the implementation to you
        }
    }
}

onPermissionGranted 函數:

private fun onPermissionGranted() {
    val lm = requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager
    if(LocationManagerCompat.isLocationEnabled(lm)) {
        // you can do this your own way, eg. from a viewModel
        // but here is where you wanna start the coroutine.
        // Choose your priority based on the permission you required
        val priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        lifecycleScope.launch {
            val location = LocationServices
                .getFusedLocationProviderClient(requireContext())
                .awaitCurrentLocation(priority)
            // do whatever with this location, notice that it's nullable
        }
    } else {
        // prompt user to enable location or launch location settings check
    }
}

現在您要做的就是將其添加到 MyLocation 按鈕單擊偵聽器:

private fun requirePermission() {
    val permissions = arrayOf(
        Manifest.permission.ACCESS_FINE_LOCATION,
        // optional: Manifest.permission.ACCESS_COARSE_LOCATION
    )
    permissionRequester.launch(permissions)
}

請注意,這具有檢查權限是否已隱式授予的優點,如果是這種情況,則不顯示對話框/請求。 因此,始終通過啟動請求者來啟動流程,並且僅在其回調中進行檢查。

    // Get LocationManager object
    LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

    // Create a criteria object to retrieve provider
    Criteria criteria = new Criteria();

    // Get the name of the best provider
    String provider = locationManager.getBestProvider(criteria, true);

    // Get Current Location
    Location myLocation = locationManager.getLastKnownLocation(provider);

    //latitude of location
    double myLatitude = myLocation.getLatitude();

    //longitude og location
    double myLongitude = myLocation.getLongitude();

    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        // TODO: Consider calling
        //    ActivityCompat#requestPermissions
        // here to request the missing permissions, and then overriding
        //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
        //                                          int[] grantResults)
        // to handle the case where the user grants the permission. See the documentation
        // for ActivityCompat#requestPermissions for more details.
        return;
    }

以上所有答案對我都不起作用,所以我回答了這個最初添加依賴項

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:name="android.hardware.location.gps" />

添加類 MyLocationListiner.java 后

package com.example.firebase_auth;

/**
 * Created by Chromicle(Ajay Prabhakar).
 */

import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;

import static android.content.Context.LOCATION_SERVICE;

public class MyLocationListener implements LocationListener {

    public static double latitude;
    Context ctx;
    Location location;
    LocationManager locationManager;
    boolean isGPSEnabled = false;
    boolean isNetworkEnabled = false;
    public static double longitude;
    MyLocationListener(Context ctx) {
        this.ctx = ctx;
        try {
            locationManager = (LocationManager) ctx.getSystemService(LOCATION_SERVICE);
            isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
            Toast.makeText(ctx, "GPS Enable " + isGPSEnabled, Toast.LENGTH_LONG).show();
            isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
            Toast.makeText(ctx, "Network Enable " + isNetworkEnabled, Toast.LENGTH_LONG).show();

            if ( Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission
                    ( ctx, android.Manifest.permission.ACCESS_FINE_LOCATION )
                    != PackageManager.PERMISSION_GRANTED &&
                    ContextCompat.checkSelfPermission( ctx,
                            android.Manifest.permission.ACCESS_COARSE_LOCATION) !=
                            PackageManager.PERMISSION_GRANTED) {  }
            if (isGPSEnabled == true) {
                locationManager.requestLocationUpdates(
                        LocationManager.GPS_PROVIDER,     0,       0, this);
                location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
            }
            if (isNetworkEnabled==true) {
                locationManager.requestLocationUpdates(
                        LocationManager.NETWORK_PROVIDER,    0,     0, this);
                location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
            }
            latitude = location.getLatitude();
            longitude = location.getLongitude();
            // Toast.makeText(ctx,"latitude: "+latitude+" longitude: "+longitude,Toast.LENGTH_LONG).show();


        }
        catch(Exception ex)
        {

            Toast.makeText(ctx,"Exception "+ex, Toast.LENGTH_LONG).show();
        }
    }
    @Nullable
    @Override
    public void onLocationChanged(Location loc)
    {
        loc.getLatitude();
        loc.getLongitude();
        latitude=loc.getLatitude();
        longitude=loc.getLongitude();
    }

    @Override
    public void onProviderDisabled(String provider)
    {
        //print "Currently GPS is Disabled";
    }
    @Override
    public void onProviderEnabled(String provider)
    {
        //print "GPS got Enabled";
    }
    @Override
    public void onStatusChanged(String provider, int status, Bundle extras)
    {

    }
}

要使用該類添加此方法位置存儲在地址字符串中

public void getLocation(){
        Double latitude = 0.0, longitude;
        String message = "";
        LocationManager mlocManager = null;
        LocationListener mlocListener;
        mlocManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        mlocListener = new MyLocationListener(this);
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        mlocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mlocListener);
        if (mlocManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {

            latitude = MyLocationListener.latitude;
            longitude = MyLocationListener.longitude;
            message = message +"https://www.google.com/maps/dir/@"+ latitude +","+  longitude;
            address=message;
            Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
            if (latitude == 0.0) {
                Toast.makeText(getApplicationContext(), "Currently gps has not found your location....", Toast.LENGTH_LONG).show();
            }

        } else {
            Toast.makeText(getApplicationContext(), "GPS is currently off...", Toast.LENGTH_LONG).show();
        }
    }

希望有幫助

我創建了一些使用可以輕松獲取當前位置的類。 我使用 FusedLocationProviderClient 來獲取當前位置。

首先將其添加到您的清單文件中:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

然后檢查位置權限:

 private fun startCheckingLocation() {
    if (checkLocationPermissions() == true) {
        checkGPSEnabled()
    } else {
        askLocationPermission()
    }
}

checkLocationPermissions 方法:

 private fun checkLocationPermissions(): Boolean? {
    return PermissionUtils.hasPermissions(
        requireContext(),
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION
    )
}

checkGPSEnabled 方法:

 private fun checkGPSEnabled() {
    GpsUtils(requireContext()) {
        it?.let {
            startCheckingCurrentLocation()
        }
    }.apply {
        turnGPSOn(gpsDialogCallback)
    }
}

由於 OnActivityResult 已棄用:

      private val gpsDialogCallback =     registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { activityResult ->
        activityResult?.let { result ->
            when (result.resultCode) {
                RESULT_OK -> {
                    startCheckingCurrentLocation()
                }
                RESULT_CANCELED -> {
                }
            }
        }
    }

startCheckingCurrentLocation 方法:

 private fun startCheckingCurrentLocation() {
    LocationUtils(requireContext()) { location ->
        Log.d(TAG, ">>>>>>>>>>>>>>" + location.latitude + " " + location.longitude)
        startIntentService(location)
    }.apply {
        startLocationUpdates()
    }
}

對於 GPS,我創建了一個類,您可以簡單地放置和使用它:

GPS實用程序:

class GpsUtils(
private val context: Context,
private val gpsStatus: (isEnable: Boolean?) -> Unit
) {

private val mSettingsClient: SettingsClient = LocationServices.getSettingsClient(context)
private val mLocationSettingsRequest: LocationSettingsRequest
private val locationManager: LocationManager =
    context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
private val locationRequest: LocationRequest = LocationRequest.create()

init {
    locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
    locationRequest.interval = 10 * 1000.toLong()
    locationRequest.fastestInterval = 2 * 1000.toLong()
    val builder = LocationSettingsRequest.Builder().addLocationRequest(locationRequest)
    mLocationSettingsRequest = builder.build()
    builder.setAlwaysShow(true) //this is the key ingredient
}

// method for turn on GPS
fun turnGPSOn(gpsDialogCallback: ActivityResultLauncher<IntentSenderRequest>) {
    if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
        gpsStatus.invoke(true)
    } else {
        mSettingsClient
            .checkLocationSettings(mLocationSettingsRequest)
            .addOnSuccessListener(
                (context as Activity)
            ) {
                //  GPS is already enable, callback GPS status through listener
                gpsStatus.invoke(true)
            }
            .addOnFailureListener(context) { e ->
                when ((e as ApiException).statusCode) {
                    LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> try {
                        // Show the dialog by calling startResolutionForResult() and check the result in onActivityResult().

                        if (e is ResolvableApiException) {
                            try {
                                val intentSenderRequest = IntentSenderRequest.Builder(e.resolution).build()
                                gpsDialogCallback.launch(intentSenderRequest)
                            } catch (throwable: Throwable) {
                                // Ignore the error.
                            }
                        }

                    } catch (sie: IntentSender.SendIntentException) {
                        // Ignore the error.
                        Timber.i("PendingIntent unable to execute request.")
                    }
                    LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
                        // Location settings are not satisfied. However, we have no way to fix the
                        // settings so we won't show the dialog.
                        val errorMessage =
                            "Location settings are inadequate, and cannot be fixed here. Fix in Settings."
                        Timber.e(errorMessage)
                    }
                    LocationSettingsStatusCodes.CANCELED -> {
                        val errorMessage =
                            "Location settings are inadequate, and cannot be fixed here. Fix in Settings."
                        Timber.e(errorMessage)
                    }
                    LocationSettingsStatusCodes.SUCCESS -> {
                        // All location settings are satisfied. The client can initialize location
                        // requests here.
                        val errorMessage =
                            "Location settings are inadequate, and cannot be fixed here. Fix in Settings."
                        Timber.e(errorMessage)
                    }
                }
            }
    }
}

}

為了檢查位置,我又創建了一個類:

class LocationUtils(
context: Context,
private val latLng: (location: Location) -> Unit) {

private var fusedLocationClient: FusedLocationProviderClient? = null

private val locationRequest = LocationRequest.create()?.apply {
    interval = 20 * 1000.toLong()
    fastestInterval = 2 * 1000.toLong()
    priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}


init {
    fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
}

/**
 * call when location permission is allowed and you want to fetch the last location of the user
 */
fun getLastLocation() {
    fusedLocationClient?.lastLocation?.addOnSuccessListener { location ->
        location?.let {
            latLng.invoke(location)
            stopLocationUpdates()
        }
    }
}

/**
 * Requested location callback
 */
private val locationCallback = object : LocationCallback() {
    override fun onLocationResult(locationResult: LocationResult?) {

        locationResult ?: return

        for (location in locationResult.locations) {
            location?.let {
                latLng.invoke(it)
                stopLocationUpdates()
            }
        }
        super.onLocationResult(locationResult)
    }
}

/**
 * call when location permission is already given to user and you want to receive continues location updates
 */
fun startLocationUpdates() {
    fusedLocationClient?.requestLocationUpdates(
        locationRequest,
        locationCallback,
        Looper.getMainLooper()
    )
}

/**
 * call when you want to stop location updates
 */
fun stopLocationUpdates() {
    fusedLocationClient?.removeLocationUpdates(locationCallback)?.addOnCompleteListener { }
}

}

startIntentService 方法:

  private fun startIntentService(location: Location?) {
    val intent = Intent(requireActivity(), FetchAddressIntentService::class.java).apply {
        putExtra(AppConstants.RECEIVER, resultReceiver)
        putExtra(AppConstants.LOCATION_DATA_EXTRA, location)
    }
    requireActivity().startService(intent)
}

我創建了一個意圖服務來從 latlng 獲取地址:

class FetchAddressIntentService : IntentService(AppConstants.LOCATION_SERVICE) {

companion object {
    const val TAG = "FetchAddressService"
}

private var receiver: ResultReceiver? = null

override fun onHandleIntent(intent: Intent?) {
    val geoCoder = Geocoder(this, Locale.getDefault())
    intent ?: return

    var errorMessage = ""

    // Get the location passed to this service through an extra.
    val location = intent.getParcelableExtra(AppConstants.LOCATION_DATA_EXTRA) as Location
    receiver = intent.getParcelableExtra(AppConstants.RECEIVER) as ResultReceiver

    var addresses: List<Address> = emptyList()

    try {
        addresses = geoCoder.getFromLocation(location.latitude, location.longitude, 1)
    } catch (ioException: IOException) {
        // Catch network or other I/O problems.
        errorMessage = getString(R.string.service_not_available)
        Log.e(TAG, errorMessage, ioException)
    } catch (illegalArgumentException: IllegalArgumentException) {
        // Catch invalid latitude or longitude values.
        errorMessage = getString(R.string.invalid_lat_long_used)
        Log.e(
            TAG,
            "$errorMessage. Latitude = $location.latitude , Longitude =  $location.longitude",
            illegalArgumentException
        )
    }

    // Handle case where no address was found.
    if (addresses.isEmpty()) {
        if (errorMessage.isEmpty()) {
            errorMessage = getString(R.string.no_address_found)
            Log.e(TAG, errorMessage)
        }
        deliverResultToReceiver(AppConstants.FAILURE_RESULT, errorMessage)
    } else {
        val address = addresses[0]
        // Fetch the address lines using getAddressLine,
        // join them, and send them to the thread.
        val addressFragments = with(address) {
            (0..maxAddressLineIndex).map { getAddressLine(it) }
        }
        Log.i(TAG, getString(R.string.address_found))
        deliverResultToReceiver(
            AppConstants.SUCCESS_RESULT,
            addressFragments.joinToString(separator = "\n")
        )
    }
}

private fun deliverResultToReceiver(resultCode: Int, message: String) {
    val bundle = Bundle().apply { putString(AppConstants.RESULT_DATA_KEY, message) }
    receiver?.send(resultCode, bundle)
}

}

比在您的片段或活動中使用 AddressResultReceiver:

 internal inner class AddressResultReceiver(handler: Handler) : ResultReceiver(handler) {

    override fun onReceiveResult(resultCode: Int, resultData: Bundle?) {

        // Display the address string
        // or an error message sent from the intent service.
        val addressOutput = resultData?.getString(AppConstants.RESULT_DATA_KEY).orEmpty()
        //displayAddressOutput()
        // Show a toast message if an address was found.
        if (resultCode == AppConstants.SUCCESS_RESULT) {
            Boast.showText(requireContext(), "Address found = $addressOutput")
            txtContinueWith.text = addressOutput
        }

    }
}

您需要在片段或活動中初始化它,您將使用上述接收器來獲取地址:

  private var resultReceiver = AddressResultReceiver(Handler())

這些是您應該按原樣使用的一些常量。

//Location Constants
const val LOCATION_SERVICE = "LOCATION_SERVICE"
const val SUCCESS_RESULT = 0
const val FAILURE_RESULT = 1
const val PACKAGE_NAME = "com.google.android.gms.location.sample.locationaddress"
const val RECEIVER = "$PACKAGE_NAME.RECEIVER"
const val RESULT_DATA_KEY = "${PACKAGE_NAME}.RESULT_DATA_KEY"
const val LOCATION_DATA_EXTRA = "${PACKAGE_NAME}.LOCATION_DATA_EXTRA"

並且不要忘記在清單文件中添加服務並添加互聯網權限:

 <service
        android:name=".ui.account.signin.service.FetchAddressIntentService"
        android:exported="false" />

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM