Retrofit同步请求触发android.os.NetworkOnMainThreadException如何解决?
android.os.NetworkOnMainThreadException for Retrofit Synchronous Calls Let's break down what's happening here and how to fix it quickly.
Why This Error Happens
Android blocks network operations on the main thread by design—this thread is meant for handling UI interactions to keep your app feeling responsive. When you call tasks.execute() directly (a synchronous call) from the main thread, you're violating that rule, hence the NetworkOnMainThreadException.
Best Solutions to Fix It
1. Switch to Retrofit's Asynchronous enqueue() (Recommended)
Retrofit has built-in support for async calls that run on a background thread automatically. Here's how to adjust your code:
public void loginInto() { LOGININTERFACE taskService = ServiceGenerator.createService(LOGININTERFACE.class); Call<LGOINMODEL> loginCall = taskService.savePost("0016642902","0016642902","password"); loginCall.enqueue(new Callback<LGOINMODEL>() { @Override public void onResponse(Call<LGOINMODEL> call, Response<LGOINMODEL> response) { if (response.isSuccessful()) { LGOINMODEL model = response.body(); // Handle success here (e.g., save user data, navigate to home screen) // Remember: If you need to update UI, do it on the main thread runOnUiThread(() -> { // Example: Update a TextView or show a toast }); } else { // Handle error responses (e.g., invalid credentials) } } @Override public void onFailure(Call<LGOINMODEL> call, Throwable t) { // Handle network failures (e.g., no internet connection) t.printStackTrace(); } }); }
2. Use a Background Thread for Synchronous Calls (If You Must)
If you have a specific reason to keep using execute() (synchronous), wrap the call in a background thread. You can use a simple Thread or an ExecutorService for better thread management:
public void loginInto() { new Thread(() -> { try { LOGININTERFACE taskService = ServiceGenerator.createService(LOGININTERFACE.class); Call<LGOINMODEL> loginCall = taskService.savePost("0016642902","0016642902","password"); LGOINMODEL model = loginCall.execute().body(); // Need to update UI? Switch back to main thread runOnUiThread(() -> { // UI updates go here }); } catch (IOException e) { e.printStackTrace(); } }).start(); }
3. Kotlin Coroutines (For Kotlin Projects)
If you're using Kotlin, coroutines simplify background tasks a lot. Wrap your synchronous call in a coroutine scope with an IO dispatcher, then switch back to main thread for UI work:
fun loginInto() = CoroutineScope(Dispatchers.IO).launch { val taskService = ServiceGenerator.createService(LOGININTERFACE::class.java) try { val response = taskService.savePost("0016642902","0016642902","password").execute() if (response.isSuccessful) { val model = response.body() // Update UI on main thread withContext(Dispatchers.Main) { // Do UI updates here } } } catch (e: IOException) { e.printStackTrace() } }
Important Reminders
- Never run network code on the main thread: It will make your app freeze and can lead to ANRs (Application Not Responding) if the call takes too long.
- UI updates must happen on the main thread: Always use
runOnUiThread()(Java) orwithContext(Dispatchers.Main)(Kotlin) when modifying views after a network call.
内容的提问来源于stack exchange,提问作者behzad

