Шаблон проектирования наблюдателя слушателей с обработчиком

Недавно я имел дело с вопросом, на который я не знаю, как ответить. Я написал пример кода для некоторой AsyncTask, которую хочу выполнить. Я где-то читал в сети, что кто-то реализовал AsyncTask и Handler как внутренние классы, и я хотел немного масштабировать это и сделать меньше связи, поэтому я сделал отдельный класс для них, чтобы я мог повторно использовать их с более чем одним действием. Поскольку мне нужно было делать разные вещи с пользовательским интерфейсом для каждого действия, я решил реализовать интерфейс для этих действий, чтобы я мог реагировать на каждое событие одними и теми же методами.

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

Вот мой пример кода:

Действие 1:

public class SomeActivity extends Activity implements MyListener{

    MyAsyncTask myTask;
    MyHandler handler;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handler = new MyHandler();
        myTask = new MyAsyncTask(handler);
        // initilize the activity views etc...
    }

    @Override
    public void do1(){
        // DO UI THINGS FOR ACTIVITY 1 IN A CALLBACK TO DO1 EVENT
    }

    @Override
    public void do2(){
        // DO UI THINGS FOR ACTIVITY 1 IN A CALLBACK TO DO2 EVENT
    }
}

Действие 2:

public class OtherActivity extends Activity implements MyListener{

    MyAsyncTask myTask;
    MyHandler handler;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handler = new MyHandler();
        myTask = new MyAsyncTask(handler);
        // initilize the activity views etc...
    }

    @Override
    public void do1(){
        // DO UI THINGS FOR ACTIVITY 2 IN A CALLBACK TO DO1 EVENT
    }

    @Override
    public void do2(){
        // DO UI THINGS FOR ACTIVITY 2 IN A CALLBACK TO DO2 EVENT
    }
}

Интерфейс слушателя:

public interface MyListener{
    void do1();
    void do2();
}

Реализация асинхронной задачи:

public class MyAsyncTask extends AsyncTask<Void,Void,String>{
    private MyModel m;

    public MyAsyncTask(Handler h){
        m = new MyModel();
        m.setHandler(h);
    }

    protected String doInBackground(Void... params) {
        // do something in background with MyModel m
        return null;            
    }
}

Реализация обработчика:

public class MyHandler extends Handler {

    Vector<MyListener> listeners = new Vector<>();

    @Override
    public void handleMessage(Message msg) {
        switch(msg.what){
            case 1:
                // do something for case 1
                fireMethod1();
                break;
            case 2:
                // do something for case 2
                fireMethod2();
                break;
        } 
    }

    public void registerListener(MyListener l){
        listeners.add(l);
    }

    public void unregisterListener(MyListener l){
        listeners.remove(l);
    }

    private void fireMethod1(){
        for(MyListener l : listeners){
            l.do1();
        }
    }

    private void fireMethod2(){
        for(MyListener l : listeners){
            l.do2();
        }
    }

}

Некоторая демонстрационная модель, которую я создал:

public class MyModel{

    private Handel h;

    public MyModel(){
        // at some point send message 1 or message 2 ...
    }

    public void setHandler(Handler h){
        this.h = h;
    }

    private void sendMessage1(){
        h.obtainMessage(1, null);
    }

    private void sendMessage2(){
        h.obtainMessage(2, null);
    }
}

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


person Dima Gimburg    schedule 20.06.2016    source источник


Ответы (2)


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

Разница в том, что когда вы используете прослушиватель, вы вызываете метод синхронно в том же потоке. Когда вы используете Handler, вы синхронно добавляете сообщение в MessageQueue, но оно обрабатывается только после тех сообщений, которые уже находятся в очереди.

Например, если вы используете обработчик пользовательского интерфейса и уже вызвали finish() для действия, а затем добавили свое сообщение, оно будет вставлено после onStop() и onDestroy(). Вы не можете добиться этого со слушателем.

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

это довольно разные решения для одной и той же проблемы?

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

person Gennadii Saprykin    schedule 20.06.2016
comment
Как я могу с одним и тем же расширенным классом обработчика выполнять какие-либо действия для разных действий? Правильно ли я делаю это с наблюдаемым шаблоном? Я просто хочу помешать себе создать дублированный метод getHandler, который создает экземпляр нового обработчика внутри каждого действия, которое я хочу обрабатывать сообщениями, поступающими от обработчика? Еще раз спасибо - person Dima Gimburg; 20.06.2016
comment
Вам действительно нужен один и тот же Handler объект для разных действий? Обычно можно создать обработчик пользовательского интерфейса в каждом действии, это не сложно. Если вам нужен тот же объект, вы можете использовать статическую переменную в классе приложения или, возможно, базовую активность. Это безопасно, потому что обработчик будет связан с потоком пользовательского интерфейса. Что касается вашего следующего вопроса, я не знаю, правильно ли вы это делаете, потому что это всегда зависит от варианта использования. Сколько длится фоновая работа? Вам нужно обновить интерфейс? Каково ожидаемое поведение, если система решит убить ваше приложение? - person Gennadii Saprykin; 20.06.2016
comment
Отлично, кажется, я тебя понял. Фоновая операция обрабатывается пользователем, это музыкальный проигрыватель, такой как (MyModel в примере кода), и пользователь выбирает, воспроизводить его или останавливать, и когда он воспроизводится, мне нужно получать определенные сообщения. И он умирает и уничтожается с помощью onStop. Каждый раз, когда я получаю сообщение, мне нужно обновить пользовательский интерфейс с помощью рисунка на холсте. Может быть, не так сложно создать обработчик в каждом действии, и я рассмотрю этот подход. - person Dima Gimburg; 20.06.2016
comment
В порядке. Для таких приложений, как музыкальный проигрыватель, обычно используется компонент Service. Это позволяет вам выполнять операцию независимо от пользовательского интерфейса, а также повышает приоритет процесса приложения, чтобы у него было больше шансов выжить, когда устройство работает с нехваткой памяти. Рассмотрите это решение, возможно, оно лучше подойдет для вашего варианта использования. Спасибо. - person Gennadii Saprykin; 20.06.2016
comment
Хорошо, я попробую запустить его как сервис, на самом деле это проигрыватель метронома (поэтому я использую AudioTrack), поэтому, когда я запускаю его как сервис, я все равно могу использовать обработчики в качестве платформы обмена сообщениями между сервисом и пользовательским интерфейсом. и воспользоваться очередью сообщений? поскольку это метроном, мне действительно нужно позаботиться о синхронности, и очередь, которую я получаю от обработчика, отлично подходит для этого. - person Dima Gimburg; 20.06.2016
comment
Да, сервисы хороши в связке с обработчиками. - person Gennadii Saprykin; 20.06.2016

Handler — это компонент с потоком пользовательского интерфейса. Использование простого слушателя может вызвать CalledFromWrongThreadException, если вы хотите коснуться какого-либо пользовательского интерфейса.

AsyncTask, хотя и имеет onPreExecute, onPostExecute и onProgressUpdate, которые являются просто методами, которые выполняются в потоке пользовательского интерфейса. doInBackground работает в отдельном потоке

person Petrov Dmitrii    schedule 20.06.2016
comment
Handler не является компонентом с потоком пользовательского интерфейса. Если вы создадите объект Handler, он будет использовать поток, в котором вы сейчас находитесь, то есть, если вы создадите Handler в фоновом потоке, он будет использовать фоновый поток. - person Gennadii Saprykin; 20.06.2016
comment
Дополнительная информация: developer.android.com/reference/android/os/Handler.html. - person Gennadii Saprykin; 20.06.2016