lateinit property sharedpreference has not been initalized - android

Hi I am trying to link my sharedpreference manager via dagger and inject it into my fragment but It keeps saying it has not been initalized yet how would I go around to fix this?
Code snippet:
lass HomeFragment : Fragment() {
private lateinit var binding: FragmentHomeBinding
private lateinit var viewModel: HomeFragmentVM
#Inject
lateinit var spManager: SPManager
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val repository = Repository()
val viewModelFactory = HomeFragmentVMFactory(repository)
viewModel = ViewModelProvider(this, viewModelFactory)[HomeFragmentVM::class.java]
getUserList()
}
private fun getUserList() {
var retrofit = RetrofitClient.getInstance()
var apiInterface = retrofit.create(SimpleApi::class.java)
lifecycleScope.launchWhenCreated {
try {
val response = apiInterface.getPost()
if (response.isSuccessful) {
val products = response.body()!!.data.toString()
if (response.body()?.data?.size!! <= 0) {
} else {
if (spManager.getProductID().isEmpty())
{
spManager.saveProductID(response.body()!!.productid.toLong())
}
txtData.text = response.body()?.data.toString()
}
} else {
}
}catch (Ex: Exception){
Log.e("Error",Ex.localizedMessage)
}
}
}
}
The error does not cause a crash to the app but it comes up in my logs as lateinit property spManager has not been initalized.

Related

RecyclerView doesn't load data initially

I made an app from which I get data from TMDB API, everything seems to work but when I start the app, it displays only hint text, after scrolling the View get's updated with the data from the API
This is how it looks before scrolling
And this is how it looks after scrolling down a a bit
This is how I implemented it:
HomeFragment.kt
class HomeFragment : Fragment() {
private var _binding: FragmentHomeBinding? = null
private lateinit var popularMovies: RecyclerView
private lateinit var popularMoviesAdapter: MoviesAdapter
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val homeViewModel = ViewModelProvider(this)[HomeViewModel::class.java]
_binding = FragmentHomeBinding.inflate(inflater, container, false)
popularMovies = binding.popularMovies
popularMovies.layoutManager = LinearLayoutManager(context,LinearLayoutManager.VERTICAL,false)
popularMoviesAdapter = MoviesAdapter(listOf())
popularMovies.addItemDecoration(DividerItemDecoration(context,DividerItemDecoration.VERTICAL))
popularMovies.adapter = popularMoviesAdapter
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
MoviesRepository.getPopularMovies(page = 1,onSuccess = ::onPopularMoviesFetched,onError = ::onError)
}
private fun onPopularMoviesFetched(movies: List<Movie>) {
popularMoviesAdapter.updateMovies(movies)
}
private fun onError() {
Toast.makeText(context, getString(R.string.error_fetch_movies), Toast.LENGTH_SHORT).show()
}
MovieAdapter.kt
class MoviesAdapter(
private var movies: List<Movie>
) : RecyclerView.Adapter<MoviesAdapter.MovieViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
val view = LayoutInflater
.from(parent.context)
val binding = MovieItemBinding.inflate(view)
return MovieViewHolder(binding)
}
override fun getItemCount(): Int = movies.size
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
holder.bind(movies[position])
}
fun updateMovies(movies: List<Movie>) {
this.movies = movies
notifyDataSetChanged()
}
inner class MovieViewHolder(private val binding: MovieItemBinding) : RecyclerView.ViewHolder(binding.root) {
private val poster: ImageView = itemView.findViewById(R.id.item_movie_poster)
fun bind(movie: Movie) {
binding.movieTitle.text =movie.title
binding.movieReleaseDate.text = movie.releaseDate
binding.movieOverview.text = movie.overview
binding.movieReleaseDate.text = movie.releaseDate
Glide.with(itemView)
.load("https://image.tmdb.org/t/p/w342${movie.posterPath}")
.transform(CenterCrop())
.into(poster)
}
}
make adapter initialization on onViewCreated instead of onCreateView

I am trying to show recyclerView in Fragment but I am getting an error

class FeedRecyclerAdapter (private val postList : ArrayList<Post>) : RecyclerView.Adapter<FeedRecyclerAdapter.PostHolder>() {
class PostHolder(val binding: FragmentDataBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostHolder {
val binding = FragmentDataBinding.inflate(LayoutInflater.from(parent.context),parent,false)
return PostHolder(binding)
}
override fun onBindViewHolder(holder: PostHolder, position: Int) {
holder.binding.verimText.text = postList.get(position).lsi
}
override fun getItemCount(): Int {
return postList.size
}
Here is the code written for recyclerView.
private lateinit var firestore: FirebaseFirestore
private lateinit var auth: FirebaseAuth
private var _binding: FragmentDataBinding? = null
private val binding get() = _binding!!
private lateinit var postArrayList : ArrayList<Post>
private lateinit var feedAdapter : FeedRecyclerAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
auth = Firebase.auth
firestore = Firebase.firestore
postArrayList = ArrayList<Post>()
feedAdapter = FeedRecyclerAdapter(postArrayList)
getData()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentDataBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
private fun getData(){
firestore.collection("Posts").addSnapshotListener { value, error ->
if (error!=null){
Toast.makeText(requireContext(),error.localizedMessage,Toast.LENGTH_SHORT).show()
}else{
if (value !=null){
if (!value.isEmpty){
val documents = value.documents
for (document in documents){
val araziBoyutu = document.get("Arazi Boyutu") as String
val araziEgimi = document.get("Arazi Eğimi") as String
val panelBoyutu = document.get("Panel Boyutu") as String
val panelSayisi = document.get("Panel Sayisi") as String
val sehir = document.get("Şehir") as String
val post = Post(panelSayisi,panelBoyutu,araziEgimi,araziBoyutu,sehir)
postArrayList.add(post)
recyclerView.adapter = FeedRecyclerAdapter(postArrayList)
}
feedAdapter.notifyDataSetChanged()
}
}
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.recyclerView.adapter = feedAdapter
recyclerView.layoutManager = LinearLayoutManager(activity)
The code here is the part where I define the RecylcerView and save the information.
I can pull the data, I can see it on firebase, when I print it with println, I can read it in the console, I can go to the page where the text should be written, but I can't see this data in the verimText TextView I'm trying to print.
Try to call notifyDataSetChanged() or notifyItemRangeInserted() after adding post.
Recyclerview should be declared inside onViewCreated Method
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val recyclerView = view.findViewById(R.id.recyclerView) as RecyclerView
recyclerView.layoutManager = LinearLayoutManager(activity)
recyclerView.adapter = FeedRecyclerAdapter(postArrayList)
}
move this line
recyclerView.adapter = FeedRecyclerAdapter(postArrayList)
to
val post = Post(panelSayisi,panelBoyutu,araziEgimi,araziBoyutu,sehir)
postArrayList.add(post)
recyclerView.adapter = FeedRecyclerAdapter(postArrayList)//move to here

Testing a Fragment Scenario with ViewBinding

I'm trying to test a fragment that uses viewBinding to show the views, and a viewModel to fetch that data.
I want to write a UI test to see if certain data is visible or not, but so fare I've had no luck and I'm hoping somebody can help me as I'm still very new to UI testing.
So far I tried mocking the viewModel, the liveData and the viewBinding, but to no success
Here are a few example classes of what I have tried to do
My ViewModel:
private val bike: MutableLiveData<Bike> = MutableLiveData()
val getBike: LiveData<BikeType>
get() = bikeType
fun getBike() {
BikeService().getBike(bikeId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
bike.postValue(it)
}, {
Timber.e(it)
}).let { disposeBag.add(it) }
}
}
}
My Fragment
class BikeFragment : AppAbstractBaseFragment<Any>(), BikeView,
OnBackPressedListener {
var mBinding: FragmentBikeBinding? = null
var viewModel: BikeViewModel? = null
var cancellable: Boolean = false
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
mBinding = FragmentBikeBinding.inflate(inflater, container, false)
return mBinding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (viewModel == null) {
viewModel = ViewModelProvider(this).get(BikeViewModel::class.java)
}
mBinding?.apply {
this.view = this#BikeDashboardFragment
viewModel?.getBike?.observe(viewLifecycleOwner, { newBike ->
bike = newBike
})
}
viewModel?.getBike()
}
The Test
class BikeTest {
#get:Rule
val screenshotTestRule = ScreenshotTestRule()
#Rule
#JvmField
val dataBindingIdlingResourceRule = DataBindingIdlingResourceRule()
#Rule
#JvmField
var instantExecutor = InstantTaskExecutorRule()
private var scenario: FragmentScenario<BikeFragment>? = null
private var bikeData = mockk<MutableLiveData<Bike>>()
private var viewModel = mockk<BikeViewModel>(relaxed = true)
private val lifecycleOwner: LifecycleOwner = mockk()
private var fakeBike =
Bike(id = 1, cancellable = true, linkable = true, ownerName = "Joske")
var context: Context? = null
#Before
fun setUp() {
val app = ApplicationProvider.getApplicationContext<BaseApplication>()
context = app.applicationContext
}
#Test
fun loadBikeSeeTheRightBikeData() {
scenario = launchFragmentInContainer(
themeResId = R.style.Theme_Brand
)
dataBindingIdlingResourceRule.monitorFragment(scenario!!)
scenario?.onFragment {
it.mBinding?.bike = fakeBike
onView(withId(R.id.ownerNameTextView)).check(matches(withText("Klaartje")))
}
}
}

Making more than one Api call with retrofit and paging

I'm trying to use paging with retrofit. But i have to make more than one calls for filtering or changing the rover(it is a nasa pictures app). But my app making Api call every second and that crashs it. I tried it with livedata also with flow but it didnt work.
There are three fragment execute same prosesses in tab layout with View Pager these are curiosityFragment, SpiritFragment, OpportunityFragment
object RetrofitInstance {
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(MoshiConverterFactory.create(moshi)).baseUrl(BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}
val api : SimpleApi by lazy {
retrofit.create(SimpleApi::class.java)
}}
interface SimpleApi {
#GET("mars-photos/api/v1/rovers/{name}/photos?sol=1000&api_key=DEMO_KEY")
fun getPhotos(#Path("name") name : String,
#Query("per_page")per_page:Int?,
#Query("page") page: Int):
Single<AllPhotos>
class DataSource(name:String) : RxPagingSource<Int,Photo>(){
var name :String
init{
this.name = name
}
override fun getRefreshKey(state: PagingState<Int, Photo>): Int? {
return null
}
override fun loadSingle(params: LoadParams<Int>): Single<LoadResult<Int, Photo>> {
val page = params.key ?:1
return RetrofitInstance.api.getPhotos(name,params.loadSize,page)
.subscribeOn(Schedulers.io())
.map {toLoadResult(it,page)}
.onErrorReturn { LoadResult.Error(it) }
}
private fun toLoadResult(data: AllPhotos,page : Int):LoadResult<Int,Photo>{
return LoadResult.Page(
data = data.photos,
prevKey = if(page==1) null else page-1,
nextKey = page.plus(1)
)
}
}
class ViewModelDataSource (application: Application):AndroidViewModel(application) {
fun getPhotosLiveData(name : String):LiveData<PagingData<Photo>>{
return Pager(
config = PagingConfig(pageSize = 10),
pagingSourceFactory = {
DataSource(name)
},
).liveData
}
}
class CuriosityFragment : Fragment() {
private lateinit var binding:FragmentCuriosityBinding
lateinit var mApiViewModel : ViewModelDataSource
private lateinit var curiosityAdapter: AdapterCuriosity
var hasLoadedOnce = false
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentCuriosityBinding.inflate(inflater,container,false)
mApiViewModel = ViewModelProvider(this).get(ViewModelDataSource::class.java)
setupRecyclerView()
loaddata()
return binding.root
}
fun setupRecyclerView(){
curiosityAdapter = AdapterCuriosity()
binding.recyclerViewCriosity.apply {
adapter = curiosityAdapter
layoutManager = LinearLayoutManager(requireContext())
}
}
fun loaddata(){
mApiViewModel.getPhotosLiveData("curiosity").observe(viewLifecycleOwner, Observer {
curiosityAdapter.submitData(this.lifecycle,it)})
}
class OpportunityFragment : Fragment() {
private lateinit var binding: FragmentOppurtunityBinding
private lateinit var mApiViewModel: ViewModelDataSource
private lateinit var recyclerView: RecyclerView
private lateinit var opportunityAdapter:AdapterOpportunity
var hasLoadedOnce = false
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentOppurtunityBinding.inflate(inflater, container, false)
recyclerView = binding.recyclerViewOpportunity
mApiViewModel = ViewModelProvider(this).get(ViewModelDataSource::class.java)
setupRecyclerView()
loaddata()
setHasOptionsMenu(true)
return binding.root
}
fun setupRecyclerView(){
opportunityAdapter = AdapterOpportunity()
binding.recyclerViewOpportunity.apply {
adapter = opportunityAdapter
layoutManager = LinearLayoutManager(requireContext())
}
}
fun loaddata(){
mApiViewModel.getPhotosLiveData("opportunity").observe(viewLifecycleOwner, Observer {
opportunityAdapter.submitData(this.lifecycle,it)})
}
class SpiritFragment : Fragment() {
private lateinit var binding: FragmentSpiritBinding
private lateinit var mApiViewModel : ViewModelDataSource
private lateinit var spiritAdapter:AdapterSpirit
lateinit var recyclerView: RecyclerView
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentSpiritBinding.inflate(inflater, container, false)
recyclerView = binding.recyclerViewSpirit
mApiViewModel = ViewModelProvider(this).get(ViewModelDataSource::class.java)
setupRecyclerView()
loaddata()
setHasOptionsMenu(true)
return binding.root
}
fun setupRecyclerView(){
spiritAdapter = AdapterSpirit()
binding.recyclerViewSpirit.apply {
adapter = spiritAdapter
layoutManager = LinearLayoutManager(requireContext())
}
}
fun loaddata(){
mApiViewModel.getPhotosLiveData("spirit").observe(viewLifecycleOwner, Observer {
spiritAdapter.submitData(this.lifecycle,it)})
}

Sometimes my ap crash when retrieve data with retrofit : D/AndroidRuntime: Shutting down VM?

this is my Retrofit retrieve code :
the error came from this line ==>> val respon = response.body()!!
( java.lang.NullPointerException
at com.xfath.hormart.fragments.homechildfragments.ChildHomeFragment$getProducts$1.onResponse(ChildHomeFragment.kt:124 )
class ChildHomeFragment : Fragment(){
private lateinit var s: SharedPreference
private var listImageSliderHomes: ArrayList<ImageSliderHome> = ArrayList()
private var listProducts: ArrayList<Products> = ArrayList()
private lateinit var sliderView: SliderView
private lateinit var rvProductBaru: RecyclerView
private lateinit var rvElektronik: RecyclerView
private lateinit var rvPribadi: RecyclerView
private lateinit var ivNotif: ImageButton
private lateinit var uidhome: TextView
private lateinit var svhome: NestedScrollView
private lateinit var mSearchView: SearchView
private lateinit var mSwipeRefreshLayout: SwipeRefreshLayout
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
getSlideImage()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_child_home, container, false)
init(view)
getProducts()
swipeRefresh()
s = SharedPreference(activity!!)
return view
}
private fun getProducts() {
ApiConfig.instanceRetrofit.getproduct().enqueue(object : Callback<ResponseModel> {
override fun onResponse(call: Call<ResponseModel>, response: Response<ResponseModel>) {
val respon = response.body()!!
if (respon.success == 1) {
listProducts = respon.products
displayProduct()
}
}
override fun onFailure(call: Call<ResponseModel>, t: Throwable) {
Log.v(TAG, "Error:" + t.message)
}
})
}
private fun displayProduct() {
val layoutManager = LinearLayoutManager(activity)
layoutManager.orientation = LinearLayoutManager.HORIZONTAL
rvProductBaru.adapter = ProductAdapter(listProducts)
rvProductBaru.layoutManager = layoutManager
}
}
thanks in advance..
This is meant that the response.body() returns null.
You should always do null checking for it or you can use let to avoid NPE.
PS: Avoid using !! in kotlin as it may throw NPE when the variable is null.
response.body()?.let { respon ->
if (respon.success == 1) {
listProducts = respon.products
displayProduct()
}
}

Categories

Resources