my project is based on clean architecture and hilt
I'm trying to request some data from a Web Service which I made with Python.
I don't know why but it doesn't receive any request from app
my apiservice
interface Service {
#GET("/question")
fun getQuestion(): Single<Question>
my apimodule
class ApiModule {
#Provides
#Singleton
fun provideOkHttpClient(): OkHttpClient {
val okHttpBuilder = OkHttpClient.Builder()
okHttpBuilder.addInterceptor(HttpLoggingInterceptor())
return okHttpBuilder.build()
}
#Provides
#Singleton
fun provideGson(): Gson {
return GsonBuilder()
.setLenient()
.create()
}
#Provides
#Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl(" http://25ae1464a63a.ngrok.io")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.client(okHttpClient)
.build()
}
#Provides
#Singleton
fun bindApiService(retrofit: Retrofit): Service {
return retrofit.create(Service::class.java)
my viewmodel
class MainViewModel #ViewModelInject constructor(
private val questionUseCaseImpl: QuestionUseCaseImpl,
): ViewModel(){
val questionModel = MutableLiveData<QuestionModel>()
private val compositeDisposable = CompositeDisposable()
fun getData() {
questionUseCaseImpl.execute()
.subscribeOn(Schedulers.io())
.subscribe({
it?.let {
questionModel.postValue(it)
}
},{
}).let {
compositeDisposable.add(it)
}
}
override fun onCleared() {
compositeDisposable.clear()
}
}
my main activity
class MainActivity : AppCompatActivity() {
private val mainViewModel by viewModels<MainViewModel> ()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button: Button = findViewById(R.id.startButton)
button.setOnClickListener {
mainViewModel.getData()
}
mainViewModel.questionModel.observe(this, {
it?.let {
Toast.makeText(
this,
"${it.first}is ${it.second}",
Toast.LENGTH_LONG
).show()
}
})
}
And I have no response at all! In logs it seems like I haven't sent any request
Related
I'm pretty new to kotlin, and I feel pretty much overwhelmed by it. I'd like to ask - how I can display any data from MutableLiveData? I've tried to Log it, but it doesn't seem to work. I've already added the internet permission to the manifest. Here's the code:
ApiServices
interface ApiServices {
#GET("/fixer/latest/")
fun getRatesData(
#Query("base") base: String,
#Query("apikey") apikey: String
): Call<CurrencyModel>
companion object {
private const val url = "https://api.apilayer.com/"
var apiServices: ApiServices? = null
fun getInstance(): ApiServices {
if (apiServices == null) {
val retrofit = Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build()
apiServices = retrofit.create(ApiServices::class.java)
}
return apiServices!!
}
}
}
Repository
class CurrencyRepository constructor(private val apiServices: ApiServices) {
fun getLatestRates() = apiServices.getRatesData("EUR", "API_KEY");
}
ViewModel
class CurrencyViewModel constructor(private val currencyRepository: CurrencyRepository) :
ViewModel() {
val currencyRatesList = MutableLiveData<CurrencyModel>()
val errorMessage = MutableLiveData<String>()
fun getLatestRates() {
val response = currencyRepository.getLatestRates();
response.enqueue(object : retrofit2.Callback<CurrencyModel> {
override fun onResponse(
call: retrofit2.Call<CurrencyModel>,
response: Response<CurrencyModel>
) {
currencyRatesList.postValue(response.body())
}
override fun onFailure(call: retrofit2.Call<CurrencyModel>, t: Throwable) {
errorMessage.postValue(t.message)
}
})
}
}
FactoryViewModel
class CurrencyViewModelFactory constructor(private val repository: CurrencyRepository) :
ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return if (modelClass.isAssignableFrom(CurrencyViewModel::class.java)) {
CurrencyViewModel(this.repository) as T
}else{
throw IllegalArgumentException("Couldn't found ViewModel")
}
}
}
MainActivity
class MainActivity : AppCompatActivity() {
private val retrofitService = ApiServices.getInstance()
lateinit var viewModel: CurrencyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProvider(this, CurrencyViewModelFactory(CurrencyRepository(retrofitService)))
.get(CurrencyViewModel::class.java)
viewModel.currencyRatesList.observe(this, Observer {
Log.d(TAG, "onCreate: $it")
})
viewModel.errorMessage.observe(this, Observer {
viewModel.getLatestRates()
})
}
}
You never call viewModel.getLatestRates() in your onCreate() to fetch an initial value for your LiveData, so it never emits anything to observe. The only place you have called it is inside your error listener, which won't be called until a fetch returns an error.
Side note, I recommend naming the function "fetchLatestRates()". By convention, "get" implies that the function returns what it's getting immediately rather than passing it to a LiveData later when its ready.
And a tip. Instead of this:
class MainActivity : AppCompatActivity() {
lateinit var viewModel: CurrencyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ...
viewModel = ViewModelProvider(this, CurrencyViewModelFactory(CurrencyRepository(retrofitService)))
.get(CurrencyViewModel::class.java)
//...
}
}
You can do this for the same result:
class MainActivity : AppCompatActivity() {
val viewModel: CurrencyViewModel by viewModels(CurrencyViewModelFactory(CurrencyRepository(retrofitService)))
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ...
}
}
i am new to kotlin android i am trying to use room with flow my code as follows
my entity as follows
#Entity(tableName = "user_data")
data class User (
#PrimaryKey(autoGenerate = true)
val id:Int,
val firstName:String,
val lastName:String,
val age:Int)
my database class as follows
#Database(entities = [User::class],version = 1,exportSchema = false)
abstract class UserDatabase :RoomDatabase(){
abstract fun userDao():UserDao
companion object{
#Volatile
private var INSTANCE:UserDatabase?=null
fun getDataBaseInstance(context:Context):UserDatabase
{
var tempInstance= INSTANCE
if(tempInstance!=null)
{
return tempInstance
}
synchronized(this)
{
val instance=Room.databaseBuilder(context.applicationContext,UserDatabase::class.java,"user_database")
.build()
INSTANCE=instance
return instance
}
}
}
}
my dao as follows
#Dao
interface UserDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun addUser(user:User) :Long
#Query("SELECT * from user_data")
fun fetchUserData(): Flow<List<User>>
}
my repository as follows
class UserRepository(private val userDao:UserDao) {
suspend fun addUser(user:User){
val rowid:Long=userDao.addUser(user)
}
fun readAllUserData():Flow<List<User>>
{
val temp=userDao.fetchUserData()
return temp
}
}
my view model as follows
class UserViewModel(application: Context) :ViewModel() {
private lateinit var _readAllData:MutableLiveData<List<User>>
private var repository: UserRepository
private var _firstName:String="Akshay"
val firstName:String get()=_firstName
private var _lastName:String=""
val lastName:String get()=_lastName
private var _age:String = ""
val age:String get()=_age
val readAllData:LiveData<List<User>> get()=_readAllData
init {
val userDao= UserDatabase.getDataBaseInstance(application).userDao()
repository=UserRepository(userDao)
}
fun setFirstName(name:String)
{
_firstName=name
}
fun setLastName(name:String)
{
_lastName=name
}
fun setAge(name:String)
{
_age=name
}
fun addUserToDB()
{
viewModelScope.launch(Dispatchers.IO) {
println("Inside addUserDB")
repository.addUser(User(0,_firstName,_lastName,_age.toInt()))
}
}
fun readDataFromUserTable():LiveData<List<User>>
{
return repository.readAllUserData().asLiveData()
}
}
class UserViewModelFactory(private val context: Context):ViewModelProvider.Factory{
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if(modelClass.isAssignableFrom(UserViewModel::class.java))
{
return UserViewModel(context)as T
}
throw IllegalArgumentException("Unknown class")
}
my fragment as follows
class ListFragment : Fragment() {
private var binding:FragmentListBinding?=null
private val sharedViewModel:UserViewModel by activityViewModels {UserViewModelFactory(requireContext())}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val fragmentBinding=FragmentListBinding.inflate(inflater,container,false);
binding=fragmentBinding
return fragmentBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding?.apply {
viewModel=sharedViewModel
lifecycleOwner=viewLifecycleOwner
listFragment=this#ListFragment
ToastCount()
}
}
private fun ToastCount() {
var temp=sharedViewModel.readDataFromUserTable()
Toast.makeText(requireContext(), temp.size.toString(),Toast.LENGTH_SHORT).show()
}
fun addNewUser()
{ findNavController().navigate(R.id.action_listFragment_to_addFragment)
}
}
I can add data I see data in my database inspector but I can not read data it always return me null in my ViewModel ... I have added all necessary dependencies somebody please guild me what is it I am doing wrong? Thanks in advance
I am trying to write simple httpclient, and i am trying to figure out how to get data from response. I have two classess:
class HttpClient(private val ctx: Context) {
private var queue: RequestQueue = Volley.newRequestQueue(this.ctx)
private lateinit var gpsModel: GpsModel
companion object {
const val BASE_URL: String = "https://uawk2yh00j.execute-api.eu-central-1.amazonaws.com/"
}
fun getGpsData() : GpsModel
{
this.queue.add(
StringRequest(
Request.Method.GET, BASE_URL + "production",
{
Log.d("GET_ALL_DATA", it.toString())
Log.d("GET_ALL_DATA", GpsModel.fromJson(it.toString())?.body?.size.toString())
this.gpsModel = GpsModel.fromJson(it.toString())!!
Log.d("GET_ALL_DATA", this.gpsModel.toString())
},
{
Log.d("GET_ALL_DATA", "Coś poszło nie tak")
}
)
)
return this.gpsModel
}
}
and main activity:
class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
private lateinit var mMap: GoogleMap
private lateinit var httpClient: HttpClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_maps)
this.httpClient = HttpClient(this)
var gpsModel = this.httpClient.getGpsData()
Log.d("GET_ALL_DATA", gpsModel.toString())
val mapFragment = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
}
override fun onMapReady(googleMap: GoogleMap) {
}
}
And i would like to know when Request is successfull and when is error, but i dont see any onSuccess or onFailure methods.
I found the answer: Its about creating interface. The link is here: Android Volley - How to isolate requests in another class
I'm parsing API and it's logging in the logcat, but I have a problem while retrieving it and using that data in a recycleview. These are my code snippets:
class MainActivity : AppCompatActivity() {
private val users = arrayListOf<User>()
private lateinit var adapter: RecyclerViewAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
init()
}
private fun init() {
adapter = RecyclerViewAdapter(users)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = adapter
val myViewModel : UsersViewModel by viewModel()
myViewModel.getAllUsers().observe(this, Observer {
users.add(it)
adapter.notifyDataSetChanged()
})
myViewModel.getUsers()
d("allUsers", users.size.toString())
}
}
I cannot set the data in a recyclerview, can anyone give me a hint? I could not find a proper source or code snippet to understand how I'm able to parse the data using a converter.
class UsersRequest {
private var retrofit = Retrofit.Builder()
.baseUrl("https://reqres.in/api/")
.addConverterFactory(ScalarsConverterFactory.create())
.build()
private var service = retrofit.create(ApiService::class.java)
interface ApiService {
#GET("users?page=1")
fun getRequest(): Call<String>
}
fun getRequest(callback: CustomCallback) {
val call = service.getRequest()
call.enqueue(onCallback(callback))
}
private fun onCallback(callback: CustomCallback): Callback<String> = object : Callback<String> {
override fun onFailure(call: Call<String>, t: Throwable) {
d("response", "${t.message}")
callback.onFailure(t.message.toString())
}
override fun onResponse(call: Call<String>, response: Response<String>) {
d("response", "${response.body()}")
callback.onSuccess(response.body().toString())
}
}
}
class RecyclerViewAdapter(private val users: ArrayList<User>) :
RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.user_layout,
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
return holder.onBind()
}
override fun getItemCount() = users.size
private lateinit var user:User
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun onBind() {
user = users[adapterPosition]
itemView.name.text = user.first_name
}
}
}
User(
val id: Int,
val email: String,
val first_name: String,
val last_name: String,
val avatar: String
)
with adapter class RecyclerViewAdapter(private val users: MutableList<User>) : RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>() { ...
Add this method to your adapter :
fun setUsers(usersList: List<User>) {
this.users.clear()
this.users.addAll(usersList)
notifyDataSetChanged()
}
and in MainActivity simply put :
myViewModel.getAllUsers().observe(this, Observer {
users -> hideLoading() // If you have a progress bar, here you can hide it
users?.let {
adapter.setUsers(users) }
})
This question has asked many times but none of the solutions is working. I don't know what's going on with code. Always shows android.os.NetworkOnMainThreadException
Below code is my ViewModel class
class MainActicityViewModel(val context: MainActivity) : BaseObservable() {
val layoutManagerAppointment = LinearLayoutManager(context)
private val appClient = Appclient().getClient(context)
private val apiInterface = appClient.create(ApiInterface::class.java)
var rcyAdapter: RcyAdapter? = null
init {
getList()
}
fun getList() {
val observable = apiInterface.getUsers()
observable.subscribeOn(Schedulers.io()) // i have also registerd for Schedulers.newThread()
observable.observeOn(AndroidSchedulers.mainThread())
observable.subscribeWith(object : DisposableObserver<ArrayList<Data>>() {
override fun onComplete() {
Toast.makeText(context, "onComplete", Toast.LENGTH_SHORT).show()
}
override fun onNext(t: ArrayList<Data>) {
Log.e("SIZE", "" + t.size)
Toast.makeText(context, "onNext", Toast.LENGTH_SHORT).show()
}
override fun onError(e: Throwable) {
Toast.makeText(context, "onError", Toast.LENGTH_SHORT).show()
Log.e("TAG ON ERROR", "" + e)
}
})
}
}
Below code is Retrofit Client class
class Appclient {
private var retrofit: Retrofit? = null
private val baseUrl = "https://jsonplaceholder.typicode.com"
fun getClient(context: Context): Retrofit {
//Here a logging interceptor is created
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val client = OkHttpClient.Builder()
.addInterceptor(interceptor).build()
retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(client)
.build()
return (retrofit)!!
}}
And my retrofit interface
interface ApiInterface {
#GET("/users")
fun getUsers(): Observable<ArrayList<Data>>}
Here is the MainActivity class
class MainActivity : AppCompatActivity() {
lateinit var vModel: MainActicityViewModel
var mainBinding: ActivityMainBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainBinding = DataBindingUtil.setContentView(this#MainActivity, R.layout.activity_main)
vModel = MainActicityViewModel(this#MainActivity)
mainBinding!!.viewModel = vModel
}}
Try this...
fun getList() {
val observable:Observable<ArrayList<Data>> = apiInterface.getUsers()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableObserver<ArrayList<Data>>() {
override fun onComplete() {
Toast.makeText(context, "onComplete", Toast.LENGTH_SHORT).show()
}
override fun onNext(t: ArrayList<Data>) {
Log.e("SIZE", "" + t.size)
Toast.makeText(context, "onNext", Toast.LENGTH_SHORT).show()
}
override fun onError(e: Throwable) {
Toast.makeText(context, "onError", Toast.LENGTH_SHORT).show()
Log.e("TAG ON ERROR", "" + e)
}
})
}
Observable needs to be chain calls when you do RxJava operations because Observable is not the builder pattern.