Сегодня, когда писал для своего сайта новый учебный пример, тренируясь на кошках, неожиданно столкнулся с одной проблемой на ровном месте. В коде программы нужно было реализовать загрузку изображения из интернета и сохранить его на SD-карточку. На первый взгляд задача не представляла трудностей, но проект вдруг стал выдавать ошибку.

Здесь надо сделать небольшое отступление. Я начал изучать программирование под Android сравнительно недавно и начинал с версии Android 2.3, так как у меня был телефон с такой же версией. И все проекты пробовал на эмуляторе под этой же версией. Пару месяцев назад для моего Samsung Galaxy SII вышло обновление до ICS (Android 4.0) и я стал создавать новые проекты под новую версию, а также переписывать свои учебные примеры под новую платформу. Периодически в Eclipse при загрузке старых проектов попадались строчки кода с зачёркнутыми методами. Было ясно, что данный метод устарел и нужно перечитать документацию.
В моём последнем случае ничего не подчёркивалось, а природа ошибки была не совсем ясна. В одном из моих уроков использовался схожий пример загрузки изображения из сети в ImageView, который прекрасно работал раньше в Android 2.3. Ради интереса, я запустил этот пример под эмулятором Android 4.0, будучи абсолютно уверенный в работоспособности примера. Но не тут-то было, и этот пример не запускался. Заменив в примере одну строчку
я получил описание ошибки — android.os.NetworkOnMainThreadException. Дальше было проще. Имея эту информацию и призвал на помощь Гугл, я выяснил, что, начиная с Android 3.0 правила немного изменились. Теперь нельзя обращаться к сетевым ресурсам в основном потоке активности. Поэтому пришлось переделывать пример на новый лад.
В исправленном варианте пример заработал. Возможно, код ещё требует доработки, но уже ясно в каком направлении нужно двигаться.
Наверняка, компании и серьёзные разработчики в курсе этой проблемы. Решение проблемы уже описано и в документации и на StackOverflow.com. Профессионалы не пишут длительные операции в основном потоке и они и не заметят проблемы вообще. В моём случае для меня это стало сюрпризом, так как в рабочих проектах не было необходимости использовать загрузку изображений из интернета, а вот в учебном примере такой «неправильный» код попался. И думается, что для многих программистов-любителей это может стать головной болью. Представьте себе ситуацию, что в вашем приложении используется загрузка картинки или другого файла из интернета. И часть пользователей, которые обновились до ICS, начинает жаловаться, что ничего не работает. Другая часть пользователей с такой же моделью телефона будет утверждать, что всё прекрасно работает. Догадаться про источник проблемы будет достаточно затруднительно.
Вывод: Пересмотрите свои старые проекты на предмет работоспособности под Android 4.0. Буквально вчера обновилась статистика устройств под разные версии Android и Android 4 уже обогнала версию 2.1. Вполне допускаю, что к концу года четверка подвинет и версию 2.2. Поэтому нужно уже сейчас задуматься об этой проблеме.
P.S. Во время опытов при решении проблемы ни одно животное не пострадало.

Здесь надо сделать небольшое отступление. Я начал изучать программирование под Android сравнительно недавно и начинал с версии Android 2.3, так как у меня был телефон с такой же версией. И все проекты пробовал на эмуляторе под этой же версией. Пару месяцев назад для моего Samsung Galaxy SII вышло обновление до ICS (Android 4.0) и я стал создавать новые проекты под новую версию, а также переписывать свои учебные примеры под новую платформу. Периодически в Eclipse при загрузке старых проектов попадались строчки кода с зачёркнутыми методами. Было ясно, что данный метод устарел и нужно перечитать документацию.
В моём последнем случае ничего не подчёркивалось, а природа ошибки была не совсем ясна. В одном из моих уроков использовался схожий пример загрузки изображения из сети в ImageView, который прекрасно работал раньше в Android 2.3. Ради интереса, я запустил этот пример под эмулятором Android 4.0, будучи абсолютно уверенный в работоспособности примера. Но не тут-то было, и этот пример не запускался. Заменив в примере одну строчку
txtUrl.setText(e.toString());
я получил описание ошибки — android.os.NetworkOnMainThreadException. Дальше было проще. Имея эту информацию и призвал на помощь Гугл, я выяснил, что, начиная с Android 3.0 правила немного изменились. Теперь нельзя обращаться к сетевым ресурсам в основном потоке активности. Поэтому пришлось переделывать пример на новый лад.
Посмотреть код
package ru.alexanderklimov.imagefrominet; import java.io.InputStream; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.widget.ImageView; public class ImageFromInetActivity extends Activity { private ImageView imgView; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); imgView = (ImageView) findViewById(R.id.imgView); // Веб-адрес картинки String url = "http://developer.alexanderklimov.ru/android/images/android_cat.jpg"; // show The Image new DownloadImageTask(imgView) .execute(url); } private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { ImageView bmImage; public DownloadImageTask(ImageView bmImage) { this.bmImage = bmImage; } protected Bitmap doInBackground(String... urls) { String urldisplay = urls[0]; Bitmap mIcon11 = null; try { InputStream in = new java.net.URL(urldisplay).openStream(); mIcon11 = BitmapFactory.decodeStream(in); } catch (Exception e) { Log.e("Error", e.getMessage()); e.printStackTrace(); } return mIcon11; } protected void onPostExecute(Bitmap result) { bmImage.setImageBitmap(result); } } }
В исправленном варианте пример заработал. Возможно, код ещё требует доработки, но уже ясно в каком направлении нужно двигаться.
Наверняка, компании и серьёзные разработчики в курсе этой проблемы. Решение проблемы уже описано и в документации и на StackOverflow.com. Профессионалы не пишут длительные операции в основном потоке и они и не заметят проблемы вообще. В моём случае для меня это стало сюрпризом, так как в рабочих проектах не было необходимости использовать загрузку изображений из интернета, а вот в учебном примере такой «неправильный» код попался. И думается, что для многих программистов-любителей это может стать головной болью. Представьте себе ситуацию, что в вашем приложении используется загрузка картинки или другого файла из интернета. И часть пользователей, которые обновились до ICS, начинает жаловаться, что ничего не работает. Другая часть пользователей с такой же моделью телефона будет утверждать, что всё прекрасно работает. Догадаться про источник проблемы будет достаточно затруднительно.
Вывод: Пересмотрите свои старые проекты на предмет работоспособности под Android 4.0. Буквально вчера обновилась статистика устройств под разные версии Android и Android 4 уже обогнала версию 2.1. Вполне допускаю, что к концу года четверка подвинет и версию 2.2. Поэтому нужно уже сейчас задуматься об этой проблеме.
P.S. Во время опытов при решении проблемы ни одно животное не пострадало.
