-1

I am trying to set var json, but i get null pointer in the end. any ideas how to fix this ? still newbie with android

class Requester(activity: Activity) {
    ...
    var json : Info?  = null
    ...

    fun getData (counter : Int) {
        println("frist attemp"+json)// here it is null as it should be
        var show = 5*counter
        var url = "https://reqres.in/api/users?page=1&per_page=$show"
        var request = Request.Builder().url(url).build()
        val client = OkHttpClient()

        client.newCall(request).enqueue(object: Callback {
            override fun onResponse(call: Call?, response: Response?) {
                val body = response?.body()?.string()
                val gson = GsonBuilder().create()
                json = gson.fromJson(body,Info::class.java)//it should be init here

                activity.runOnUiThread {
                    dataSize = json!!.total
                    val adapter = UsersAdapter(activity ,json!!,activity)
                    if(activity is MainActivity)
                    activity.recycler_View.adapter = adapter

                }
                isLoading=false
                toast.cancel()


            }
            override fun onFailure(call: Call?, e: IOException?) {
                activity.runOnUiThread {
                    val adapter = UsersAdapter(activity ,Info(0,0,0,0,
                            listOf(Data(0,"NO CONECTION","",""))),activity)
                    activity.recycler_View.adapter = adapter
                    Toast.makeText(activity,"Failed to connect", Toast.LENGTH_SHORT).show()
                    toast.cancel()
                }

                isLoading=false


            }

        })
        println("end "+json)// here i get NULL any ideas why ?
    }

}

Class Info:

class Info(val page: Int,
           val per_page: Int,
           val total: Int,
           val total_pages: Int,
           val data: List<Data>)

Class Data:

class Data(val id: Int,
           val first_name: String,
           val last_name: String,
           val avatar: String)

Class UserActivity:

class UserActivity : AppCompatActivity(){
var id : Int =0
val requester : Requester = Requester(this)
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_user)
     id = intent.getIntExtra("id",0)
    display()

}

private fun display() {
    textView_ID_DEFINED.setText("$id")
    requester.getData(id/3+1)
    var info : Info? = requester.json
    println("requester="+requester)
    println("requester.info= "+requester.json)

    if (info !=null){
        for (i in 0..info.data.size){
            if (id == info.data[i].id){
                println("ahoj")
            }
        }
    }
}

}

Stacktrace:

I/Timeline: Timeline: Activity_launch_request id:com.example.android.zadanie time:197846234 I/System.out:requester=com.example.android.zadanie.Requester@127852b requester.info= null I/Timeline: Timeline: Activity_idle id: android.os.BinderProxy@5994e58 time:197846419 I/System.out: mid com.example.android.zadanie.Info@e380f1b

Tobias
  • 161
  • 12

1 Answers1

0

The place where your json variable is supposed to be initialized (i.e., fun onResponse) is called asynchronously, when the HTTP request is completed. This means that getData ends before the callback is actually called, and that's why you get a null.

Why does your callback get called asynchronously? Well, that's how enqueue is implemented, and its main advantage is that it runs the HTTP request in a separate thread, so that your UI doesn't freeze. However, when working with asynchronous callbacks you must consider that your code may be executed at any point in time, even when your application is no more in foreground (imagine a scenario where the user receives a phone call while your application is running)

user2340612
  • 10,053
  • 4
  • 41
  • 66
  • You should only use the `json` from inside the callback, where you're sure it's not null (assuming the the deserialization doesn't fail) – user2340612 Nov 03 '18 at 13:43
  • "json" is defined by using "body" and "body " is defined by using "response", which comes from overriden method, so i cannot extract it from there – Tobias Nov 03 '18 at 13:49
  • where is your NPE happening? – user2340612 Nov 03 '18 at 13:56
  • wait, you can't have an NPE at `var info: Info? = requester.json`, since it would mean that `requester` is null, but in such a case your code would have failed before, at least 1 line above. Would you mind updating your question with the full stack trace? – user2340612 Nov 03 '18 at 14:05
  • i edited (i am new also on stackoverflow). sorry for troubles. requester is not null – Tobias Nov 03 '18 at 14:15
  • No worries. The issue is what I explained above, i.e. that `requester.getData()` terminates before you actually got the data from the internet, hence `requester.json` is always `null` and should not be used in any place except the callback. The "quick and dirty" solution would be to move the "if/for/if" code from `display` to `onResponse`. In this way you'll see that `requester.json` is correctly populated (assuming no other errors happen) – user2340612 Nov 03 '18 at 14:22
  • you're welcome! Please consider upvoting/accepting the answer if it was useful, or otherwise downvoting it if not useful. This will contribute in increasing StackOverflow's overall quality – user2340612 Nov 03 '18 at 14:26
  • 1
    i cannot do that because my low reputation :/ – Tobias Nov 03 '18 at 14:47
  • Oh that's true, I forgot about that, my bad! – user2340612 Nov 03 '18 at 14:49