Reputation: 91
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=" :
Upvotes: 1
Views: 1977
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 Activity
s 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
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