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

Android模拟器中USGS地震JSON数据无法显示问题求助

问题:HTTP请求获取JSON后无数据显示

我正在Udacity上学习Android开发免费课程,使用课程提供的代码从USGS网站获取地震相关JSON数据并在应用中显示,但始终无法显示数据。请问可能的原因是什么?另外我使用的是Android模拟器,没有真机,这会不会是原因?

以下是相关代码:

/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.example.android.soonami;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;

/**
 * Displays information about a single earthquake.
 */
public class MainActivity extends AppCompatActivity {

    /** Tag for the log messages */
    public static final String LOG_TAG = MainActivity.class.getSimpleName();

    /** URL to query the USGS dataset for earthquake information */
    private static final String USGS_REQUEST_URL = "https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2014-01-01&endtime=2014-12-01&minmagnitude=7";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Kick off an {@link AsyncTask} to perform the network request
        TsunamiAsyncTask task = new TsunamiAsyncTask();
        task.execute();
    }

    /**
     * Update the screen to display information from the given {@link Event}.
     */
    private void updateUi(Event earthquake) {
        // Display the earthquake title in the UI
        TextView titleTextView = (TextView) findViewById(R.id.title);
        titleTextView.setText(earthquake.title);

        // Display the earthquake date in the UI
        TextView dateTextView = (TextView) findViewById(R.id.date);
        dateTextView.setText(getDateString(earthquake.time));

        // Display whether or not there was a tsunami alert in the UI
        TextView tsunamiTextView = (TextView) findViewById(R.id.tsunami_alert);
        tsunamiTextView.setText(getTsunamiAlertString(earthquake.tsunamiAlert));
    }

    /**
     * Returns a formatted date and time string for when the earthquake happened.
     */
    private String getDateString(long timeInMilliseconds) {
        SimpleDateFormat formatter = new SimpleDateFormat("EEE, d MMM yyyy 'at' HH:mm:ss z");
        return formatter.format(timeInMilliseconds);
    }

    /**
     * Return the display string for whether or not there was a tsunami alert for an earthquake.
     */
    private String getTsunamiAlertString(int tsunamiAlert) {
        switch (tsunamiAlert) {
            case 0:
                return getString(R.string.alert_no);
            case 1:
                return getString(R.string.alert_yes);
            default:
                return getString(R.string.alert_not_available);
        }
    }

    /**
     * {@link AsyncTask} to perform the network request on a background thread, and then
     * update the UI with the first earthquake in the response.
     */
    private class TsunamiAsyncTask extends AsyncTask<URL, Void, Event> {

        @Override
        protected Event doInBackground(URL... urls) {
            // Create URL object
            URL url = createUrl(USGS_REQUEST_URL);

            // Perform HTTP request to the URL and receive a JSON response back
            String jsonResponse = "";
            try {
                jsonResponse = makeHttpRequest(url);
            } catch (IOException e) {
                // TODO Handle the IOException
            }

            // Extract relevant fields from the JSON response and create an {@link Event} object
            Event earthquake = extractFeatureFromJson(jsonResponse);

            // Return the {@link Event} object as the result of the {@link TsunamiAsyncTask}
            return earthquake;
        }

        /**
         * Update the screen with the given earthquake (which was the result of the
         * {@link TsunamiAsyncTask}).
         */
        @Override
        protected void onPostExecute(Event earthquake) {
            if (earthquake == null) {
                return;
            }
            updateUi(earthquake);
        }

        /**
         * Returns new URL object from the given string URL.
         */
        private URL createUrl(String stringUrl) {
            URL url = null;
            try {
                url = new URL(stringUrl);
            } catch (MalformedURLException exception) {
                Log.e(LOG_TAG, "Error with creating URL", exception);
                return null;
            }
            return url;
        }

        /**
         * Make an HTTP request to the given URL and return a String as the response.
         */
        private String makeHttpRequest(URL url) throws IOException {
            String jsonResponse = "";
            HttpURLConnection urlConnection = null;
            InputStream inputStream = null;
            try {
                urlConnection = (HttpURLConnection) url.openConnection();
                urlConnection.setRequestMethod("GET");
                urlConnection.setReadTimeout(10000 /* milliseconds */);
                urlConnection.setConnectTimeout(15000 /* milliseconds */);
                urlConnection.connect();
                inputStream = urlConnection.getInputStream();
                jsonResponse = readFromStream(inputStream);
            } catch (IOException e) {
                // TODO: Handle the exception
            } finally {
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }
                if (inputStream != null) {
                    // function must handle java.io.IOException here
                    inputStream.close();
                }
            }
            return jsonResponse;
        }

        /**
         * Convert the {@link InputStream} into a String which contains the
         * whole JSON response from the server.
         */
        private String readFromStream(InputStream inputStream) throws IOException {
            StringBuilder output = new StringBuilder();
            if (inputStream != null) {
                InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
                BufferedReader reader = new BufferedReader(inputStreamReader);
                String line = reader.readLine();
                while (line != null) {
                    output.append(line);
                    line = reader.readLine();
                }
            }
            return output.toString();
        }

        /**
         * Return an {@link Event} object by parsing out information
         * about the first earthquake from the input earthquakeJSON string.
         */
        private Event extractFeatureFromJson(String earthquakeJSON) {
            try {
                JSONObject baseJsonResponse = new JSONObject(earthquakeJSON);
                JSONArray featureArray = baseJsonResponse.getJSONArray("features");
                // If there are results in the features array
                if (featureArray.length() > 0) {
                    // Extract out the first feature (which is an earthquake)
                    JSONObject firstFeature = featureArray.getJSONObject(0);
                    JSONObject properties = firstFeature.getJSONObject("properties");

                    // Extract out the title, time, and tsunami values
                    String title = properties.getString("title");
                    long time = properties.getLong("time");
                    int tsunamiAlert = properties.getInt("tsunami");

                    // Create a new {@link Event} object
                    return new Event(title, time, tsunamiAlert);
                }
            } catch (JSONException e) {
                Log.e(LOG_TAG, "Problem parsing the earthquake JSON results", e);
            }
            return null;
        }
    }
}

问题排查与解决方案

先明确一点:Android模拟器本身不会导致数据无法显示,只要模拟器能正常连接外网,就能和真机一样发起网络请求。下面是具体的排查方向和修复建议:

一、最可能的原因:缺失网络权限

Android应用默认没有访问互联网的权限,必须在AndroidManifest.xml中显式声明。你需要在<application>标签外部添加以下权限:

<uses-permission android:name="android.permission.INTERNET"/>

如果没有这个权限,所有网络请求都会静默失败,不会有任何日志提示,这是新手最容易踩的坑。

二、代码中的关键问题:异常处理缺失

你的代码里多个异常块都只是TODO注释,没有任何日志输出,导致你根本不知道请求过程中哪里出错了。比如:

  • 网络连接超时
  • 服务器返回错误状态码(如404、500)
  • 输入流读取失败

修复方法:添加异常日志

在doInBackground和makeHttpRequest的catch块中添加日志,方便定位问题:

// 在doInBackground的catch块中
catch (IOException e) {
    Log.e(LOG_TAG, "Failed to perform HTTP request", e);
}

// 在makeHttpRequest的catch块中
catch (IOException e) {
    Log.e(LOG_TAG, "Error connecting to server or reading response", e);
}

添加后,你可以在Android Studio的Logcat中过滤LOG_TAG(即MainActivity),查看具体的错误信息。

三、未校验HTTP响应状态码

代码中直接调用urlConnection.getInputStream(),但如果服务器返回非200的状态码(比如URL无效、服务器维护),这一步会抛出异常,导致jsonResponse为空,最终解析出的Event对象为null,onPostExecute直接返回,不更新UI。

修复方法:添加状态码校验

在makeHttpRequest中连接服务器后,先检查响应状态码:

urlConnection.connect();
// 新增:检查响应码是否在成功范围内(200-299)
int responseCode = urlConnection.getResponseCode();
if (responseCode < 200 || responseCode >= 300) {
    Log.e(LOG_TAG, "Server returned error code: " + responseCode);
    throw new IOException("Invalid response code: " + responseCode);
}
// 只有响应成功才读取输入流
inputStream = urlConnection.getInputStream();

四、JSON解析前未判空

如果jsonResponse为空字符串,new JSONObject(earthquakeJSON)会抛出异常,虽然你有catch块,但最好提前判空,避免不必要的异常:

private Event extractFeatureFromJson(String earthquakeJSON) {
    // 新增:先判断JSON字符串是否为空
    if (android.text.TextUtils.isEmpty(earthquakeJSON)) {
        Log.e(LOG_TAG, "JSON response is empty");
        return null;
    }
    // 原解析代码...
}

记得要导入android.text.TextUtils类。

五、验证模拟器网络是否正常

打开模拟器的浏览器,输入你使用的USGS URL:
https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2014-01-01&endtime=2014-12-01&minmagnitude=7
如果能正常加载JSON数据,说明模拟器网络没问题;如果不能,检查模拟器的网络设置(比如是否开启代理、宿主机网络是否正常)。


按照以上步骤排查,应该能快速找到问题并解决。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:13:57