Home > Software engineering >  Is this a bug relating to LiveData
Is this a bug relating to LiveData

Time:05-14

I have isolated the code to show this problem.

I have a recyclerview inside a fragment. I also have LiveData and MutableLiveData.

In the ViewModel.kt code there are these two lines in this order and it works fine:

private val mMLD = MutableLiveData<OneLine>()
var mLD : LiveData<OneLine> = getMLD()

If I reverse the order, ie

var mLD : LiveData<OneLine> = getMLD()
private val mMLD = MutableLiveData<OneLine>()

the program crashes.

Is this a bug?

I am using Android Studio Chipmunk 2021.2.1

Here is my code:

class Adapter(private var mList: List<OneLine>) : RecyclerView.Adapter<Adapter.ViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.one_line, parent, false)
        return ViewHolder(view)
    }
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val DisplayViewModel = mList.get(position)
            holder.textView.text = DisplayViewModel.text
    }
    override fun getItemCount(): Int {
        return mList.size
    }
    class ViewHolder(ItemView: View) : RecyclerView.ViewHolder(ItemView) {
        val textView: TextView = itemView.findViewById(R.id.textView)
    }
}
class Fragment : Fragment() {
    private lateinit var adapter : Adapter
    private lateinit var viewModel: ViewModel
    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 view: View = inflater.inflate(R.layout.fragment, container, false)

        viewModel = ViewModelProvider(requireActivity()).get(ViewModel::class.java)
        viewModel.Initialize()
        enableRecyclerView(view)
        viewModel.mLD.observe(viewLifecycleOwner, Observer {
            adapter.notifyDataSetChanged()
        })
        return view
    }

    fun enableRecyclerView(view:View){
        var mRecyclerView: RecyclerView? = null

        mRecyclerView = view.findViewById(R.id.recycler_view)
        val linearLayoutManager = LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false)
        mRecyclerView.layoutManager = linearLayoutManager
        adapter = Adapter(viewModel.rvData)   // This will pass the ArrayList to our Adapter
        mRecyclerView.adapter = adapter
   }
}
class MainActivity : AppCompatActivity()  {
    private lateinit var operator_display: ViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}
data class OneLine(val text: String) {
}
class ViewModel : ViewModel(){
    var mLD : LiveData<OneLine> = getMLD()             // observer watches this variable
    private val mMLD = MutableLiveData<OneLine>()
    private fun getMLD():MutableLiveData<OneLine>{return mMLD}
    var rvData = ArrayList<OneLine>()
    fun Initialize(){
        rvData.add(OneLine("Item A"))
        rvData.add(OneLine("Item B"))
        rvData.add(OneLine("Item C"))
    }
}

CodePudding user response:

private val _thing = "hi"
val wow = getThing() // _thing already assigned
fun getThing():String { return _thing }

fun main() {
    println(wow)
}

> hi
val wow = getThing() // _thing not assigned yet
private val _thing = "hi"
fun getThing():String { return _thing } // called when assigning wow, _thing not assigned yet

fun main() {
    println(wow)
}

> null

When you initialise properties through functions, you can run into issues like this, where the declaration order matters, or the compiler can't tell that a variable is definitely getting initialised somewhere. So here, you're effectively skipping ahead to a function that references a variable before that variable has been assigned

Since it hasn't been assigned yet, it's in its default state, i.e. null. Also notice that it's null even though the function's return type is the non-null String - the null check system doesn't help you at this point.

You didn't post your actual error, but at a guess it's running into that null and either crashing with a NullPointerException when you try calling observe on it, or the null checker is seeing you assign it to something that shouldn't be null, and it's going "hold on a second buddy"

  • Related