Rudrank Basant
Rudrank Basant

Reputation: 91

How to make API calls. I am a beginner

I want to make an app that shows a funfact/joke and there's a button that refreshes the activity to load another funfact/joke.

I have made the data class, retrofit object and the interface and made the layout and also have written some code in the main activity. The app runs, however the funfact/joke isn't loaded on the activity.

Please help, I am a beginner.

Here are the codes:

Data Class:

data class FunFact(
    val joke: String
)

Retrofit Object:

object RetrofitInstance {

    val api: FunFactInterface by lazy {
        Retrofit.Builder()
            .baseUrl("https://geek-jokes.sameerkumar.website")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(FunFactInterface::class.java)
    }
}

Interface:

interface FunFactInterface {

    @GET("/api?format=json")
    suspend fun getFacts(): Response<FunFact>
}

Main Activity:

class MainActivity : AppCompatActivity() {

    var binding: ActivityMainBinding? = null

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


        GlobalScope.launch {
            loadData()
        }

    }


    private suspend fun loadData() {

        val dataApi = RetrofitInstance.api
        val results = dataApi.getFacts()
        if(results!=null){
            if (binding != null) {
                binding!!.textView.text= results.toString()
            }

        }
    }
}

XML Layout:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="the fact"
        android:textSize="20dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Another Fact"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

</androidx.constraintlayout.widget.ConstraintLayout>

The XML Design is this: APP Design

Edit/Update: Thanks to you guys the app is getting response. I removed the keyword suspend from the functions and also added code to handle onFailure scenario. Also removed "val" before binding in mainActivity as suggested.

Updated Code:

class MainActivity : AppCompatActivity() {

    var binding: ActivityMainBinding? = null

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


        loadData()
        
        binding!!.button.setOnClickListener{
            loadData()
        }


    }


    private fun loadData() {

        val dataApi = RetrofitInstance.api
        var results = dataApi.getFacts()
        if (results!=null){

            results.enqueue(object: Callback<FunFact>{
                override fun onResponse(call: Call<FunFact>, response: Response<FunFact>) {
                    if(response!=null){
                        binding!!.textView.text= response.body().toString()

                    }
                }

                override fun onFailure(call: Call<FunFact>, t: Throwable) {
                    Toast.makeText(this@MainActivity,"Failure of response from API", Toast.LENGTH_SHORT)
                        .show()
                }
            })

        }



    }
}

The app runs and the joke is loaded, the format of the joke is little different than desired. Any kind of help is truly appreciated.

This is the image of the response that I got, I want to remove "funfact" and "joke=" : the app image

Upvotes: 1

Views: 1977

Answers (2)

Tobi Daada
Tobi Daada

Reputation: 533

If the code in MainActivity is correct, then you're not setting the value of the instance variable binding 'cause you're creating another local variable with the same name in the Activitys onCreate

Here's something you can do

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // set the value of the instance variable
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        GlobalScope.launch {
            loadData()
        }
    }


    private suspend fun loadData() = withContext(Dispatchers.Main) {
        val dataApi = RetrofitInstance.api
        val results = dataApi.getFacts()

        binding.textView.text = results.body()?.joke
    }
}

Upvotes: 2

Tenesuzun
Tenesuzun

Reputation: 93

If I am not making a mistake you set everything up correctly however did not initiate the API call in your loadData method. You can do that by writing dataApi.getFacts().enqueue() inside enqueue you have to add Callback object as parameter. Maybe this link could be more helpful for you https://futurestud.io/tutorials/retrofit-synchronous-and-asynchronous-requests

Instead of this

private suspend fun loadData() {

    val dataApi = RetrofitInstance.api
    val results = dataApi.getFacts()
    if(results!=null){
        if (binding != null) {
            binding!!.textView.text= results.toString()
        }

    }
}

Should be like this

private suspend fun loadData() {

        val dataApi = RetrofitInstance.api
        val results = dataApi.getFacts().enqueue(object: Callback<FunFacts>{
            override fun onResponse(
                    call: Call<FunFacts>,
            response: Response<FunFacts>
                        ){
                // you can work with the response here
                if(results!=null){
                    if (binding != null) {
                        binding!!.textView.text= results.toString()
                    }

                }
            } override fun onFailure(call: Call<FunFacts>, t: Throwable) {
                // put code here when the call fails 
            }
        })
        
    }

Upvotes: 1

Related Questions