How to prevent onCreate from returning results before doAsync Completes

68 views Asked by At

I am needing some help here (I am a new programmer). I am writing an app in Kotlin that requires me to find the local restaurants based on GPS location. Once I find them, I have to return them to the user. The specific part I am working on (I have the mapview completed) is a list-view of the restaurant specific details.

The problem that I am running into is that my doAsync tasks are not completing before onCreateView (yes, it is in a fragment) is returning the view. Therefore, the view is blank. I have tried to make the thread sleep, but it pauses everything, including the async tasks. No matter what I do, when the view is returned, the restaurantList.size is always 0 and the view is always blank. I can see in the logs, that the view returns before the list are populated. I have been working on this for 2 weeks now. Can someone please give me some help?

Here is my code for onCreateView:

 override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val root = inflater.inflate(R.layout.fragment_list, container, false)

        fusedLocationClient = LocationServices.getFusedLocationProviderClient(requireActivity())
        if (checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION)) {
            fusedLocationClient?.lastLocation?.addOnSuccessListener(requireActivity()) { location: Location? ->
                if (location == null) {
                    // TODO, handle it
                } else location.apply {
                    var long = location.longitude
                        var lat = location.latitude
                        // Handle location object
                        var type = "restaurant"
                        var url = getUrl(lat, long, type)

                        doAsync {
                            var googlePlacesData = (URL(url).readText())
                            onComplete {
                                val parser = DataParser()
                                nearbyPlaceList = parser.parse(googlePlacesData)
                    for (i in nearbyPlaceList.indices) {
                         val googlePlace = nearbyPlaceList[i]
                         placeName = googlePlace["place_name"]!!
                         vicinity = googlePlace["vicinity"]
                         id = googlePlace["place_id"]

                         val detailURL = StringBuilder("https://maps.googleapis.com/maps/api/place/details/json?")
                                    detailURL.append("place_id=" + id)
                                    detailURL.append("&key=" + "KEY_GOES_HERE")

                         doAsync {
                            var detailData = (URL(detailURL.toString()).readText())
                            onComplete {
                                val jsonObject: JSONObject
                                val jsonObject2: JSONObject
                                jsonObject = JSONObject(detailData)
                                jsonObject2 = jsonObject.getJSONObject("result")
                                if (!jsonObject2.isNull("name")) {
                                    placeName = jsonObject2.getString("name")
                                }
                                if (!jsonObject2.isNull("place_id")) {
                                    placeId = jsonObject2.getString("place_id")
                                }
                                if (!jsonObject2.isNull("formatted_address")) {
                                    address = jsonObject2.getString("formatted_address")
                                }
                                if (!jsonObject2.isNull("formatted_phone_number")) {
                                    phone = jsonObject2.getString("formatted_phone_number")
                                }
                                if (!jsonObject2.isNull("website")) {
                                    website = jsonObject2.getString("website")
                                }
                                if (!jsonObject2.isNull("rating")) {
                                    rating = jsonObject2.getDouble("rating")
                                }
                                if (bmImg == null) {
                                    var jsonPhotosArray: JSONArray? = null
                                    if (jsonObject2.has("photos")) {
                                        jsonPhotosArray = jsonObject2.getJSONArray("photos")
                                        val obj = jsonPhotosArray.getJSONObject(0)
                                        photoReference = obj.getString("photo_reference")
                                        val photoURL =
                                            StringBuilder("https://maps.googleapis.com/maps/api/place/photo?")
                                        photoURL.append("maxwidth=500")
                                        photoURL.append("&maxheight=500")
                                        photoURL.append("&key=" + "KEY_GOES_HERE")
                                        photoURL.append("&photoreference=" + photoReference)

                                        doAsync {
                                            ImageUrl = URL(photoURL.toString())
                                            val conn =
                                                ImageUrl.openConnection() as HttpURLConnection
                                            conn.setDoInput(true)
                                            conn.connect()
                                            var `is` = conn.getInputStream()
                                            val options = BitmapFactory.Options()
                                            options.inPreferredConfig = Bitmap.Config.RGB_565
                                            bmImg =
                                                BitmapFactory.decodeStream(`is`, null, options)!!
                                            onComplete {
                                                var style = "Restaurant"
                                                var openTil = "7 p.m."
                                                var distance = "0.0"
                                                var empsGoing = "2"
                                                restaurantList.add(ListViewModel(placeName,style,address, openTil, distance, empsGoing, rating, phone))
                                                Log.d("Log", "List Total = " + restaurantList.size)
                                                

                                            }
                                        }
                                    }
                                }
                            }
                                }
                            }
                         }
                    }
                    }
                }
                Toast.makeText(requireContext(), "Listing Nearby Restaurants", Toast.LENGTH_SHORT).show()
            }

        Log.d("Log", "List Total End= " + restaurantList.size)
        val recyclerview = root.findViewById(R.id.rvList) as RecyclerView
        recyclerview.layoutManager = LinearLayoutManager(requireActivity())
        recyclerview.adapter = RecyclerAdapter(restaurantList, requireContext())
        return root
    }

Here is what I get for the Log Output:

2020-07-25 07:39:47.354 17576-17576/com.mark.lunchappredesign D/Log: Inside onCreate
2020-07-25 07:39:53.415 17576-17576/com.mark.lunchappredesign D/Log: List Total End= 0
2020-07-25 07:39:53.494 17576-17576/com.mark.lunchappredesign D/Log: ListFragment - url = https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=39.812755,-85.7664&radius=5000&type=restaurant&sensor=true&key=KEY_GOES_HERE
2020-07-25 07:39:53.992 17576-17576/com.mark.lunchappredesign D/Log: DataParser - jsonobject ={"business_status":"OPERATIONAL","geometry":{"location":{"lat":39.80146370000001,"lng":-85.7692799},"viewport":{"northeast":{"lat":39.80274508029151,"lng":-85.7679299197085},"southwest":{"lat":39.80004711970851,"lng":-85.7706278802915}}},"icon":"https:\/\/maps.gstatic.com\/mapfiles\/place_api\/icons\/restaurant-71.png","id":"297878a9f9b57ecb7d7c00c3a695b30ec908c5d4","name":"Subway","opening_hours":{"open_now":false},"photos":[{"height":2988,"html_attributions":["<a href=\"https:\/\/maps.google.com\/maps\/contrib\/116980312202159126535\">Janet Williams<\/a>"],"photo_reference":"CmRaAAAAVQBC5037LqGuRj5bmi0IJg3mYRaZZGWTO--YmFq6iMTRDxWQTEyMILNhI1pjY-T1Ma8Cb_HxvFQc31_Nt-oQv-rOy61APaP5Wytcog
...

...
2020-07-25 07:39:54.918 17576-17576/com.mark.lunchappredesign D/Log: List Total = 1
2020-07-25 07:39:54.931 17576-17576/com.mark.lunchappredesign D/Log: List Total = 2
2020-07-25 07:39:54.942 17576-17576/com.mark.lunchappredesign D/Log: List Total = 3
2020-07-25 07:39:55.022 17576-17576/com.mark.lunchappredesign D/Log: List Total = 4
2020-07-25 07:39:55.034 17576-17576/com.mark.lunchappredesign D/Log: List Total = 5
2020-07-25 07:39:55.034 17576-17576/com.mark.lunchappredesign D/Log: List Total = 6
2020-07-25 07:39:55.083 17576-17576/com.mark.lunchappredesign D/Log: List Total = 7
2020-07-25 07:39:55.091 17576-17576/com.mark.lunchappredesign D/Log: List Total = 8
2020-07-25 07:39:55.126 17576-17576/com.mark.lunchappredesign D/Log: List Total = 9
2020-07-25 07:39:55.131 17576-17576/com.mark.lunchappredesign D/Log: List Total = 10
2020-07-25 07:39:55.172 17576-17576/com.mark.lunchappredesign D/Log: List Total = 11
2020-07-25 07:39:55.183 17576-17576/com.mark.lunchappredesign D/Log: List Total = 12
2020-07-25 07:39:55.194 17576-17576/com.mark.lunchappredesign D/Log: List Total = 13
2020-07-25 07:39:55.203 17576-17576/com.mark.lunchappredesign D/Log: List Total = 14
2020-07-25 07:39:55.273 17576-17576/com.mark.lunchappredesign D/Log: List Total = 15
2020-07-25 07:39:55.278 17576-17576/com.mark.lunchappredesign D/Log: List Total = 16
2020-07-25 07:39:55.310 17576-17576/com.mark.lunchappredesign D/Log: List Total = 17
2020-07-25 07:39:55.410 17576-17576/com.mark.lunchappredesign D/Log: List Total = 18
2020-07-25 07:39:55.611 17576-17576/com.mark.lunchappredesign D/Log: List Total = 19
2

There are 2 answers

0
Mark On BEST ANSWER

So, I FINALLY got it figured out! The issue was in the placement of the "add" statement! Because I am only downloading 1 image for each restaurant, I have an if statement to limit that section of code (if (bmImg == null))

As a result, the add statement was only executing once . . . For the last restaurant being added!

Thank you all for your help and thank you for looking!

4
sajjad On

Move this part of you code inside onComplete block:

    Log.d("Log", "List Total End= " + restaurantList.size)
    val recyclerview = root.findViewById(R.id.rvList) as RecyclerView
    recyclerview.layoutManager = LinearLayoutManager(requireActivity())
    recyclerview.adapter = RecyclerAdapter(restaurantList, requireContext())

Anyways the code obviously is not a perfect one and has a lot to improve. And note that anyways when you add your Fragment, it shouldn't necessarily have the remote data. Normally the API call is made when Fragment is created, and then in onComplete, you can use the data to populate the view.