0

I am creating an instance of my fragment and then trying to call one of it's methods but a variable needs to be instantiated in onCreateView() before the method can be called without throwing a NullPointerException.

Here is part of my fragment:

class UpdatesFragment : androidx.fragment.app.Fragment(){

var progressBar: ProgressBar? = null
private var instance: UpdatesFragment? = null
private var application: CustomApplication? = null
var bluetoothManager2: BluetoothManager? = null
private lateinit var li: LayoutInflater
private var deviceContainer: ViewFlipper? = null
private var devicePrep: View? = null
private var progressLayout: View? = null
private var progressText: TextView? = null
var progressBarText: TextView? = null
var progressChunk: TextView? = null
private var scroll: ScrollView? = null
private var logWindow: TextView? = null
private var currentView = 0
private var disconnected = false

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
View? {

instance = this
application = activity?.application as CustomApplication
if (bluetoothManager2 == null) {
bluetoothManager2 = SuotaManager() // THIS LINE NEEDS TO BE CALLED BEFORE MY METHOD IS CALLED. 
>         }
bluetoothManager2!!.setContext(this)
bluetoothManager2!!.setDevice(application!!.device)

progressText = progressLayout!!.findViewById(R.id.progress_text)
progressChunk = progressLayout!!.findViewById(R.id.progress_chunk)
progressBar = progressLayout!!.findViewById(R.id.progress_bar)
progressBarText = progressLayout!!.findViewById(R.id.progress_bar_text)
scroll = progressLayout!!.findViewById<View>(R.id.logScroll) as ScrollView
logWindow = progressLayout!!.findViewById<View>(R.id.logWindow) as TextView
logWindow!!.setText(null, TextView.BufferType.EDITABLE)
progressBar!!.progress = 0
progressBar!!.max = 100

initDevicePrepScreen()
val fragmentView = inflater.inflate(R.layout.activity_fragment, container, false)

li = LayoutInflater.from(activity)
deviceContainer = fragmentView.findViewById(R.id.deviceLayoutContainer) as ViewFlipper
devicePrep = inflater.inflate(R.layout.device_prep, deviceContainer, true)
progressLayout = inflater.inflate(R.layout.progress, deviceContainer, true)

return fragmentView
>     }

And here is where I call it from the activity:

bluetoothGattReceiver =
            object : BluetoothGattReceiver() {
                override fun onReceive(context: Context, intent: Intent) {
                    super.onReceive(context, intent)

                    if (updatesFragment == null) {
                        updatesFragment = UpdatesFragment()
                    }
                    updatesFragment!!.processStep(intent)

How can I get onCreateView() to be called before I run updatesFragment!!.processStep(intent)?

Here is the stack trace:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.unobatteries.unobmsapp, PID: 31486
    java.lang.NullPointerException
        at com.unobatteries.unobmsapp.fragments.UpdatesFragment.processStep(UpdatesFragment.kt:85)
        at com.unobatteries.unobmsapp.activities.UpdatesActivity$onResume$2.onReceive(UpdatesActivity.kt:144)
        at androidx.localbroadcastmanager.content.LocalBroadcastManager.executePendingBroadcasts(LocalBroadcastManager.java:313)
        at androidx.localbroadcastmanager.content.LocalBroadcastManager$1.handleMessage(LocalBroadcastManager.java:121)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:246)
        at android.app.ActivityThread.main(ActivityThread.java:8653)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
fun processStep(intent: Intent?) {
        bluetoothManager2!!.processStep(intent) // Line 85
    }
Dylon Jaynes
  • 166
  • 11
  • Please give a [mcve] that reproduces the error. And what is the exact error? Which line causes the error? You create a fragment object with `updatesFragment = UpdatesFragment()`, but do you ever set this fragment as the current one for the activity to display? – Code-Apprentice Apr 05 '22 at 22:22
  • @Code-Apprentice I think it's the line where I call processStep(intent) in my activity that is causing the problem. I edited my question and included the stack trace. Do I need to have the activity display it before onCreateView() will be called? – Dylon Jaynes Apr 05 '22 at 22:35
  • The error tells you exactly what line is the problem: ` at com.unobatteries.unobmsapp.fragments.UpdatesFragment.processStep(UpdatesFragment.kt:89)`. At the end here, it says the error occurs on line 89 of `UpdatesFragments.kt`. So there's no need to guess. – Code-Apprentice Apr 05 '22 at 22:37
  • "Do I need to have the activity display it before onCreateView() will be called?" Yes, you have to use `FragmentManager` to display the fragment before `onCreateView()` will be called. – Code-Apprentice Apr 05 '22 at 22:37
  • Questions for you to think about...what does `processStep()` do? Why is this method in the fragment class? Does it really need to be there? It seems like you want to call this method without changing the UI. If that is the case, then it the method shouldn't be in a fragment. Instead, you may want to just create a separate class. There's nothing stopping you from creating your own classes that aren't activities or fragments. In fact, it's perfectly fine if it makes sense in designing the code in your app. – Code-Apprentice Apr 05 '22 at 22:41
  • @Code-Apprentice That is really weird that I'm getting a null pointer exception on line 89 because it should be instantiated. Perhaps the issue is that I'm simply not displaying the fragment via FragmentManager as you mentioned. Either way, I appreciate the help! – Dylon Jaynes Apr 05 '22 at 22:45
  • @Code-Apprentice Yeah, to be honest, I am trying to modify an existing apps code to work within my app and that is the way that they had it so I just kept it the same. I will definitely consider moving it out to another location though. – Dylon Jaynes Apr 05 '22 at 22:50
  • What is line 89? – Code-Apprentice Apr 05 '22 at 22:52
  • I edited my post with line 89 included. I don't even call this function until well after line 143 that is in the stack trace. disconnected is a var that is declared with a value of false as you can see in my code snippet. – Dylon Jaynes Apr 05 '22 at 22:54
  • 1
    That doesn't look right then. `this.disconnected = disconnected` can't cause a `NullPointerException`. And the stack trace shows that the exception occurs inside of `processStep()`, not `setDisconnected()`. Have you edited the code since you ran it and got this stack trace? Doing so cause make the line numbers in the stack trace to not match the line numbers in the current version of your code. – Code-Apprentice Apr 05 '22 at 23:00
  • 1
    @Code-Apprentice Oh wow, I just ran it again and it is now line 85 that is throwing the error which is the processStep() function within my fragment so that makes a lot more sense. Thank you for pointing that out! – Dylon Jaynes Apr 06 '22 at 14:35
  • That makes more sense. And now I can see what you mean that `onCreateView()` isn't called. As I said earlier, you have to show the fragment in order to have it called. – Code-Apprentice Apr 06 '22 at 15:03
  • Yeah, that makes a lot of sense. I think I figured out a solution that involves me not having to have the fragment displayed. I realized that I needed to actually call processStep() function in an instance of my bluetoothManager class (an instance which resides in my fragment) so I just called that on the instance even though bluetoothManager instance is null at that point. I believe it works because processStep() within bluetoothManager is an abstract function that is implemented in one of it's children. I'm not sure if that's best practice but it seemed to work for me. – Dylon Jaynes Apr 06 '22 at 15:17
  • I saw your answer below. See my comment there. – Code-Apprentice Apr 06 '22 at 15:23

1 Answers1

1

UPDATE: I realized that I needed to call this function without displaying the fragment while still accessing the bluetoothManager class that it references. It was pointed out to me that I didn't need an instance of my fragment to access the function in bluetoothManager because fragments are primarily for working with ui components.

Since BluetoothManager class is abstract, in my activity, I declared an instance of SuotaManager class which extends BluetoothManager and called processStep() on that instance and that fixed the problem!

Dylon Jaynes
  • 166
  • 11
  • While this fixes your immediately problem, there are issues with doing things this way. Fragments are intended to hold UI components to show, but you are creating a fragment instance just to access the `bluetoothManager` inside it. This suggests that the `bluetoothManager` belongs either in the activity or another non-UI class that the activity can us rather than in the fragment. – Code-Apprentice Apr 06 '22 at 15:06
  • Also note that the `?.` means that `processStep()` won't be called if `bluetoothManager` is `null`. This avoids the NPE you were originally asking about, but might not create the behavior you want. – Code-Apprentice Apr 06 '22 at 15:11
  • 1
    @Code-Apprentice Okay, I see what you mean. I just realized that the activity holds a reference to bluetoothManager already but not an instance of it because it's an abstract class. So, in my activity I declared an instance of SuotaManager class which extends BluetoothManager class and called processStep() (an abstract function) on that. Now it is working as expected! I will update my answer to include this solution. Thanks a lot for helping me out! – Dylon Jaynes Apr 06 '22 at 15:41
  • Glad to hear you are figuring it out. Note that "holds a reference to bluetoothManager already but not an instance" doesn't make much sense because a "reference" must refer to an "instance". I think this is just a misuse of terminology when you try to communicate what you see in your code. As you continue to learn, your understanding and ability to use terminology correctly will improve. – Code-Apprentice Apr 06 '22 at 17:19
  • 1
    Thank you! I appreciate the correction. I also made a mistake in assuming that this code `private lateinit var bluetoothManager: BluetoothManager` was referring to my custom `com.unobatteries.unobmsapp.ota.BluetoothManager` class when it actually was referring to BluetoothManager in the `android.bluetooth` library so even if the class wasn't abstract, there would be no reference to it. Haha! – Dylon Jaynes Apr 06 '22 at 17:41