Android 应用程序崩溃,甚至无法启动。空指针异常?

时间:2021-03-11 19:03:40

标签: android kotlin nullpointerexception

我的应用程序崩溃并显示“CurrencyConverter 不断崩溃”。老实说,我看不出哪里有错误。

package com.example.currencycalculator

import android.app.Activity
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.View
import android.view.ViewGroup
import android.widget.*
import android.widget.Spinner

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        var currencyPicker: Spinner = findViewById(R.id.spinner)
        var userInput : EditText = findViewById(R.id.userInput)
        var toConvert : Currencies = Currencies(userInput)
        var convertedText : TextView = findViewById(R.id.endText)
        ArrayAdapter.createFromResource(
            this, R.array.currencyArray, android.R.layout.simple_spinner_item
        ).also { adapter ->
            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
            currencyPicker.adapter = adapter
        }
        currencyPicker.onItemSelectedListener = SpinnerActivity()
        userInput.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(p0: Editable?) {
            }

            override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
            }

            override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
                currencyPicker.setSelection(0)
            }

        })
        }

        inner class SpinnerActivity : Activity(), AdapterView.OnItemSelectedListener {
            val userInput : EditText = findViewById(R.id.userInput)
            var toConvert : Currencies = Currencies(userInput)
            override fun onNothingSelected(p0: AdapterView<*>?) {
            }
            override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
                if (p2 == 0)
                {
                    toConvert.getPesos(userInput)
                }
                if (p2 == 1)
                {
                    toConvert.getCan(userInput)
                }
                if (p2 == 2)
                {
                    toConvert.getPesos(userInput)
                }
            }
        }
}

activity_main.xml <

?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:id="@+id/layone"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    <TextView
        android:id="@+id/prompt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Please choose the country to convert to:"
        android:textSize="18sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.414" />

    <Spinner
        android:id="@+id/spinner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/prompt"
        app:layout_constraintVertical_bias="0.093" />

    <EditText
        android:id="@+id/userInput"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="numberDecimal"
        app:layout_constraintBottom_toTopOf="@+id/prompt"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/endText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/spinner"
        tools:text="Conversion:" />

</androidx.constraintlayout.widget.ConstraintLayout>

Currencies.kt

package com.example.currencycalculator

import android.widget.EditText

class Currencies(userInput: EditText) {
    fun getPesos(totalAmt:EditText): Double {
        var total: Double = totalAmt.text.toString().toDouble()
        //if (!totalAmt.text.toString().isEmpty()) {
            val converted : Double = total * 20.765275
            return converted
        //}
        //else
           // return null!!.toDouble()
    }
    fun getCan(totalAmt:EditText) : Double {
        var total: Double = totalAmt.text.toString().toDouble()
        //if (!totalAmt.text.toString().isEmpty()) {
            val converted : Double = total * 1.257629
            return converted
        //}
        //else
            //return null!!.toDouble()
    }
    fun getEuros(totalAmt:EditText) : Double {
        var total: Double = totalAmt.text.toString().toDouble()
        //if (!totalAmt.text.toString().isEmpty()) {
            val converted : Double = total * 0.836502
            return converted
        //}
        //else
            //eturn null!!.toDouble()
    }
}

这是 Logcat:

03/11 13:40:34: Launching 'app' on No Devices.
App restart successful without requiring a re-install.
$ adb shell am start -n "com.example.currencycalculator/com.example.currencycalculator.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Connected to process 3515 on device 'emulator-5554'.
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
W/RenderThread: type=1400 audit(0.0:252): avc: denied { write } for name="property_service" dev="tmpfs" ino=6766 scontext=u:r:untrusted_app:s0:c139,c256,c512,c768 tcontext=u:object_r:property_socket:s0 tclass=sock_file permissive=0
D/libEGL: Emulator has host GPU support, qemu.gles is set to 1.
W/libc: Unable to set property "qemu.gles" to "1": connection failed; errno=13 (Permission denied)
D/libEGL: loaded /vendor/lib/egl/libEGL_emulation.so
D/libEGL: loaded /vendor/lib/egl/libGLESv1_CM_emulation.so
D/libEGL: loaded /vendor/lib/egl/libGLESv2_emulation.so
W/rencycalculato: Accessing hidden method Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z (greylist, reflection, allowed)
W/rencycalculato: Accessing hidden method Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V (greylist, reflection, allowed)
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.currencycalculator, PID: 3515
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.currencycalculator/com.example.currencycalculator.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.Window.findViewById(int)' on a null object reference
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3270)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.Window.findViewById(int)' on a null object reference
        at android.app.Activity.findViewById(Activity.java:3209)
        at com.example.currencycalculator.MainActivity$SpinnerActivity.<init>(MainActivity.kt:43)
        at com.example.currencycalculator.MainActivity.onCreate(MainActivity.kt:27)
        at android.app.Activity.performCreate(Activity.java:7802)
        at android.app.Activity.performCreate(Activity.java:7791)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1299)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016) 
        at android.os.Handler.dispatchMessage(Handler.java:107) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.app.ActivityThread.main(ActivityThread.java:7356) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) 

这是一个货币转换器应用程序,我使用 Spinner 来选择用户想要转换成的货币。货币类只包含计算每种货币的函数。

在 MainActivity 中,我尝试使用 TextWatcher 来更新转换后的值,并通过 TextView 表示它(因此没有使用按钮)。 EditText 接受用户输入,该值由 Currencies 类使用。

1 个答案:

答案 0 :(得分:0)

你的内部类毫无理由地是 Activity 的子类。当您从此类内部调用 findViewById 时,它正在调用内部类的 findViewById,而不是 MainActivity。

由于您没有将内部类用作适当的 Activity,它没有设置任何视图,因此 findViewById 将失败。

将您的内部类更改为匿名对象(并删除其冗余属性),就像您用于文本观察器一样。

    currencyPicker.onItemSelectedListener = object: AdapterView.OnItemSelectedListener {

        override fun onNothingSelected(parent: AdapterView<*>) {
        }

        override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
            if (position == 0)
            {
                toConvert.getPesos(userInput)
            }
            if (position == 1)
            {
                toConvert.getCan(userInput)
            }
            if (position == 2)
            {
                toConvert.getPesos(userInput)
            }
        }
    }

我还建议使用 when 语句以提高可读性:

override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
    when(position) {
        0, 2 -> toConvert.getPesos(userInput)
        1 -> toConvert.getCan(userInput)
    }
}