Android从EditText传值调用天气API时出现NullPointerException的解决
问题场景
开发天气查询应用的ManualSearchActivity时,需要从EditText获取城市名称,拼接进OpenWeatherMap API请求URL来获取天气数据,但启动Activity时直接触发NullPointerException错误。
错误堆栈信息:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.Window.findViewById(int)' on a null object reference
at com.example.weatherapp.ManualSearchActivity.(ManualSearchActivity.kt:15)
at java.lang.Class.newInstance(Native Method)
...
原错误代码
Kotlin代码(错误写法)
class ManualSearchActivity : AppCompatActivity() { // 错误:在成员变量初始化时调用findViewById,此时Activity Context未初始化 private val cityEditText = findViewById<EditText>(R.id.et_city) private val apiKey = "YOUR_API_KEY" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_manual_search) findViewById<Button>(R.id.btn_search).setOnClickListener { FetchWeatherTask().execute() } } inner class FetchWeatherTask : AsyncTask<Void, Void, String>() { override fun doInBackground(vararg params: Void?): String { val cityName = cityEditText.text.toString() val urlString = "https://api.openweathermap.org/data/2.5/weather?q=$cityName&appid=$apiKey" // 网络请求逻辑... return "" } } }
对应布局文件activity_manual_search.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp"> <EditText android:id="@+id/et_city" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="输入城市名称"/> <Button android:id="@+id/btn_search" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="查询天气"/> </LinearLayout>
错误原因
问题出在Activity的初始化阶段(成员变量赋值时)调用findViewById——此时Activity的Context还未完成初始化,Window对象为空,调用findViewById自然会抛出NullPointerException。
解决方案
将EditText的实例获取或内容读取逻辑移到Activity生命周期的onCreate之后(比如在AsyncTask的doInBackground方法内,或者在onCreate中初始化EditText实例),确保调用findViewById时Context已经就绪。
修正方案1:在doInBackground中直接获取EditText内容
class ManualSearchActivity : AppCompatActivity() { private val apiKey = "YOUR_API_KEY" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_manual_search) findViewById<Button>(R.id.btn_search).setOnClickListener { FetchWeatherTask().execute() } } inner class FetchWeatherTask : AsyncTask<Void, Void, String>() { override fun doInBackground(vararg params: Void?): String { // 此时Activity已完成初始化,可安全调用findViewById val cityName = findViewById<EditText>(R.id.et_city).text.toString() val urlString = "https://api.openweathermap.org/data/2.5/weather?q=$cityName&appid=$apiKey" // 执行网络请求逻辑... return "" } } }
修正方案2:在onCreate中初始化EditText实例(更规范)
class ManualSearchActivity : AppCompatActivity() { // 使用lateinit延迟初始化,避免提前调用findViewById private lateinit var cityEditText: EditText private val apiKey = "YOUR_API_KEY" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_manual_search) // 在onCreate中初始化EditText,Context已就绪 cityEditText = findViewById(R.id.et_city) findViewById<Button>(R.id.btn_search).setOnClickListener { FetchWeatherTask().execute() } } inner class FetchWeatherTask : AsyncTask<Void, Void, String>() { override fun doInBackground(vararg params: Void?): String { val cityName = cityEditText.text.toString() val urlString = "https://api.openweathermap.org/data/2.5/weather?q=$cityName&appid=$apiKey" // 执行网络请求逻辑... return "" } } }
内容的提问来源于stack exchange,提问作者Beetle

