Android中HttpURLConnection触发NetworkOnMainThreadException原因咨询
兄弟,这个异常其实是Android系统给咱们新手的一个「规范提醒」,咱们来一步步掰扯清楚:
异常产生的核心原因
Android的**主线程(UI线程)**是专门用来处理界面渲染、用户交互(比如点击按钮、滑动屏幕)的,它要求必须时刻保持响应速度——如果主线程被阻塞超过5秒,系统就会弹出「应用无响应(ANR)」的提示,这体验就太差了。
而网络请求是典型的耗时操作:你发请求到服务器,服务器处理、返回数据,期间可能因为网络差、服务器负载高等待好几秒。如果把网络请求直接放在onCreate这种主线程的方法里,主线程就会被彻底卡住,没法处理任何UI相关的操作。Android系统为了避免这种糟糕的用户体验,从API 11(Android 3.0)开始,就强制禁止在主线程中执行网络操作,一旦发现就抛出NetworkOnMainThreadException。
为啥IntelliJ里的代码能正常运行?
你在IntelliJ里写的是普通Java桌面程序,桌面程序的主线程没有Android这么严格的限制——就算主线程被网络请求卡住,顶多界面假死,但系统不会主动抛出异常阻止你。但Android是移动系统,对流畅性要求更高,所以有了这个强制规范。
怎么解决这个问题?
咱们需要把网络请求放到子线程里执行,等请求完成后再回到主线程更新UI。给你几个适合新手的方案:
方案1:用AsyncTask(快速改造你的现有代码)
AsyncTask是Android提供的轻量级异步任务工具,专门用来处理「后台耗时操作+主线程更新UI」的场景,直接改造你的MainActivity代码:
package android.adelsaadeddin.app3; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.os.AsyncTask; import android.widget.TextView; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class MainActivity extends AppCompatActivity { private TextView textView; @Override protected void onCreate (Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.text); // 启动异步任务 new NetworkTask().execute(); } // 异步任务类:Params, Progress, Result private class NetworkTask extends AsyncTask<Void, Void, String> { @Override protected String doInBackground(Void... voids) { // 这里是子线程,执行网络请求 HttpURLConnection urlConnection = null; try { URL url = new URL("http://codeforces.com/api/user.rating?handle=Adel_SaadEddin"); urlConnection = (HttpURLConnection) url.openConnection(); InputStream in = urlConnection.getInputStream(); InputStreamReader isw = new InputStreamReader(in); StringBuilder stringBuilder = new StringBuilder(); int data = isw.read(); while (data != -1) { char current = (char) data; data = isw.read(); stringBuilder.append(current); } return stringBuilder.toString(); } catch (Exception ex) { return "Error"; } finally { if (urlConnection != null) { urlConnection.disconnect(); } } } @Override protected void onPostExecute(String result) { // 这里回到主线程,更新UI textView.setText(result); } } }
方案2:用Kotlin协程(更现代的方式)
如果以后你开始用Kotlin开发,协程是更简洁的异步处理方式,代码会更清爽,不用写嵌套的任务类。
额外注意事项
你的代码里用了http://明文协议,在Android 9(API 28)及以上版本,默认是禁止明文网络请求的,会导致请求失败。解决办法有两个:
- 把URL改成
https://codeforces.com/api/user.rating?handle=Adel_SaadEddin(推荐,更安全) - 或者在
AndroidManifest.xml的<application>标签里添加android:usesCleartextTraffic="true"
内容的提问来源于stack exchange,提问作者Adel SaadEddin

