java.lang.RuntimeException: Unable to instantiate activity

Приложение представляет собой обычный калькулятор. В Android Studio собирается без ошибок, но при запуске на телефоне крашится и выдаёт ошибку:

Код ошибки

    2021-05-31 15:43:41.689 19019-19019/mog.isdelal.kotlincalculator E/AndroidRuntime: FATAL EXCEPTION: main  
    Process: mog.isdelal.kotlincalculator, PID: 19019  
    java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{mog.isdelal.kotlincalculator/mog.isdelal.kotlincalculator.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method  'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference  
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2906)  
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3113)  
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)  
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:113)  
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:71)  
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1858)  
        at android.os.Handler.dispatchMessage(Handler.java:106)  
        at android.os.Looper.loop(Looper.java:201)  
        at android.app.ActivityThread.main(ActivityThread.java:6820)  
        at java.lang.reflect.Method.invoke(Native Method)  
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)  
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:922)  
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference  
        at android.content.ContextWrapper.getApplicationInfo(ContextWrapper.java:169)  
        at android.view.ContextThemeWrapper.getTheme(ContextThemeWrapper.java:157)  
        at android.content.Context.obtainStyledAttributes(Context.java:684)  
        at androidx.appcompat.app.AppCompatDelegateImpl.createSubDecor(AppCompatDelegateImpl.java:839)  
        at androidx.appcompat.app.AppCompatDelegateImpl.ensureSubDecor(AppCompatDelegateImpl.java:806)  
        at androidx.appcompat.app.AppCompatDelegateImpl.findViewById(AppCompatDelegateImpl.java:630)  
        at androidx.appcompat.app.AppCompatActivity.findViewById(AppCompatActivity.java:223)  
        at mog.isdelal.kotlincalculator.MainActivity.<init>(MainActivity.kt:9)  
        at java.lang.Class.newInstance(Native Method)  
        at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:69)  
        at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:45)  
        at android.app.Instrumentation.newActivity(Instrumentation.java:1216)  
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2894)  
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3113)  
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)  
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:113)  
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:71)  
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1858)  
        at android.os.Handler.dispatchMessage(Handler.java:106)  
        at android.os.Looper.loop(Looper.java:201)  
        at android.app.ActivityThread.main(ActivityThread.java:6820)  
        at java.lang.reflect.Method.invoke(Native Method)  
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)  
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:922)   
2021-05-31 15:43:43.577 19019-19019/mog.isdelal.kotlincalculator I/Process: Sending signal. PID: 19019 SIG: 9 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<TextView
    android:id="@+id/taskTextView"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="30"
    android:gravity="center"
    android:hint="0"
    android:text=""
    android:textSize="30dp" />

<TextView
    android:id="@+id/resultTextView"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="30"
    android:gravity="center"
    android:hint="0"
    android:text=""
    android:textSize="30dp" />

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:layout_weight="8">

    <Button
        android:id="@+id/clearButton"
        android:layout_weight="25"
        android:layout_height="match_parent"
        android:text="C"
        android:textSize="30dp"
        android:layout_width="0dp"/>

    <Button
        android:id="@+id/backSpace"
        android:layout_weight="25"
        android:layout_height="match_parent"
        android:text="Back"
        android:textSize="18dp"
        android:layout_width="0dp"/>

    <Button
        android:id="@+id/divisionButton"
        android:layout_weight="25"
        android:layout_height="match_parent"
        android:text="/"
        android:textSize="30dp"
        android:layout_width="0dp"/>

    <Button
        android:id="@+id/multiplicationButton"
        android:layout_weight="25"
        android:layout_height="match_parent"
        android:text="*"
        android:textSize="30dp"
        android:layout_width="0dp"/>

</LinearLayout>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:layout_weight="12">

    <Button
        android:id="@+id/button7"
        android:layout_weight="25"
        android:layout_height="match_parent"
        android:text="7"
        android:textSize="30dp"
        android:layout_width="0dp"/>

    <Button
        android:id="@+id/button8"
        android:layout_weight="25"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:text="8"
        android:textSize="30dp" />

    <Button
        android:id="@+id/button9"
        android:layout_weight="25"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:text="9"
        android:textSize="30dp" />

    <Button
        android:id="@+id/deductionButton"
        android:layout_weight="25"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:text="-"
        android:textSize="30dp" />

</LinearLayout>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:layout_weight="8">

    <Button
        android:id="@+id/button4"
        android:layout_weight="25"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:text="4"
        android:textSize="30dp" />

    <Button
        android:id="@+id/button5"
        android:layout_weight="25"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:text="5"
        android:textSize="30dp" />

    <Button
        android:id="@+id/button6"
        android:layout_weight="25"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:text="6"
        android:textSize="30dp" />

    <Button
        android:id="@+id/additionButton"
        android:layout_weight="25"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:text="+"
        android:textSize="30dp" />

</LinearLayout>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:layout_weight="8">

    <Button
        android:id="@+id/button1"
        android:layout_weight="25"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:text="1"
        android:textSize="30dp" />

    <Button
        android:id="@+id/button2"
        android:layout_weight="25"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:text="2"
        android:textSize="30dp" />

    <Button
        android:id="@+id/button3"
        android:layout_weight="25"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:text="3"

        android:textSize="30dp" />

    <Button
        android:id="@+id/dotButton"
        android:layout_weight="25"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:text="."
        android:textSize="30dp" />

</LinearLayout>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:layout_weight="8">

    <Button
        android:id="@+id/openBracket"
        android:layout_weight="25"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:text="("
        android:textSize="30dp" />

    <Button
        android:id="@+id/button0"
        android:layout_weight="25"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:text="0"
        android:textSize="30dp" />

    <Button
        android:id="@+id/closeBacked"
        android:layout_weight="25"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:text=")"
        android:textSize="30dp" />

    <Button
        android:id="@+id/resultButton"
        android:layout_weight="25"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:text="="
        android:textSize="30dp" />

</LinearLayout>

MainActivity.kt

package mog.isdelal.kotlincalculator

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView

class MainActivity : AppCompatActivity() {
    private val button0: Button = findViewById(R.id.button0)
    private val button1: Button = findViewById(R.id.button1)
    private val button2: Button = findViewById(R.id.button2)
    private val button3: Button = findViewById(R.id.button3)
    private val button4: Button = findViewById(R.id.button4)
    private val button5: Button = findViewById(R.id.button5)
    private val button6: Button = findViewById(R.id.button6)
    private val button7: Button = findViewById(R.id.button7)
    private val button8: Button = findViewById(R.id.button8)
    private val button9: Button = findViewById(R.id.button9)
    private val clearButton: Button = findViewById(R.id.clearButton)
    private val backSpace: Button = findViewById(R.id.backSpace)
    private val divisionButton: Button = findViewById(R.id.divisionButton)
    private val multiplicationButton: Button = findViewById(R.id.multiplicationButton)
    private val deductionButton: Button = findViewById(R.id.deductionButton)
    private val additionButton: Button = findViewById(R.id.additionButton)
    private val dotButton: Button = findViewById(R.id.dotButton)
    private val resultButton: Button = findViewById(R.id.resultButton)
    private val openBracket: Button = findViewById(R.id.openBracket)
    private val closeBacked: Button = findViewById(R.id.closeBacked)
    private var taskTextView: TextView = findViewById(R.id.taskTextView)
    private val resultTextView: TextView = findViewById(R.id.resultTextView)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //Ввод строки примера
        button0.setOnClickListener {
            taskTextView.text = taskTextView.text.toString() + "0"
        }
        button1.setOnClickListener {
            taskTextView.text = taskTextView.text.toString() + "1"
        }
        button2.setOnClickListener {
            taskTextView.text = taskTextView.text.toString() + "2"
        }
        button3.setOnClickListener {
            taskTextView.text = taskTextView.text.toString() + "3"
        }
        button4.setOnClickListener {
            taskTextView.text = taskTextView.text.toString() + "4"
        }
        button5.setOnClickListener {
            taskTextView.text = taskTextView.text.toString() + "5"
        }
        button6.setOnClickListener {
            taskTextView.text = taskTextView.text.toString() + "6"
        }
        button7.setOnClickListener {
            taskTextView.text = taskTextView.text.toString() + "7"
        }
        button8.setOnClickListener {
            taskTextView.text = taskTextView.text.toString() + "8"
        }
        button9.setOnClickListener {
            taskTextView.text = taskTextView.text.toString() + "9"
        }
        clearButton.setOnClickListener {
            taskTextView.text = ""
        }
        backSpace.setOnClickListener {
            taskTextView.text = taskTextView.text.toString().dropLast(2)
        }
        divisionButton.setOnClickListener {
            taskTextView.text = taskTextView.text.toString() + " / "
        }
        multiplicationButton.setOnClickListener {
            taskTextView.text = taskTextView.text.toString() + " * "
        }
        deductionButton.setOnClickListener {
            taskTextView.text = taskTextView.text.toString() + " - "
        }
        additionButton.setOnClickListener {
            taskTextView.text = taskTextView.text.toString() + " + "
        }
        dotButton.setOnClickListener {
            taskTextView.text = taskTextView.text.toString() + "."
        }
        openBracket.setOnClickListener {
            taskTextView.text = taskTextView.text.toString() + " ( "
        }
        closeBacked.setOnClickListener {
            taskTextView.text = taskTextView.text.toString() + " ) "
        }
        //обработка нажатия на =
        resultButton.setOnClickListener {
            try {
                // переводим строку примера в ArrayList<String>, разделяя её по пробелам и удаляя их.
                val list: ArrayList<String> = arrayListOf()
                val string = taskTextView.text.toString().trim()
                var lastIndex = 0
                for ((index, value) in string.withIndex()) {
                    var stringPath = ""
                    if (value == ' ') {
                        while (lastIndex < index) {
                            stringPath += string[lastIndex]
                            lastIndex++
                        }
                        list.add(stringPath)
                        lastIndex = index + 1

                    }
                }
                list.add(string.drop(lastIndex))
                resultTextView.text = buildAllFun(list)
            } catch (e: Exception) {
                taskTextView.text = ""
                resultTextView.text = "Ошибка ввода"
            }

        }
    }
    // функция buildAllFun проверяет на наличие () в присере и передаёт его дальше в taskInTask, если () есть, или сразу в result, если их нет.
    private fun buildAllFun(arr: ArrayList<String>): String {
        return if (arr.indexOf("(") != -1) {
            taskInTask(arr)[0]
        } else {
            result(arr)[0]
        }
    }
    // функция taskInTask рекурсивно находит текст простого примера в (), который надо выполнит первым, и исходя из полученного результата, выполняет остальные действия, выходя из рекурсии.
    private fun taskInTask(arr: ArrayList<String>): ArrayList<String> {
        var indexFirst = 0
        var indexLast = 0
        for ((index, value) in arr.withIndex()) {
            if (value == "(") {
                indexFirst = index
            }
        }
        for ((index, value) in arr.withIndex()) {
            if (value == ")" && index > indexFirst) {
                indexLast = index
                break
            }
        }

        indexFirst++
        var taskInTaskList: ArrayList<String> = arrayListOf()
        while (indexFirst < indexLast) {
            taskInTaskList.add(arr[indexFirst])
            indexFirst++
        }

        val result: String = result(taskInTaskList)[0]
        taskInTaskList = arr

        for ((index, value) in arr.withIndex()) {
            if (value == "(") {
                indexFirst = index
            }
        }

        taskInTaskList.add(indexFirst++, result)
        indexLast++

        while (indexLast >= indexFirst) {
            taskInTaskList.removeAt(indexLast)
            indexLast--
        }

        return if (taskInTaskList.indexOf("(") != -1) {
            taskInTask(taskInTaskList)
        } else {
            result(taskInTaskList)
        }
    }
    // функция result выполняет счёт простых примеров без () например: "5 + 5 * 5". Возвращает список с одним элементом(ответом).
    private fun result(arr: ArrayList<String>): ArrayList<String> {
        var a: Double
        var b: Double
        while (arr.size != 1) {
            if (arr.indexOf("/") != -1 || arr.indexOf("*") != -1 || arr.indexOf("-") != -1 || arr.indexOf("+") != -1) {

                for ((index, value) in arr.withIndex()) {
                    if (value == "*") {
                        a = arr[index - 1].toDouble()
                        b = arr[index + 1].toDouble()
                        arr.add(index - 1, (a * b).toString())
                        arr.removeAt(index + 2)
                        arr.removeAt(index + 1)
                        arr.removeAt(index)
                        break
                    }
                    if (value == "/") {
                        a = arr[index - 1].toDouble()
                        b = arr[index + 1].toDouble()
                        arr.add(index - 1, (a / b).toString())
                        arr.removeAt(index + 2)
                        arr.removeAt(index + 1)
                        arr.removeAt(index)
                        break
                    }
                    if (arr.indexOf("/") == -1 && arr.indexOf("*") == -1 && (arr.indexOf("-") != -1 || arr.indexOf("+") != -1)) {

                        if (value == "+") {
                            a = arr[index - 1].toDouble()
                            b = arr[index + 1].toDouble()
                            arr.add(index - 1, (a + b).toString())
                            arr.removeAt(index + 2)
                            arr.removeAt(index + 1)
                            arr.removeAt(index)
                            break
                        }
                        if (value == "-") {
                            a = arr[index - 1].toDouble()
                            b = arr[index + 1].toDouble()
                            arr.add(index - 1, (a - b).toString())
                            arr.removeAt(index + 2)
                            arr.removeAt(index + 1)
                            arr.removeAt(index)
                            break
                        }
                    }
                }
            }
        }
        if (arr[0].toDouble() % 1 == 0.0) {
            arr[0] = arr[0].toDouble().toInt().toString()
        }
        return arr
    }
}

P.S. Я новичок, поэтому мне было бы полезно послушать обоснованную критику в адрес моего кода, чтобы в будущем писать его более чистым. Заранее спасибо


Ответы (1 шт):

Автор решения: Alex Sus

Вот здесь:

class MainActivity : AppCompatActivity() {
private val button0: Button = findViewById(R.id.button0)
private val button1: Button = findViewById(R.id.button1)
...

идет обращение к элементам, которые еще не созданы в текущий момент, естественно получается ошибка. А созданы они будут только после того, как в onCreate сработает строка setContentView(R.layout.activity_main), т.е. вам нужно все эти ваши privat val ... перенести и поставить после этой строки. Чтобы работать с Андроидом нужно хорошо понимать, что в какой момент создается.

И насчет "писать код более чистым": в 2021 году пользоваться findViewById уже не комильфо. Почитайте про View Binding

→ Ссылка