Android使用HttpURLConnection报NetworkOnMainThreadException如何解决
错误原因
android.os.NetworkOnMainThreadException 是Android系统的强制限制:主线程负责所有UI渲染和用户交互响应,耗时的网络操作会阻塞主线程导致界面卡顿甚至ANR,因此系统直接禁止在主线程发起网络请求。
具体实现方案(基于java.util.concurrent官方推荐方案)
步骤1:声明全局线程管理工具
在CarSearch类的成员变量位置添加如下代码,用来管理子线程和切换主线程:
// 单线程线程池,专门处理网络请求 private final ExecutorService networkExecutor = Executors.newSingleThreadExecutor(); // 绑定主线程Looper的Handler,用来将UI操作切回主线程执行 private final Handler mainHandler = new Handler(Looper.getMainLooper());
步骤2:修改点击事件中的网络请求逻辑
将你原来try-catch包裹的网络请求代码,全部放到线程池的子线程中执行,涉及UI更新的操作(比如content.setText、弹Toast)要通过mainHandler切回主线程:
insert.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String vehiclenameTXT = name.getText().toString(); String numberplateTXT = plate.getText().toString(); String mileageTXT = mileage.getText().toString(); Toast.makeText(CarSearch.this, "1", Toast.LENGTH_SHORT).show(); Boolean checkinsertdata = DB.insertvehicledata(vehiclenameTXT, numberplateTXT, mileageTXT); if(checkinsertdata) { Toast.makeText(CarSearch.this, "New Vehicle Added", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(CarSearch.this, "Cannot add the same vehicle details twice!", Toast.LENGTH_SHORT).show(); } Toast.makeText(CarSearch.this, "2", Toast.LENGTH_SHORT).show(); // 把网络请求放到子线程执行 networkExecutor.execute(() -> { StringBuilder htmlContent = new StringBuilder(); try { String addr = "https://cartaxcheck.co.uk/free-car-check/?vrm=LN11VUM"; URL url = new URL(addr); HttpURLConnection con = (HttpURLConnection) url.openConnection(); BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); String inputLine; while ((inputLine = in.readLine()) != null) { htmlContent.append(inputLine); } in.close(); // 切回主线程更新UI mainHandler.post(() -> { content.setText(htmlContent.toString()); Toast.makeText(CarSearch.this, "3", Toast.LENGTH_SHORT).show(); }); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }); } });
步骤3:避免内存泄漏
在CarSearch的onDestroy生命周期方法中关闭线程池,释放资源:
@Override protected void onDestroy() { super.onDestroy(); // 页面销毁时关闭线程池,避免内存泄漏 if (!networkExecutor.isShutdown()) { networkExecutor.shutdown(); } }
注意事项
- 所有UI相关操作禁止在子线程执行,必须通过
mainHandler.post投递到主线程 - 如果后续需要发起多个并行网络请求,可以将
Executors.newSingleThreadExecutor()替换为Executors.newCachedThreadPool() - 直接爬取第三方站点HTML需遵守对方的使用条款和robots协议,避免合规风险
内容的提问来源于stack exchange,提问作者Zeeshan
相关产品推荐
相关产品推荐

