Avatar ·

Как исправить ошибку android.os.NetworkOnMainThreadException в Android?

Всем привет. Я начинающий Android-разработчик, пишу простое приложение, которое должно получать данные с сервера по HTTP и отображать их в TextView. Но при запуске приложение вылетает с ошибкой `android.os.NetworkOnMainThreadException`. Вот мой код: ```java public class MainActivity extends AppCompatActivity { private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.textView); // Пытаюсь получить данные с сервера try { URL url = new URL("https://api.example.com/data"); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); StringBuilder response = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { response.append(line); } reader.close(); String result = response.toString(); textView.setText(result); } catch (Exception e) { e.printStackTrace(); } } } ``` Я понимаю, что ошибка связана с тем, что я делаю сетевой запрос в главном потоке. Но как правильно это исправить? Я слышал про AsyncTask, но говорят, что он устарел. Какие есть современные способы выполнять сетевые запросы в Android? И как вообще организовать асинхронную работу, чтобы не блокировать UI? Заранее спасибо за помощь! <<>>

Проблема: почему возникает NetworkOnMainThreadException

В Android начиная с версии 3.0 (Honeycomb) система запрещает выполнять сетевые операции в главном (UI) потоке. Это сделано, чтобы приложение не "зависало" при медленном соединении — пользовательский интерфейс должен оставаться отзывчивым. Если вы делаете HTTP-запрос в onCreate() или любом другом методе, который выполняется в главном потоке, система выбрасывает NetworkOnMainThreadException.

Решение: используем корутины с Retrofit (современный подход)

Самый правильный способ в современной Android-разработке — использовать Kotlin Coroutines в сочетании с библиотекой Retrofit для сетевых запросов. Но так как ваш код на Java, я покажу решение на Java с использованием AsyncTask (хотя он помечен как устаревший, он всё ещё работает и прост для понимания новичками) и более современный вариант с Thread + Handler.

Вариант 1: AsyncTask (простой, но устаревший)


public class MainActivity extends AppCompatActivity {
    private TextView textView;

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

        new NetworkTask().execute("https://api.example.com/data");
    }

    private class NetworkTask extends AsyncTask<String, Void, String> {
        @Override
        protected String doInBackground(String... urls) {
            // Этот код выполняется в фоновом потоке
            try {
                URL url = new URL(urls[0]);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                StringBuilder response = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    response.append(line);
                }
                reader.close();
                return response.toString();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

        @Override
        protected void onPostExecute(String result) {
            // Этот код выполняется в главном потоке после завершения фоновой работы
            if (result != null) {
                textView.setText(result);
            } else {
                textView.setText("Ошибка загрузки");
            }
        }
    }
}

Вариант 2: Thread + Handler (более гибкий)


public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private Handler mainHandler = new Handler(Looper.getMainLooper());

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

        new Thread(new Runnable() {
            @Override
            public void run() {
                // Фоновый поток
                try {
                    URL url = new URL("https://api.example.com/data");
                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("GET");
                    BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                    StringBuilder response = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        response.append(line);
                    }
                    reader.close();
                    final String result = response.toString();

                    // Возвращаем результат в главный поток
                    mainHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            textView.setText(result);
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                    mainHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            textView.setText("Ошибка загрузки");
                        }
                    });
                }
            }
        }).start();
    }
}

Рекомендации

  • Не используйте чистый HttpURLConnection в продакшене — лучше взять Retrofit или OkHttp, которые обрабатывают много подводных камней (таймауты, кэширование, переподключение).
  • Всегда обрабатывайте ошибки — сеть нестабильна, пользователь может быть без интернета.
  • Не забывайте про разрешения — в AndroidManifest.xml нужно добавить <uses-permission android:name="android.permission.INTERNET" />.
  • Для долгих операций (загрузка файлов, стриминг) используйте WorkManager или Services.

Помните: главный поток — только для UI. Любая длительная операция (сеть, база данных, сложные вычисления) должна выполняться в фоне.

<<>>

Альтернативный подход: использование библиотеки Volley

Если вы не хотите возиться с низкоуровневыми потоками, рекомендую использовать библиотеку Volley от Google. Она специально создана для простых сетевых запросов в Android и автоматически управляет потоками.

Добавьте зависимость в build.gradle:

implementation 'com.android.volley:volley:1.2.1'

Пример использования:


public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private RequestQueue requestQueue;

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

        requestQueue = Volley.newRequestQueue(this);
        String url = "https://api.example.com/data";

        StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
            new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {
                    // Успех — выполняется в главном потоке
                    textView.setText(response);
                }
            },
            new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    // Ошибка
                    textView.setText("Ошибка: " + error.getMessage());
                }
            });

        requestQueue.add(stringRequest);
    }
}

Volley автоматически выполняет запрос в фоновом потоке и возвращает результат в главный. Также он поддерживает кэширование, повторные попытки и отмену запросов при уничтожении Activity. Это отличный выбор для небольших проектов или прототипов.