You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin天气APP遇JSON转换异常与空指针错误求助

Kotlin天气APP的JSON解析与空指针问题解决

问题概述

开发Kotlin天气APP时遇到两个核心问题:

  • 解析JSON时抛出org.json.JSONException,提示JSONObject无法转换为JSONArray;
  • 触发java.lang.NullPointerException,提示在null对象上调用了String.length()方法。

错误日志

第一个错误日志

2022-12-01 13:32:16.389 5990-5990/com.example.wheather I/System.out: org.json.JSONException: Value {"coord":{"lon":77.2167,"lat":28.6667},"weather":[{"id":711,"main":"Smoke","description":"smoke","icon":"50d"}],"base":"stations","main":{"temp":23.05,"feels_like":22.61,"temp_min":23.05,"temp_max":23.05,"pressure":1015,"humidity":46},"visibility":1500,"wind":{"speed":2.57,"deg":280},"clouds":{"all":0},"dt":1669881717,"sys":{"type":1,"id":9165,"country":"IN","sunrise":1669857989,"sunset":1669895639},"timezone":19800,"id":1273294,"name":"Delhi","cod":200} of type org.json.JSONObject cannot be converted to JSONArray

第二个错误日志

2022-12-01 16:38:42.435 6838-6838/com.example.wheather I/System.out: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference

原代码

class MainActivity : AppCompatActivity() {
    val CITY: String="Delhi,In"
    val API: String= "67561777e881bf52c2b82163225846ad"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        wetherTask().execute()
    }
    inner class wetherTask() :AsyncTask<String,Void,String>()
    {
        override fun onPreExecute() {
            super.onPreExecute()
            findViewById<ProgressBar>(R.id.loader).visibility= View.VISIBLE
            findViewById<RelativeLayout>(R.id.maincontainer).visibility=View.GONE
            findViewById<TextView>(R.id.errortext).visibility=View.GONE
        }

        override fun doInBackground(vararg  params: String?): String? {
            var response:String?
            try {
                response =URL("https://api.openweathermap.org/data/2.5/weather?q=$CITY&units=metric&APPID=$API")
                    .readText(Charsets.UTF_8)
                System.out.println(response);

            }
            catch (e:Exception)
            {
                response=null
            }
            return response
    }


        override fun onPostExecute(result: String?) {
            super.onPostExecute(result)
            try {
                val jasonObj= JSONObject(result)
                val main=jasonObj.getJSONObject("main")
                val sys=jasonObj.getJSONObject("sys")
                val wind=jasonObj.getJSONObject("wind")
                val weather=jasonObj.getJSONObject("weather")

                val updateAt:Long=jasonObj.getLong("dt")
                val updateAtText="Update at: "+SimpleDateFormat("dd/MM/yyyy hh:mm a", Locale.ENGLISH).format(Date(updateAt*1000))
                val temp=main.getString("temp")+"°C"
                val tempMin="Min Temp: "+main.getString("temp_min")+"°C"
                val tempMax="Max Temp: "+main.getString("temp_max")+"°C"
                val pressure= main.getString("pressure")
                val humidity=main.getString("humidity")
                val sunrise:Long=sys.getLong("sunrise")
                val sunset:Long=sys.getLong("sunset")
                val windSpeed=wind.getString("speed")
                val weatherDescription=weather.getString("description")
                val address=jasonObj.getString("name")+", "+sys.getString("country")

                findViewById<TextView>(R.id.adress).text=address
                findViewById<TextView>(R.id.update_at).text=updateAtText
                findViewById<TextView>(R.id.status).text=weatherDescription.capitalize()
                findViewById<TextView>(R.id.temp).text=temp
                findViewById<TextView>(R.id.temp_min).text=tempMin
                findViewById<TextView>(R.id.temp_max).text=tempMax
                findViewById<TextView>(R.id.sunrise).text=SimpleDateFormat("hh:mm a",Locale.ENGLISH).format((Date(sunrise*1000)))
                findViewById<TextView>(R.id.sunset).text=SimpleDateFormat("hh:mm a",Locale.ENGLISH).format((Date(sunset*1000)))
                findViewById<TextView>(R.id.wind).text=windSpeed
                findViewById<TextView>(R.id.pressure).text=pressure
                findViewById<TextView>(R.id.humidity).text=humidity

                findViewById<ProgressBar>(R.id.loader).visibility=View.GONE
                findViewById<RelativeLayout>(R.id.maincontainer).visibility=View.VISIBLE
            }
            catch (e:Exception){
                System.out.println(e);
                findViewById<ProgressBar>(R.id.loader).visibility=View.GONE
                findViewById<TextView>(R.id.errortext).visibility=View.VISIBLE
            }
        }
}
}

错误原因分析

1. JSON解析异常

从返回的JSON数据可以看到,weather字段是一个JSON数组(格式为[{"id":711,...}]),但原代码中使用getJSONObject("weather")尝试将其解析为JSONObject,类型不匹配直接抛出异常。

2. 空指针异常

doInBackground方法中,如果网络请求失败(比如无网络、API错误),response会被赋值为null。而onPostExecute中直接调用JSONObject(result),当result为null时,JSONObject的构造函数会尝试调用String.length(),从而触发空指针异常。

解决方案

1. 修正JSON数组解析

将weather字段的解析方式改为JSONArray,再取数组中的第一个元素获取天气描述:

val weatherArray = jasonObj.getJSONArray("weather")
val weather = weatherArray.getJSONObject(0)
val weatherDescription = weather.getString("description")

2. 增加空值判断

在onPostExecute开头先判断result是否为null,若为null直接显示错误界面,避免后续无效操作:

if (result == null) {
    findViewById<ProgressBar>(R.id.loader).visibility = View.GONE
    findViewById<TextView>(R.id.errortext).visibility = View.VISIBLE
    return
}

3. 优化异常日志

在doInBackground的catch块中打印异常信息,方便排查网络问题:

catch (e: Exception) {
    e.printStackTrace()
    response = null
}

修正后的完整代码

class MainActivity : AppCompatActivity() {
    val CITY: String = "Delhi,In"
    val API: String = "67561777e881bf52c2b82163225846ad"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        wetherTask().execute()
    }

    inner class wetherTask : AsyncTask<String, Void, String>() {
        override fun onPreExecute() {
            super.onPreExecute()
            findViewById<ProgressBar>(R.id.loader).visibility = View.VISIBLE
            findViewById<RelativeLayout>(R.id.maincontainer).visibility = View.GONE
            findViewById<TextView>(R.id.errortext).visibility = View.GONE
        }

        override fun doInBackground(vararg params: String?): String? {
            var response: String?
            try {
                response = URL("https://api.openweathermap.org/data/2.5/weather?q=$CITY&units=metric&APPID=$API")
                    .readText(Charsets.UTF_8)
                System.out.println(response);

            } catch (e: Exception) {
                e.printStackTrace()
                response = null
            }
            return response
        }

        override fun onPostExecute(result: String?) {
            super.onPostExecute(result)
            
            // 先判断result是否为空
            if (result == null) {
                findViewById<ProgressBar>(R.id.loader).visibility = View.GONE
                findViewById<TextView>(R.id.errortext).visibility = View.VISIBLE
                return
            }

            try {
                val jasonObj = JSONObject(result)
                val main = jasonObj.getJSONObject("main")
                val sys = jasonObj.getJSONObject("sys")
                val wind = jasonObj.getJSONObject("wind")
                // 修正weather字段解析为JSONArray
                val weatherArray = jasonObj.getJSONArray("weather")
                val weather = weatherArray.getJSONObject(0)

                val updateAt: Long = jasonObj.getLong("dt")
                val updateAtText = "Update at: " + SimpleDateFormat("dd/MM/yyyy hh:mm a", Locale.ENGLISH).format(Date(updateAt * 1000))
                val temp = main.getString("temp") + "°C"
                val tempMin = "Min Temp: " + main.getString("temp_min") + "°C"
                val tempMax = "Max Temp: " + main.getString("temp_max") + "°C"
                val pressure = main.getString("pressure")
                val humidity = main.getString("humidity")
                val sunrise: Long = sys.getLong("sunrise")
                val sunset: Long = sys.getLong("sunset")
                val windSpeed = wind.getString("speed")
                val weatherDescription = weather.getString("description")
                val address = jasonObj.getString("name") + ", " + sys.getString("country")

                findViewById<TextView>(R.id.adress).text = address
                findViewById<TextView>(R.id.update_at).text = updateAtText
                findViewById<TextView>(R.id.status).text = weatherDescription.capitalize()
                findViewById<TextView>(R.id.temp).text = temp
                findViewById<TextView>(R.id.temp_min).text = tempMin
                findViewById<TextView>(R.id.temp_max).text = tempMax
                findViewById<TextView>(R.id.sunrise).text = SimpleDateFormat("hh:mm a", Locale.ENGLISH).format(Date(sunrise * 1000))
                findViewById<TextView>(R.id.sunset).text = SimpleDateFormat("hh:mm a", Locale.ENGLISH).format(Date(sunset * 1000))
                findViewById<TextView>(R.id.wind).text = windSpeed
                findViewById<TextView>(R.id.pressure).text = pressure
                findViewById<TextView>(R.id.humidity).text = humidity

                findViewById<ProgressBar>(R.id.loader).visibility = View.GONE
                findViewById<RelativeLayout>(R.id.maincontainer).visibility = View.VISIBLE
            } catch (e: Exception) {
                e.printStackTrace()
                findViewById<ProgressBar>(R.id.loader).visibility = View.GONE
                findViewById<TextView>(R.id.errortext).visibility = View.VISIBLE
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者Priyanshu Samal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 13:05:20