Привязать к службе из нового контекста для изменений конфигурации или привязать из контекста приложения?

Я пытаюсь выяснить, подходит ли связанный сервис для фоновой работы в моем приложении. Требования заключаются в том, что различные компоненты приложения могут делать через него веб-запросы с различным приоритетом. (Таким образом, служба должна поддерживать какую-то очередь и иметь возможность отменять текущие запросы для других с более высоким приоритетом). Я бы хотел, чтобы служба была относительно ненавязчивой для пользователя, чтобы он не обнаружил ее работающей после завершения работы с приложением. Если я хочу сделать что-то более важное, что продолжается, пока приложение закрыто, я могу использовать startForeground( ), чтобы отправить уведомление во время процесса.

Решение первое: выполнить привязку из действия

Таким образом, для данного компонента приложения он должен иметь возможность связываться со службой для выполнения работы. Но, по-видимому, существует хорошо известная проблема, заключающаяся в том, что если действие выполняет привязку, привязка будет потеряна во время изменения конфигурации (ротации), поскольку действие будет закрыто.

Итак, я подумал, что мог бы использовать другой созданный мной контекст (new Context()) и привязать его к службе, а затем использовать фрагмент, не относящийся к пользовательскому интерфейсу, для сохранения этого контекста при изменении конфигурации, пока я не решу, что с ним покончено. Я мог сделать это только во время изменения конфигурации или как постоянную альтернативу привязке из активности. (Возможно, я должен указать, что это стандарт и рекомендуется способ поддерживать экземпляры при изменении конфигурации)

Решение номер 2:

Основная альтернатива, которую я вижу, заключается в том, что я могу использовать контекст приложения для выполнения привязки, но может ли это сохраняться слишком долго? и/или может быть какая-то циклическая связь между контекстом приложения и службой, что предотвращает уничтожение службы и контекста приложения?

Вопросы:

Итак, вопрос, на который я пытаюсь ответить себе: должен ли я использовать первый метод (действия с временными контекстами)? Или второй (просто привязать сервис к контексту приложения)?

Правильно ли я понимаю, что контекст приложения может связываться со службой несколько раз, а затем столько же раз от нее отвязываться? (Т.е. что у вас может быть несколько действительных привязок для PER контекста)?

Может ли использование моего собственного контекста (new Context()) в первом решении вызвать какие-либо проблемы?

Изменить

Нашел дополнительную информацию: https://groups.google.com/forum/#!topic/android-developers/Nb58dOQ8Xfw

Также кажется, что будет сложно «создать» контекст произвольно, поэтому комбинация решений 1 и 2 кажется подходящей, когда соединение со службой сохраняется при изменении конфигурации, но привязка осуществляется к контексту приложения. Меня все еще беспокоит возможность двойной отвязки от контекста приложения. Самому вести подсчет привязок кажется ненужным - может ли кто-нибудь подтвердить/опровергнуть, что привязки относятся к соединению, а не к контексту?


comment
Он предназначен для использования привязки для ссылок Activity-Service. Я не понимаю, почему вы просто не используете его так, как он должен был использоваться. Просто привяжите свою активность снова после изменения конфигурации.   -  person RaphMclee    schedule 21.06.2014
comment
Raph - не умрет ли сервис при уничтожении контекста   -  person Sam    schedule 22.06.2014
comment
т.е. Я не хочу откладывать долгое сетевое действие только потому, что телефон повернут....   -  person Sam    schedule 22.06.2014
comment
По-разному. Если вы запускаете службу с помощью bind. Служба будет уничтожена после удаления всех привязок. Но можно запустить службу независимо от привязки, чтобы она выжила даже без всякой привязки. Вы хотите отсрочить что-нибудь.   -  person RaphMclee    schedule 22.06.2014
comment
Да, извините, я не ясно выразился - я мог бы запустить его с привязкой, чтобы он существовал только до тех пор, пока это необходимо.   -  person Sam    schedule 23.06.2014
comment
Вы знаете метод continueInstance() для активности? Вероятно, это соответствует вашим потребностям.   -  person RaphMclee    schedule 24.06.2014
comment
Нет такого метода. Вы говорите о методе setRetainInstance(Boolean) для фрагментов? Это то, что я имел в виду, когда говорил «фрагмент, не связанный с пользовательским интерфейсом». Посмотрите здесь, возможно, вы немного устарели, так как некоторые методы устарели: developer.android.com/guide/topics/resources/   -  person Sam    schedule 24.06.2014
comment
Ваш service может быть и started, и bound. Тогда unbind не уничтожит service (сама служба будет нести ответственность за вызов stopSelf, когда решит, что задание выполнено И никакая активность не привязана к нему).   -  person Tomasz Gawel    schedule 27.06.2014
comment
@TomaszGawel Я думаю, что автоматическое уничтожение в UnBind - самая желательная функция связанного сервиса.   -  person Sam    schedule 30.06.2014
comment
@TomaszGawel Я разместил код в своем ответе, чтобы продемонстрировать, что я имею в виду...   -  person Sam    schedule 30.06.2014


Ответы (4)


Итак, покопавшись, я думаю, что нашел (пока) непроверенное решение.

Во-первых, на основе предложения Дианы здесь: https://groups.google.com/forum/#!topic/android-developers/Nb58dOQ8Xfw Я должен быть привязан к контексту приложения, поэтому моя проблема с потерей контекста исчезла. Я могу поддерживать свое ServiceConnection при изменении конфигурации с помощью фрагмента, отличного от пользовательского интерфейса. отличный. Затем, когда я закончу, я могу использовать контекст приложения, чтобы вернуть соединение со службой и отменить привязку. Я не должен получать никаких предупреждений об утечке сервисного соединения. (Возможно, я должен указать, что это стандарт и рекомендуется способ поддерживать экземпляры при изменении конфигурации)

Последней проблемой этой проблемы было то, что я не был уверен, смогу ли я привязываться несколько раз из одного и того же контекста - документация по привязкам подразумевает, что существует некоторая зависимость между привязкой и жизненным циклом контекста, и поэтому я беспокоился, что мне придется делать свои собственные форма подсчета ссылок. Я просмотрел исходный код и оказался здесь: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.2_r1/android/app/LoadedApk.java#LoadedApk.forgetServiceDispatcher%28android.content.Context%2Candroid.content.ServiceConnection%29

Главное, эти строки:

sd = map.get(c);
    if (sd != null) {
        map.remove(c);
        sd.doForget();
        if (map.size() == 0) {
            mServices.remove(context);
        }

Выясните, что map используется для подсчета ссылок, о котором я беспокоился.

Так что взять домой это:

  • Связанная служба будет нормально работать с контекстом приложения, и мы ДОЛЖНЫ сделать это, чтобы предотвратить утечку соединения службы из одного действия в другое во время изменения конфигурации.
  • Я могу безопасно сохранить подключение службы к фрагменту, отличному от пользовательского интерфейса, и использовать его для отмены привязки, когда закончу.

Я постараюсь вскоре опубликовать проверенный код.

ОБНОВЛЕНИЕ и протестированное решение: я сделал код для проверки и опубликовал его здесь: https://github.com/samskiter/BoundServiceTest

Кажется, это работает довольно хорошо, и фрагмент, не относящийся к пользовательскому интерфейсу (фрагмент данных), действует как хороший прокси-слушатель во время ротационных изменений, чтобы перехватывать результаты службы (намерение слушателей состоит в том, чтобы тесно привязать запросы к пользовательскому интерфейсу, чтобы гарантировать он остается отзывчивым.Очевидно, что любые изменения модели могут быть переданы в пользовательский интерфейс через наблюдателей.)

Изменить: я думал, что должен явно ответить на вопросы в ОП...

  • следует ли использовать первый метод (действия с временными контекстами)? Или второй (просто привязать сервис к контексту приложения)? Второй

  • Правильно ли я понимаю, что контекст приложения может связываться со службой несколько раз, а затем столько же раз от нее отвязываться? (Т.е. что у вас может быть несколько действительных привязок для PER контекста)? Да

  • Может ли использование моего собственного контекста (new Context()) в первом решении вызвать какие-либо проблемы? Это даже невозможно

Окончательное резюме:

Этот шаблон должен быть довольно мощным — я могу расставить приоритеты сетевого ввода-вывода (или других задач), поступающих из различных источников в моем приложении. У меня могла бы быть активность переднего плана, делающая небольшой ввод-вывод, о котором просил пользователь, одновременно я мог бы запустить службу переднего плана для синхронизации всех данных моих пользователей. И служба переднего плана, и действие могут быть привязаны к одной и той же сетевой службе для выполнения своих запросов.

Все это при условии, что сервис живет ровно столько, сколько ему нужно, то есть он прекрасно работает с Android.

Я очень рад получить это в приложении в ближайшее время.

ОБНОВЛЕНИЕ: я попытался написать это и дать некоторый контекст более широкой проблеме фоновой работы в записи блога здесь: http://blog.airsource.co.uk/2014/09/10/android-bound-services/

person Sam    schedule 27.06.2014
comment
Зачем вам посредник через фоновый фрагмент? Это только кажется излишне сложным. - person Rarw; 27.06.2014
comment
так что вы можете сохранить связь. это новая альтернатива использованию onretainnonconfigurationinstance и не такая сложная - person Sam; 28.06.2014
comment
Это просто еще один уровень сложности. Вы используете фрагмент, чтобы просто поддерживать контекст, к которому вы также привязываете службу, что странно для меня, поскольку служба имеет свой собственный контекст и может работать без привязки к чему-либо. Но опубликуйте свое окончательное решение, потому что мне интересно посмотреть, что вы в конечном итоге используете. - person Rarw; 28.06.2014
comment
Ах, извините, я, возможно, объяснил это плохо. Я собираюсь использовать фрагмент только для поддержания соединения, а не контекста. Поддержание такого контекста привело бы к утечке. Сохранение соединения — это фактически просто способ хранения указателей на службу и, таким образом, поддержания ее в рабочем состоянии. Поэтому, когда все «указатели» на службу исчезли, ее можно уничтожить. - person Sam; 28.06.2014
comment
Вы имеете в виду ссылки. Но жизненный цикл вашего фрагмента по-прежнему привязан к активности, в которой он размещен, фоновой или иной. Так как же изменение конфигурации не приводит к изменению жизненного цикла фрагмента и все равно не приводит к потере соединения со службой? - person Rarw; 28.06.2014
comment
Он использует фрагмент, не относящийся к пользовательскому интерфейсу, который сохраняется при изменении конфигурации: developer .android.com/guide/topics/resources/ - person Sam; 28.06.2014
comment
Любые мысли @Rarw или другие? - person Sam; 30.06.2014
comment
Выглядит неплохо. Вид приятного поворота шаблона наблюдателя для Android. - person Rarw; 30.06.2014
comment
Да, поведение кэширования фрагмента данных само по себе отлично подходит для обработки асинхронных обратных вызовов (в отличие от необходимости повторной подписки на обычные наблюдаемые и чтения начального состояния при каждом onCreate()). - person Sam; 01.07.2014

Можете ли вы не просто выбрать конфигурации, которые вы хотели бы обрабатывать с помощью атрибута configChanges в вашем манифесте, и вручную изменить ориентацию в пользовательском интерфейсе? В этом случае вам нужно только привязаться к сервису в onCreate, а затем отменить привязку в onDestroy.

или, может быть, попробуйте что-то вроде этого (я не сделал должной проверки ошибок):

  

    class MyServiceConnection implements ServiceConnection,Parcelable {
                public static final Parcelable.Creator CREATOR
                = new Parcelable.Creator() {
                    public MyServiceConnection createFromParcel(Parcel in) {
                        return new MyServiceConnection(in);
                    }

                    public MyServiceConnection[] newArray(int size) {
                        return new MyServiceConnection[size];
                    }
                };

                @Override
                public int describeContents() {
                    return 0;
                }

                @Override
                public void writeToParcel(Parcel dest, int flags) {

                }

                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {

                }

                @Override
                public void onServiceDisconnected(ComponentName name) {

                }
            }
            MyServiceConnection myServiceConnection;
            boolean configChange = false;

            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
                if (savedInstanceState != null) {
                    myServiceConnection = savedInstanceState.getParcelable("serviceConnection");
                } else {
                    myServiceConnection = new MyServiceConnection();
                }

            }
            @Override
            protected void onSaveInstanceState(Bundle outState) {
                super.onSaveInstanceState(outState);
                if (myServiceConnection != null) {
                    outState.putParcelable("serviceConnection",myServiceConnection);
                    configChange = true;
                }
            }
            @Override
            protected void onDestroy() {
                super.onDestroy();
                if (!configChange && myServiceConnection != null){
                    unbindService(myServiceConnection);
                }
            }
        }

person hoomi    schedule 26.06.2014
comment
Не совсем так - это крайняя мера. См. комментарии Ромена Гая здесь: stackoverflow.com/questions/2620917/ Извиняюсь за голосование против - я не хочу, чтобы другие доходили до этого вопроса и решали проблему таким образом. - person Sam; 26.06.2014
comment
Не беспокойся. Я добавил еще одно решение. - person hoomi; 26.06.2014
comment
хммм, я вижу, вы пытаетесь сделать сервисное подключение разделяемым, но я не понимаю, как это происходит. Не могли бы вы объяснить немного больше, пожалуйста? - person Sam; 26.06.2014
comment
Чтобы отвязаться от сервиса, вам нужен экземпляр подключения к сервису, который вы использовали при привязке к сервису. Поскольку подключение к службе является интерфейсом, вашему классу не нужно иметь никаких полей. Вот почему у меня есть пустые конструкторы. Однако вы можете добавить некоторые поля, если хотите, но затем вы должны соответствующим образом изменить метод разделения. Дайте мне знать, если вам нужно больше объяснений - person hoomi; 27.06.2014
comment
но вы ничего не делаете в writeToParcel? и если у вас есть только пустые конструкторы, как вы вызываете этот метод: MyServiceConnection(in) - person Sam; 27.06.2014
comment
Я использую writeToParcel только в том случае, если в моем классе есть поля, но в этом примере их нет. Кроме того, мне не нужно вызывать MyServiceConnection(in), так как in не содержит никаких полезных для меня данных (поскольку я ничего в него не записывал). Я мог бы просто вызвать MyServiceConnection() в этом примере. Вы можете просто написать простой пример и посмотреть, как он работает. - person hoomi; 27.06.2014
comment
извините, я почти уверен, что это не сработает. вы потеряете свой экземпляр ServiceConnection и создадите новый после десериализации. Поэтому вы не можете отменить привязку к тому же ServiceConnection, с которым вы связались (потому что вы потеряли первый в процессе отправки). - person Sam; 27.06.2014
comment
Вы абсолютно правы. Когда я запустил свой тест, я не понял, что произошла утечка ServiceConnection. - person hoomi; 27.06.2014
comment
Почитав кое-что об андроиде, я думаю, что у меня есть решение вопроса, которое я опубликую позже. - person Sam; 27.06.2014

Существует гораздо более простой способ справиться с этой ситуацией, называемый IntentService, о котором вы можете узнать подробнее здесь. С сайта андроида:

«Класс IntentService предоставляет простую структуру для запуска операции в одном фоновом потоке. Это позволяет ему обрабатывать длительные операции, не влияя на скорость отклика вашего пользовательского интерфейса. Кроме того, IntentService не зависит от большинства событий жизненного цикла пользовательского интерфейса, поэтому он продолжает работать в обстоятельствах, которые могут закрыть AsyncTask"

Вместо того, чтобы привязывать свою службу к своей активности, вы можете запускать длительные действия в фоновом потоке, просто используя намерение, которое запускает ваш IntentService

public class RSSPullService extends IntentService {

    @Override
    protected void onHandleIntent(Intent workIntent) {
    // Gets data from the incoming Intent
    String dataString = workIntent.getDataString();
    ...
    // Do work here, based on the contents of dataString
    ...
    }
}

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

Преимущество службы намерений заключается в том, что она работает в фоновом потоке и не привязана к жизненному циклу запускаемой активности. Это означает, что изменения конфигурации не должны влиять на выполнение службы.

По завершении службы вы можете сообщить о статусе работы. используя локальную трансляцию - либо отправляя результаты непосредственно обратно в активность (через широковещательный приемник), либо, возможно, даже через onNewIntent() (хотя заставить это работать немного более неуклюже.

Изменить – ответить на вопросы в комментариях

IntentService — относительно небольшой класс. Это упрощает модификацию. Стандартный код для IntentService вызывает stopSelf() и умирает, когда у него заканчивается работа. Это можно легко исправить. Изучив исходный код IntentService (см. предыдущую ссылку), вы можете увидеть, что он в значительной степени уже работает с очередью, получая сообщения в onStart() и затем выполняя их в порядке получения, как описано в комментарии. Переопределение onStart() позволит вам реализовать новую структуру очереди в соответствии с вашими потребностями. Используйте пример кода для обработки входящего сообщения и получения Intent, а затем просто создайте свою собственную структуру данных для обработки приоритета. Вы должны иметь возможность запускать/останавливать свои веб-запросы в IntentService так же, как и в Service. Таким образом, переопределяя onStart() и onHandleIntent(), вы сможете делать то, что хотите.

person Rarw    schedule 27.06.2014
comment
Привет @Rarw, спасибо за ваш ответ, к сожалению, я уже просмотрел IntentService, но он не соответствовал моим требованиям, поскольку мне нужно иметь возможность отменять и ставить в очередь задания по приоритету, и поэтому мне нужна разумно настраиваемая реализация службы: требования что различные компоненты приложения могут делать через него веб-запросы с разным приоритетом. (Таким образом, служба должна поддерживать какую-то очередь и иметь возможность отменять текущие запросы для других с более высоким приоритетом). - person Sam; 27.06.2014
comment
Конкретная проблема заключается в том, что IntentService предназначен для последовательного выполнения задач: рабочие запросы выполняются последовательно. Если в IntentService выполняется операция, и вы отправляете ей другой запрос, запрос ожидает завершения первой операции. Конечно, поправьте меня, если считаете, что я неправильно понял. - person Sam; 27.06.2014
comment
Последнее примечание: я не могу немедленно вернуться из IntentService, а затем публиковать обновления позже, поскольку IntentService останавливается, когда его рабочая очередь пуста: клиенты отправляют запросы через вызовы startService(Intent); служба запускается по мере необходимости, обрабатывает каждое намерение по очереди, используя рабочий поток, и останавливается, когда заканчивается работа. - person Sam; 27.06.2014
comment
отвечу на ваши вопросы выше - person Rarw; 27.06.2014
comment
Спасибо, я также опубликовал свой собственный ответ после того, как немного покопался в источнике Android, чтобы успокоить нервы. - person Sam; 27.06.2014
comment
Хорошо, я вижу, как можно поддерживать работу службы намерений. Поэтому я бы также отправил специальное намерение остановить службу, когда я закончу с ней. Разве я не эффективно воссоздаю связанную службу? - person Sam; 28.06.2014
comment
В вашей активности или фрагменте должен быть метод stopService() так же, как и startService(). Но да, вы можете отправить намерение с флагом остановки и остановить службу. Есть много способов сделать это. - person Rarw; 28.06.2014

У меня была аналогичная проблема, когда у меня была связанная служба, используемая в действии. Внутри действия я определяю ServiceConnection, mConnection, а внутри onServiceConnected я устанавливаю поле класса, syncService которое является ссылкой на Сервис:

private SynchronizerService<Entity> syncService;

(...)

/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {

    @Override
    public void onServiceConnected(ComponentName className, IBinder service) {
        // We've bound to LocalService, cast the IBinder and get
        // LocalService instance
        Log.d(debugTag, "on Service Connected");
        LocalBinder binder = (LocalBinder) service;
        //HERE
        syncService = binder.getService();
        //HERE
        mBound = true;
        onPostConnect();
    }

    @Override
    public void onServiceDisconnected(ComponentName arg0) {
        Log.d(debugTag, "on Service Disconnected");
        syncService = null;
        mBound = false;
    }
};

Используя этот метод, всякий раз, когда менялась ориентация, я получал NullPointerException при ссылке на переменную syncService, несмотря на то, что служба работала, и я пробовал несколько методов, которые никогда не работали.

Я собирался реализовать решение, предложенное Сэмом, используя сохраненный фрагмент для сохранения переменной, но сначала вспомнил о простой вещи: установить для переменной syncService значение static.. и ссылка на соединение сохраняется при изменении ориентации !

Итак, теперь у меня есть

private static SynchronizerService<Entity> syncService = null;

...

/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {

    @Override
    public void onServiceConnected(ComponentName className, IBinder service) {
        // We've bound to LocalService, cast the IBinder and get
        // LocalService instance
        Log.d(debugTag, "on Service Connected");
        LocalBinder binder = (LocalBinder) service;
        //HERE
        if(syncService == null) {
            Log.d(debugTag, "Initializing service connection");
            syncService = binder.getService();
        }
        //HERE
        mBound = true;
        onPostConnect();
    }

    @Override
    public void onServiceDisconnected(ComponentName arg0) {
        Log.d(debugTag, "on Service Disconnected");
        syncService = null;
        mBound = false;
    }
};
person miguel_rdp    schedule 07.07.2014
comment
Я не думаю, что установка статической переменной, фактически делающая ее глобальной, является стандартным или рекомендуемым способом поддерживать любую переменную в действии. что, если я повторно использую активность? или что, если он будет уничтожен по другим причинам? - person Sam; 07.07.2014
comment
@Sam Я не понимаю, почему бы и нет .. Если бы это была асинхронная задача, я бы согласился, но если служба всегда работает, а ее жизненный цикл хорошо известен и контролируется, почему бы не сохранить частную статическую переменную или даже глобальный, вне активности, если на то пошло, если кто-то хочет его повторно использовать? Конечно, это менее затратно, чем безголовый фрагмент, хотя я согласен, что ваше решение, возможно, более элегантно. - person miguel_rdp; 08.07.2014
comment
Потому что связанная служба не всегда работает. Судя по POV действия, вы можете гарантировать, что оно существует только до тех пор, пока это действие (за исключением изменений конфигурации, теперь у нас есть наше исправление), поэтому вам лучше очистить его, когда вы будете уничтожены должным образом. Поэтому делать его доступным для всех остальных бессмысленно, поскольку вы можете просто умереть, отключить соединение и убить службу. Если вы имеете в виду, что объекты модели могут использовать его - они могут просто взять свою собственную ссылку и использовать связанную активность по назначению - где каждый компонент приложения индивидуально привязывается к нему, чтобы поддерживать его в своих целях. - person Sam; 08.07.2014
comment
Хотя использование частной статики, вероятно, будет работать для большинства целей, я думаю, что есть определенное утверждение, что в каждый момент времени будет только один экземпляр каждой активности. Помните, что фоновая активность только останавливается (или, если она видна, она приостанавливается). Если вы решили отключиться от службы только в onDestroy, у вас может быть два экземпляра вашей активности в разных частях их жизненного цикла, корректирующих и изменяющих статическую переменную. По сути, это выглядит просто, но скрывает проблемы и может быть легко сделано неправильно. Почему существует onSaveInstanceState, если со статикой все в порядке? - person Sam; 08.07.2014
comment
@ Сэм, я понимаю, что ты говоришь. Я думаю, это зависит от архитектуры. В случае с моим приложением это больше, чем состояние... служба всегда должна быть доступна, как, например, HTTP-клиент в пуле или кеш изображений для всего приложения. Что касается проблемы параллелизма, о которой вы говорите, 2 действия, изменяющие статическую переменную, я думаю, что ваше решение также может иметь ту же проблему, если 2 действия пытаются манипулировать переменной внутри сохраненного фрагмента, нет? - person miguel_rdp; 08.07.2014
comment
Нет, все будет хорошо, так как сохраненный фрагмент относится к «экземпляру» действия. (где экземпляр означает действие, поскольку оно живет при изменениях конфигурации). Для меня это звучит так, как будто вам не следует использовать связанный сервис, если он вам всегда нужен. - person Sam; 08.07.2014
comment
Я использую связанную службу, потому что с ней проще общаться и напрямую вызывать методы и т. Д. О, я не знал, что ваш сохраненный фрагмент был для каждого экземпляра. Если у меня возникнут проблемы с использованием статики или одноэлементного шаблона, я обязательно реализую ваше решение! - person miguel_rdp; 08.07.2014
comment
да, обязательно взгляните на это: developer.android.com /руководство/темы/ресурсы/ - person Sam; 08.07.2014