Пересмотрите старые проекты для Android 4.0

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





    Здесь надо сделать небольшое отступление. Я начал изучать программирование под 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. Во время опытов при решении проблемы ни одно животное не пострадало.
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 29

      +1
      Подождите, а разве APK собранный под 2.3 не запустится на 4 андроиде в режиме совместимости? Вы ведь имеете в виду именно если собирать под 4 андроид?
        +1
        Гугл вроде как руку на отсечение дает по поводу обратной совместимости.
          +1
          У него рук много — ему одной не жалко…
          +1
          Сами проекты запускаются. Если я закоментирую строчку вызова картинки из интернета, то приложение будет работать. Тут речь не о совместимости.
            +1
            Как же не о ней, вы же явно пишете:
            Представьте себе ситуацию, что в вашем приложении используется загрузка картинки или другого файла из интернета. И часть пользователей, которые обновились до ICS, начинает жаловаться, что ничего не работает. Другая часть пользователей с такой же моделью телефона будет утверждать, что всё прекрасно работает

            Однако же, пока вы не указали в target-sdk версию API ICS, то будет работать обратная совместимость.

            Хотя, замечу, что подгружать что-либо из сети в главном потоке непрофессионально, как минимум.
              0
              Да, это не профессионально, но в демонстрационных целях для себя такое часто используется.
                +1
                Всё что в Интернете — по дефолту не только для себя :)
            0
            Обеспечивается совместимость public API, то есть все классы и интерфейсы которые есть в Android.jar 2.3, будут и в 4.0. Поведение же приложения может отличаться, если в manifest как target api указана 14. В этом случае, напрмер, будет по умолчанию показан Action Bar. Насчет сетевых дел в основном потоке точно не знаю, но думаю, что если target api указан ниже чем honeycomb, то исключения не будет вылетать.
            +15
            1. Почему бы не просто посмотреть LogCat — сразу была бы видна ошибка и в какой строке, и не надо выводить текст ошибки в TextView.
            2. Загружать что-то из сети в UI потоке — зло. Будет тормозить загружаемый ресурс или интернет-будет висеть все приложение, поэтому всегда надо загружать их в отдельном потоке.
              –6
              Я согласен, что зло. Но в 2.3 это работало. Теперь ошибка заставит переделать примеры. В принципе, в серьезных проектах такой подход и не использовали. А вот в простеньких примерах сплошь и рядом.
                +2
                Даже в примерах не соит такого делать-люди учатся и пишут тако. только если пример-как не надо делать.
                  0
                  Вы правы, пример получился неважным. Мне хотелось посмотреть на саму возможность загрузки в ImageView из интернета. Будем исправлять
                    0
                    Хорошо. И остальные примеры, в которых есть неверный подход лучше тоже пересмотрите.
                  +4
                  Тема такая уже 100500 раз поднималась, раз уж учите молодежь, то учите правильно.
                +12
                Что ж это за уроки такие, где учат делать I/O в основном потоке…
                  –2
                  Челябинские?
                  0
                  Наконец-то будут бороться, плохо что не сразу. Еще могли бы какой-нибудь механизм LogCat.Alert придумать, чтобы в режиме обратной совместимости выдавать в Eclipse (так как в обратной совместимости оно не падает).
                • UFO just landed and posted this here
                    +3
                    Вот еще один пример когда сначала пишут код и только потом читают документацию, когда ничего не работает. Я бы посоветовал автору для начала причитать все статьи здесь и только потом применять свои знания в реальных проектах. Спасает от многих ошибок.
                    Единственное что для меня до сих пор не понятно, так это то почему исключение кидается только в Android 3+, ведь могли сделать и раньше.
                      –1
                      Можно заметить, что google стало заботить качество приложений совсем не давно. Раньше и гайдлайнов не было. Может настанет то светлое время, когда на маркете появится хотя-бы минимальная цензура.
                    0
                    Для подобных задач имеет смысл использовать IntentService.
                      0
                      Как из пушки по воробьям.
                      Для таких задач используется AsyncTask или Thread
                      +2
                      мда, сами создали проблему, сами же ее героически решили.
                        +2
                        Посмотрел под котом — нет там никаких подробностей…
                          0
                          под котом, обычные другое можно найти.)
                          • UFO just landed and posted this here
                          +3
                          Ну эм, а то, что у вас зависал раньше интерфейс вас не смущало? И как вы прогресс бар отображали, ну или хотя бы крутящийся значок?

                          Only users with full accounts can post comments. Log in, please.