请求权限后在 API GET 调用上使用位置

Use location on API GET call after requesting permissions

基本上我的问题出在标题中,我在其中获得位置权限并从那里尝试根据位置获得 API 响应。问题是线程似乎在得到答案之前继续?以下是相关的代码片段(对于初学者的错误表示歉意)

class ApiViewModel : ViewModel() {
    private val _listOfRegions = MutableLiveData<List<Region>>()
    val listOfRegions: LiveData<List<Region>> = _listOfRegions

    fun getRegionsData(country: String) {
        viewModelScope.launch {
            Log.d("Workflow", "We will try to fetch info for $country")
            try {
                val listResult = SpotApi.retrofitService.getRegions(country)
                Log.d("Workflow", listResult.toString())
                if (listResult.isSuccessful) {
                    _listOfRegions.postValue(listResult.body())
                    Log.d("Workflow", listResult.body().toString())
                } else {
                    _listOfRegions.value = emptyList()
                }
            } catch (e: Exception) {
                Log.d("Workflow", "Failure: ${e.message}")
                _listOfRegions.value = emptyList()
            }
        }
    }
private val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    .build()

private val retrofit = Retrofit.Builder()
    .addConverterFactory(MoshiConverterFactory.create(moshi))
    .baseUrl(BASE_URL)
    .build()

interface ApiService {
    @GET("{country}")
    suspend fun getRegions(@Path("country") country: String): Response<List<Region>>
}

object SpotApi {
    val retrofitService: ApiService by lazy {
        retrofit.create(ApiService::class.java)
    }
}
object RegionsHelper {

    fun getCurrentLocation(context: Context, lat: Double, long: Double): String {
        val geocoder = Geocoder(context)
        val locationResult = geocoder.getFromLocation(lat, long, 1)
        val country = locationResult[0].countryCode
        Log.d("Workflow", "Country is $country")
        Log.d(
            "Workflow",
            locationResult[0].latitude.toString() + locationResult[0].longitude.toString()
        )
        return if (locationResult[0] != null) {
            country
        } else {
            "Something Else"
        }
    }
}
class MainActivity : AppCompatActivity() {

    private lateinit var fusedLocationClient: FusedLocationProviderClient
    val viewModel: ApiViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)

        // TODO: If LastLocation available use that to trigger result, else use default value
        getLastLocation()
    }

    private fun getLastLocation() {
        if (checkLocationPermission()) {
            if (isLocationEnabled()) {
                fusedLocationClient.lastLocation.addOnCompleteListener(this) { task ->
                    val location: Location? = task.result
                    if (location == null) {
                        requestNewLocationData()
                    } else {
                        Log.d("Workflow", "Permission Granted")
                        Log.d("Workflow", "Location is $location")
                        val retrievedCurrentCountry = getCurrentLocation(this@MainActivity, location.latitude, location.longitude)
                        Log.d("Workflow", "Current country is $retrievedCurrentCountry")
                        viewModel.getRegionsData(retrievedCurrentCountry)
                        //Log.d("Workflow", listOfRegions.toString())
                    }
                }
            }
        }
    }
}

日志结果为:

D/Workflow: 国家是PT
D/Workflow: 38.7211345-9.139605
D/Workflow: 当前国家是PT
D/Workflow: 我们将尝试获取 PT
的信息 D/Workflow: 空
D/Workflow: 响应{protocol=http/1.0, code=200, message=OK, url=http://192.168.1.181:5000/PT/}

D/Workflow: [地区(地区=卡斯卡伊斯, 国家=PT, long=-9.4207, lat=38.6968), 地区(region=sintra, country=PT, long=-9.3817, lat=38.8029 ), 地区(地区=caparica, country=PT, long=-9.2334, lat=38.6446), 地区(region=ericeira, country=PT, long=-9.4176, lat=38.9665)]*

尽管使用融合位置提供程序获取最后一个位置非常快,但有时可能需要一些时间(最多 1000 毫秒)。

在您的代码中,您的 API 在获取最后一个位置之前被调用。您可以通过对检索到的当前位置使用观察器来实现此目的。

class MainActivity : AppCompatActivity() {

    private lateinit var fusedLocationClient: FusedLocationProviderClient
    val viewModel: ApiViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)

        // TODO: If LastLocation available use that to trigger result, else use default value
        getLastLocation()

        observeRetrievedLocation()
    }

    private fun observeRetrievedLocation() {
        viewModel.retrievedLocation.observe(this) {
            if (it.isNotEmpty) {
              viewModel.getRegionsData(it)
            }
        }
    }

    private fun getLastLocation() {
        if (checkLocationPermission()) {
            if (isLocationEnabled()) {
                fusedLocationClient.lastLocation.addOnCompleteListener(this) { task ->
                    val location: Location? = task.result
                    if (location == null) {
                        requestNewLocationData()
                    } else {
                        Log.d("Workflow", "Permission Granted")
                        Log.d("Workflow", "Location is $location")
                        val retrievedCurrentCountry = getCurrentLocation(this@MainActivity, location.latitude, location.longitude)
                        Log.d("Workflow", "Current country is $retrievedCurrentCountry")
                        viewModel.loadRetrievedLocation(retrievedCurrentCountry)
                    }
                }
            }
        }
    }
}

还有你的 viewModel,

class ApiViewModel : ViewModel() {
    private val _listOfRegions = MutableLiveData<List<Region>>()
    val listOfRegions: LiveData<List<Region>> = _listOfRegions

    private val _retrievedLocation = MutableLiveData<String>()
    val retrievedLocation: LiveData<String> = _retrievedLocation


    fun loadRetrievedLocation(retrievedLocation: String) {
        _retrievedLocation.value = retrievedLocation
    }

    fun getRegionsData(country: String) {
        viewModelScope.launch {
            Log.d("Workflow", "We will try to fetch info for $country")
            try {
                val listResult = SpotApi.retrofitService.getRegions(country)
                Log.d("Workflow", listResult.toString())
                if (listResult.isSuccessful) {
                    _listOfRegions.postValue(listResult.body())
                    Log.d("Workflow", listResult.body().toString())
                } else {
                    _listOfRegions.value = emptyList()
                }
            } catch (e: Exception) {
                Log.d("Workflow", "Failure: ${e.message}")
                _listOfRegions.value = emptyList()
            }
        }
    }

所以只有当 retrievedCurrentLocation Livedata 的值发生变化而不是 null 时,您的 API 才会被调用。