刷新后位置请求需要很长时间才能 运行

Location request takes a lot time to run after refresh

我目前正在通过教程开发天气应用程序。该应用程序工作正常,直到我尝试刷新所有内容并再次请求一个位置并使用新位置调用 API 的那一刻。每当我这样做时,要么需要花费大量时间才能找到新位置,要么永远不会发生。我在这里有点迷路,很想知道如何让它发挥作用。

一步一步:

  1. override fun onOptionsItemSelected 中,我调用 requestLocationData() -> 这部分运行正常,调用的函数运行。
  2. requestLocationData() 中,我应该使用融合位置提供程序客户端请求位置并移动到 override fun onLocationResult 但这要么会花费很多时间,要么永远不会发生。

主要活动:


import android.Manifest
import android.annotation.SuppressLint
import android.app.Dialog
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.location.Location
import android.location.LocationManager
import android.net.Uri
import android.os.Bundle
import android.os.Looper
import android.provider.Settings
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.gms.location.*
import com.karumi.dexter.Dexter
import com.karumi.dexter.MultiplePermissionsReport
import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
import eu.apps.weatherapp.databinding.ActivityMainBinding
import eu.apps.weatherapp.models.WeatherResponse
import eu.apps.weatherapp.network.WeatherService
import retrofit2.*
import retrofit2.converter.gson.GsonConverterFactory
import java.text.SimpleDateFormat
import java.util.*

// OpenWeather Link : https://openweathermap.org/api
/**
 * The useful link or some more explanation for this app you can checkout this link :
 * https://medium.com/@sasude9/basic-android-weather-app-6a7c0855caf4
 */
class MainActivity : AppCompatActivity() {

    // A fused location client variable which is further used to get the user's current location
    private lateinit var mFusedLocationClient: FusedLocationProviderClient
    // Binding variable
    private lateinit var binding: ActivityMainBinding
    // A dialog variable
    private var mProgressDialog: Dialog? = null
    //longitude and latitude
    private var mLongitude: Double = 0.0
    private var mLatitude: Double = 0.0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)

        // Initialize the Fused location variable
        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this)


        if (!isLocationEnabled()) {
            Toast.makeText(
                this,
                "Your location provider is turned off. Please turn it on.",
                Toast.LENGTH_SHORT
            ).show()

            // This will redirect you to settings from where you need to turn on the location provider.
            val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
            startActivity(intent)
        } else {
            // Asking the location permission on runtime.)
            Dexter.withActivity(this)
                .withPermissions(
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION
                )
                .withListener(object : MultiplePermissionsListener {
                    override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
                        if (report!!.areAllPermissionsGranted()) {
                            // Calling the location request function
                            requestLocationData()
                        }

                        if (report.isAnyPermissionPermanentlyDenied) {
                            Toast.makeText(
                                this@MainActivity,
                                "You have denied location permission. Please allow it is mandatory.",
                                Toast.LENGTH_SHORT
                            ).show()
                        }
                    }

                    override fun onPermissionRationaleShouldBeShown(
                        permissions: MutableList<PermissionRequest>?,
                        token: PermissionToken?
                    ) {
                        showRationalDialogForPermissions()
                    }
                }).onSameThread()
                .check()
        }
    }

    /**
     * A function which is used to verify that the location or GPS is enabled or not.
     */
    private fun isLocationEnabled(): Boolean {
        // This provides access to the system location services.
        val locationManager: LocationManager =
            getSystemService(Context.LOCATION_SERVICE) as LocationManager
        return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(
            LocationManager.NETWORK_PROVIDER
        )
    }

    /**
     * A function used to show the alert dialog when the permissions are denied and need to allow it from settings app info.
     */
    private fun showRationalDialogForPermissions() {
        AlertDialog.Builder(this)
            .setMessage("It Looks like you have turned off permissions required for this feature. It can be enabled under Application Settings")
            .setPositiveButton(
                "GO TO SETTINGS"
            ) { _, _ ->
                try {
                    val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
                    val uri = Uri.fromParts("package", packageName, null)
                    intent.data = uri
                    startActivity(intent)
                } catch (e: ActivityNotFoundException) {
                    e.printStackTrace()
                }
            }
            .setNegativeButton("Cancel") { dialog,
                                           _ ->
                dialog.dismiss()
            }.show()
    }

    /**
     * A function to request the current location. Using the fused location provider client.
     */
    @SuppressLint("MissingPermission")
    private fun requestLocationData() {
        Log.e("requestLocationData:", "Start")

        val mLocationRequest = LocationRequest()
        mLocationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY

        mFusedLocationClient.requestLocationUpdates(
            mLocationRequest, mLocationCallback,
            Looper.myLooper()
        )
    }

    /**
     * A location callback object of fused location provider client where we will get the current location details.
     */
    private val mLocationCallback = object : LocationCallback() {
        override fun onLocationResult(locationResult: LocationResult) {
            Log.e("LocationResult:", "Start")

            val mLastLocation: Location = locationResult.lastLocation
            mLatitude = mLastLocation.latitude
            Log.i("Current Latitude", "$mLatitude")

            mLongitude = mLastLocation.longitude
            Log.i("Current Longitude", "$mLongitude")

            //Getting weather details for lat and long
            getLocationWeatherDetails()
        }
    }

    /**
     * Function responsible for getting weather using API call and current location
     */
    private fun getLocationWeatherDetails(){
        Log.e("LocationWeatherDetails:", "Start")

        if(Constants.isNetworkAvailable(this)){
            //Building retrofit object
            val retrofit: Retrofit = Retrofit.Builder()
                .baseUrl(Constants.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
            //Creating service
            val service: WeatherService = retrofit
                .create<WeatherService>(WeatherService::class.java)
            //Prepare call
            val listCall: Call<WeatherResponse> = service.getWeather(
                mLatitude, mLongitude, Constants.APP_ID, Constants.METRIC_UNIT)

            //Show custom progress dialog
            showCustomProgressDialog()

            //Performing API call
            listCall.enqueue(object : Callback<WeatherResponse>{
                override fun onResponse(
                    call: Call<WeatherResponse>,
                    response: Response<WeatherResponse>
                ) {
                    if (response.isSuccessful){
                        //Hide custom progress dialog
                        hideCustomProgressDialog()
                        val weatherList: WeatherResponse = response.body()!!
                        Log.i("Response Result", "$weatherList")
                        //Setting up UI
                        setupUI(weatherList)
                    }else{
                        //Hide custom progress dialog
                        hideCustomProgressDialog()

                        //Logging return code.
                        val rc = response.code()
                        when(rc){
                            401 -> {
                                Log.e("Error 401", "API Key Issue")
                            }
                            400 -> {
                                Log.e("Error 400", "Bad Connection")
                            }
                            404 -> {
                                Log.e("Error 404", "Not Found")
                            }
                            429 -> {
                                Log.e("Error 429", "Over 60 API calls per minute")
                            }else -> {
                                Log.e("Error", "Generic Error: $rc")
                            }
                        }
                    }
                }

                override fun onFailure(call: Call<WeatherResponse>, t: Throwable) {
                    Log.e("Error", t.message.toString())
                }

            })

        }else {
            Toast.makeText(this@MainActivity,
                "No internet connection",
                Toast.LENGTH_SHORT).show()
        }
    }

    private fun showCustomProgressDialog() {
        mProgressDialog = Dialog(this)

        /* Set the screen content from a layout.
           The layout will be inflated, adding a top-level views to the screen*/
        mProgressDialog!!.setContentView(R.layout.dialog_custom_progress)

        // Show dialog on the screen
        mProgressDialog!!.show()
    }

    private fun hideCustomProgressDialog() {
        if (mProgressDialog != null){
            mProgressDialog!!.dismiss()
        }
    }

    /**
     * Function responsible for populating UI with data from API
     */
    private fun setupUI(weatherList: WeatherResponse) {
        //Since Weather is a list we go through all of its elements
        for(i in weatherList.weather.indices){

            binding.tvMain.text = weatherList.weather[i].main
            binding.tvMainDescription.text = weatherList.weather[i].description

            //deciding on the icon to use based on ID.
            when (weatherList.weather[i].id){
                in 200..232 -> binding.ivMain.setImageResource(R.drawable.rain)
                in 300..321 -> binding.ivMain.setImageResource(R.drawable.rain)
                in 500..531 -> binding.ivMain.setImageResource(R.drawable.rain)
                in 600..622 -> binding.ivMain.setImageResource(R.drawable.snowflake)
                781 -> binding.ivMain.setImageResource(R.drawable.storm)
                800 -> binding.ivMain.setImageResource(R.drawable.sunny)
                in 801..804 -> binding.ivMain.setImageResource(R.drawable.cloud)
            }
        }

        binding.tvTemp.text = weatherList.main.temp.toString() + getUnit(application.resources.configuration.locale.toString())
        binding.tvHumidity.text = weatherList.main.humidity.toString() + "%"
        binding.tvMin.text = weatherList.main.temp_min.toString() +
                getUnit(application.resources.configuration.locale.toString()) + " min"
        binding.tvMax.text = weatherList.main.temp_max.toString() +
                getUnit(application.resources.configuration.locale.toString()) + " max"
        binding.tvSpeed.text = weatherList.wind.speed.toString()
        binding.tvName.text = weatherList.name
        binding.tvCountry.text = weatherList.sys.country

        //getting current sunset and sunrise
        val sunrise = unixTime(weatherList.sys.sunrise)
        val sunset = unixTime(weatherList.sys.sunset)
        binding.tvSunriseTime.text = sunrise
        binding.tvSunsetTime.text = sunset
    }

    /**
     * Function responsible for returning user temperature unit based on the configuration
     */
    private fun getUnit(value: String): String {
        var unit = "°C"
        if (value == "US" || value == "LR" || value == "MM"){
            unit = "°F"
        }

        return unit
    }

    /**
     * Function responsible for getting current time
     * It changes Long value into Date then set the simple date format and return hours and minutes
     */
    private fun unixTime(timex: Long): String?{
        val date = Date(timex * 1000L)
        val sdf = SimpleDateFormat("HH:mm")
        sdf.timeZone = TimeZone.getDefault()

        return sdf.format(date)
    }

    /**
     * Function responsible for displaying
     */
    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.menu_main, menu)
        return super.onCreateOptionsMenu(menu)
    }

    /**
     * Function responsible for refreshing when menu button is clicked.
     */
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when(item.itemId) {
            R.id.action_refresh -> {
                Log.e("Refresh:", "Clicked")
                requestLocationData()
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }
}

我的日志从开始 -> 刷新 ->(等待) -> 刷新 -> 现在:

2022-03-30 11:48:31.370 10371-10371/eu.apps.weatherapp E/requestLocationData:: Start
2022-03-30 11:48:32.068 10371-10371/eu.apps.weatherapp E/LocationResult:: Start
2022-03-30 11:48:32.069 10371-10371/eu.apps.weatherapp E/LocationWeatherDetails:: Start
2022-03-30 11:51:09.086 10371-10371/eu.apps.weatherapp E/Refresh:: Clicked
2022-03-30 11:51:09.086 10371-10371/eu.apps.weatherapp E/requestLocationData:: Start
2022-03-30 11:54:23.514 10371-10371/eu.apps.weatherapp E/LocationResult:: Start
2022-03-30 11:54:23.515 10371-10371/eu.apps.weatherapp E/LocationWeatherDetails:: Start
2022-03-30 12:13:16.605 10371-10371/eu.apps.weatherapp E/Refresh:: Clicked
2022-03-30 12:13:16.606 10371-10371/eu.apps.weatherapp E/requestLocationData:: Start```

每次调用requestLocationData()时,整个服务都会创建一个新实例,这意味着整个过程从零开始。这不是一个好习惯。首先,您应该使用 ViewModel 方法或创建一个全局改造实例。