Как или почему работают интерфейсы прослушивателя? И есть ли у интерфейсов какое-либо другое применение, кроме как слушателя?

Всякий раз, когда мы хотим создать слушателя, мы реализуем интерфейс слушателя. Например, давайте реализуем SensorEventListener.

Теперь нам нужно переопределить методы этого интерфейса слушателя.

public void onSensorChanged(SensorEvent event);

а также

public void onAccuracyChanged(Sensor sensor, int accuracy);

Чего я не понимаю:

  1. Почему и как работают эти методы, когда я их автоматически использую?
  2. Почему метод onAccuracyChanged вызывается при изменении точности?
  3. В конце концов, onAccuracyChanged — это просто пустой метод, который мы переопределяем, поскольку этого требует наша формула (или интерфейс, который мы реализуем). Если это что-то волшебное, вызванное нижними уровнями
  4. Когда и зачем кому-то на самом деле использовать интерфейс в своем собственном проекте независимо от Android?

person Huzo    schedule 01.12.2018    source источник


Ответы (4)


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

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

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

Прослушиватель — это тот интерфейс, который слышит и уведомляет об обратном вызове.

Как же все это можно использовать? И как все это взаимодействует?

  • Сначала создайте интерфейс с пустыми методами тела, которые вы собираетесь вызывать при возникновении события:
public interface MyListener{

      void actionOneHappens(Object o);
      void actionTwo();
      void actionThree();

}
  • Создайте класс, который что-то обрабатывает, например, считает:
public class MyCounter{
//create a member of type MyListener if you intend to exchange infos

private MyListener myListener;

//let's create a setter for our listener
public void setMyListener(MyListener listener)
{
this.myListener=listener;
}

  MyCounter(){

  }
//this method will help us count
public void startCounting()
{
  new CountDownTimer(10000,1000)
       {

           @Override
           public void onTick(long millisUntilFinished) {

            //I want to notify at third second after counter launched

            if(millisUntilFinished/1000==3)
            {
              // I notify if true :
              //as someone can forget to set the listener let's test if it's not //null
              if(myListener!=null){
                 myListener.actionThree();
              }


            }

           }

           @Override
           public void onFinish() {

           }
       }.start();
}




}
  • Затем вы можете создать объект типа MyCounter и узнать, когда он равен трем:

MyCounter myCounter=new MyCounter();

myCounter.setMyListener(new MyListener()
{
//then override methods here
  @override
  void actionOneHappens(Object o){
  }
  @override
  void actionTwo()
  {}

  @override
  void actionThree()
  {
   //Add you code here
   Toast.makeText(getApplicationContext(),"I'm at 3",Toast.LENGTH_LONG).show()
   }



});

//start your counter
myCounter.startCounting();

И это сделано!! Вот как мы поступаем.

person Gratien Asimbahwe    schedule 01.12.2018

Вот подходящий ответ. Позвольте мне привести вам пример о слушателях.

Слушатели:

Предположим, что есть класс, который извлекает данные в фоновом режиме, Worker, и другой класс, который заинтересован в этих данных, InterestedClass.

public class Worker extends Thread{
  interface DataFetchedListener{
    void onDataFetched(String data);
  }

  private DataFetchedListener listener;

  @Override
  public void run(){
   String data = fetchData();
   // Data fetched inform your listener so he can take action
   listener.onDataFetched(data);
  }

  public void setDataFetchedListener(DataFetchedListener listener){
   this.listener = listener;
  }

  private String fetchData(){
    // returns the fetched data after some operations
    return "Data";
  }
}


public class InterestedClass implements Worker.DatafetchedListener{

 @Override
 public void onDataFetched(String data){
  doSomethingWith(data);
 }

 private doSomethingWith(String data){
  // just print it in the console
  System.out.println("Data fetched is -> " + data);
 }

}

Worker все равно, какой класс будет манипулировать его данными, пока этот класс следует контракту DataFetchedListener.

В равной степени это означает, что любой класс может что-то делать с данными (InterestedClass просто выводит это в консоль), но Worker не нужно знать, что это за класс, просто он реализует свои интерфейс.

Главное может быть так...

public class Application{
  public static void main(String[] args){
   InterestedClass interested = new InterestedClass();
   Worker worker = new Worker();
   worker.setDataFetchedListener(intereseted);
   worker.start(); // Starts Worker's thread
  }
} 

Когда Worker получит данные, он уведомит своего слушателя (в настоящее время объект interested), и слушатель будет действовать соответствующим образом (interested выведет данные на консоль).

person Themelis    schedule 01.12.2018
comment
Понятно, но одно мне непонятно, ваш интерфейс был каким-то внутренним классом Worker. Но в Android слушатели сами по себе являются интерфейсами. Так что эта часть меня смутила. - person Huzo; 01.12.2018
comment
О... Вы можете определить интерфейс где угодно. Я просто определил его внутри Worker для согласованности. Пока интерфейс виден Worker и классу, который его реализует, проблем нет! - person Themelis; 01.12.2018

Интерфейсы не имеют реализации и для их использования у нас есть два варианта:

  1. Класс, который их реализует
  2. Анонимный класс

И рассмотрим этот код:

interface TestInterface {
    void doSomething();
}

class TestClass{
    private TestInterface ti;
    public TestClass(TestInterface ti){
        this.ti = ti;
    }

    public void testActionMethod(){
        ti.doSomething();
        //some other codes
    }
}

class OurOwnLauncherApp{
    public static void main(String[] args) {
        TestClass tc = new TestClass(new TestInterface() {
            @Override
            public void doSomething() {
                System.out.println("Hi!");
            }
        });

        tc.testActionMethod();

        TestClass tc2 = new TestClass(new TestInterface() {
            @Override
            public void doSomething() {
                System.out.println("Bye!");
            }
        });

        tc2.testActionMethod();
    }
}

Здесь у нас есть:

  1. Интерфейс (как и то, что вы просили)
  2. Функциональный класс использует этот интерфейс
  3. Приложение где-то, о чем мы не знаем (может быть, ваше приложение для телефона, может быть, приложение для телефона ваших друзей и т. д.)

Что делает этот код, он предоставляет анонимный класс (который реализует TestInterface) для testActionMethod и с вызовом метода doSomething внутри testActionMethod, мы инвертируем вызов нашего собственного метода. поэтому вы увидите такой результат:

Hi!

До свидания!

Это точная упрощенная версия интерфейсов слушателей и того, как они работают

person Mehdi    schedule 01.12.2018

Нет ничего волшебного. Как правило, механизм прослушивания событий выглядит следующим образом:

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

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

1- Прослушиватель реализует интерфейс 2- Прослушиватель регистрируется как прослушиватель кнопки (Генератор событий) 3- Генератор событий вызывает соответствующий метод всех зарегистрированных слушателей (этот метод является методом интерфейса).

Для вашего случая андроид предоставляет менеджер, с помощью которого вы можете зарегистрировать слушателя на некоторых датчиках: android.hardware.SensorManager.registerListener(). Все происходит здесь (что не волшебство!). Когда вы регистрируете объект (который реализует соответствующий интерфейс, SensorEventListener) в качестве прослушивателя датчика, изменения в этом датчике вызовут методы прослушивателя).

person hadi.mansouri    schedule 01.12.2018
comment
Но я не понимаю, каковы отношения этих генераторов со слушателями. Определено ли в SensorManager, что при изменении точности SensorManager ожидает, что в вызывающем классе будет существовать метод onAccuracyChanged? - person Huzo; 01.12.2018