Android: ссылка на функцию передачи AsyncTask

Я новичок в android и очень привык к веб-разработке. в javascript, когда вы хотите выполнить асинхронную задачу, вы передаете функцию в качестве аргумента (обратный вызов):

http.get('www.example.com' , function(response){
   //some code to handle response
});

мне было интересно, можем ли мы сделать то же самое с android AsyncTask , передать ссылку на функцию onPostExecute() метод , и он будет работать.

какие предложения ?

3 ответов


ДА, концепция обратных вызовов также очень существует в Java. В Java вы определяете обратный вызов следующим образом:

public interface TaskListener {
    public void onFinished(String result);
}

часто можно было бы вложить такие определения слушателей в AsyncTask такой:

public class ExampleTask extends AsyncTask<Void, Void, String> {

    public interface TaskListener {
        public void onFinished(String result);
    }

    ...
}

и полная реализация обратного вызова в AsyncTask будет выглядеть так:

public class ExampleTask extends AsyncTask<Void, Void, String> {

    public interface TaskListener {
        public void onFinished(String result);
    }

    // This is the reference to the associated listener
    private final TaskListener taskListener;

    public ExampleTask(TaskListener listener) {
        // The listener reference is passed in through the constructor
        this.taskListener = listener;
    }

    @Override
    protected String doInBackground(Void... params) {
        return doSomething();
    }

    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);

        // In onPostExecute we check if the listener is valid
        if(this.taskListener != null) {

            // And if it is we call the callback function on it.
            this.taskListener.onFinished(result);
        }
    }
}

onPostExecute() вызывается, как только фоновая задача завершается. Вы можете использовать все это, как это:

ExampleTask task = new ExampleTask(new ExampleTask.TaskListener() {
    @Override
    public void onFinished(String result) {
        // Do Something after the task has finished
    }
});

task.execute();

или вы можете определить TaskListener совершенно отдельно такой:

ExampleTask.TaskListener listener = new ExampleTask.TaskListener() {
    @Override
    public void onFinished(String result) {
        // Do Something after the task has finished
    }
};

ExampleTask task = new ExampleTask(listener);    
task.execute();

или вы можете подкласс TaskListener такой:

public class ExampleTaskListener implements TaskListener {

    @Override
    public void onFinished(String result) {

    }
}

а затем используйте его так:

ExampleTask task = new ExampleTask(new ExampleTaskListener());    
task.execute();

вы можете, конечно, просто переопределить onPostExecute() метод AsyncTask, но это не рекомендуется и в большинстве случаев на самом деле очень плохая практика. Например, вы можете сделать следующее:

ExampleTask task = new ExampleTask() {
    @Override
    public void onPostExecute(String result) {
        super.onPostExecute(result);

        // Your code goes here
    }
};

это будет работать так же хорошо как реализация выше с отдельным интерфейсом прослушивателя, но есть несколько проблем с этим:

в первую очередь вы можете фактически сломать ExampleTask все вместе. Все сводится к super.onPostExecute() выше вызова. Если вы как разработчик переопределите onPostExecute() как и выше, и забудьте включить супер вызов или просто удалить его по какой-либо причине, что оригинал onPostExecute() метод ExampleTask больше не будет называться. Например, вся реализация listener с TaskListener внезапно не будет работать больше, так как вызов обратного вызова реализован в onPostExecute(). Вы также можете сломать TaskListener многими другими способами, неосознанно или невольно влияя на состояние ExampleTask так что это больше не будет работать.

если вы посмотрите на то, что на самом деле происходит, когда вы переопределяете такой метод, чем становится гораздо более ясно, что происходит. Путем переопределения onPostExecute() вы создаете новый подкласс ExampleTask. Было бы точно то же самое, что делать это:

public class AnotherExampleTask extends ExampleTask {

    @Override
    public void onPostExecute(String result) {
        super.onPostExecute(result);

        // Your code goes here
    }
}

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

в итоге:

  • переопределение такого метода фактически создает новый подкласс. Вы не просто добавляете обратный вызов, вы изменяете, как работает этот класс, и можете неосознанно сломать так много вещей.
  • отладка такие ошибки могут быть намного больше, чем просто боль в a**. Потому что вдруг ExampleTask могу кинуть Exceptions или просто больше не работает без видимых причин, потому что вы никогда не изменяли его код.
  • каждый класс должен предоставлять реализации прослушивателя в местах, где это уместно и предназначено. Конечно, вы можете просто добавить их позже, переопределив super.onPostExecute() позвоните в его ответ, представьте, что может сделать какой-то другой не столь опытный разработчик!
  • немного абстракции никому не повредит. Написание конкретных слушателей может быть немного больше кода, но это гораздо лучшее решение. Код будет более чистым, более читаемым и намного более доступным для обслуживания. Использование ярлыков, таких как overriding onPostExecute() по существу жертвует качеством кода для немного удобства. Это никогда не хорошая идея, и это просто вызовет проблемы в долгосрочной перспективе бежать.

в Java функции меньше являются гражданами первого класса, чем в JavaScript. AsyncTask предоставляет обратный вызов как метод в классе, который необходимо переопределить.

посмотреть сделайте HTTP-запрос с android для подкласса AsyncTask с реализацией doInBackground, который делает веб-запрос.

Если вы хотите выполнить несколько HTTP-запросов с разными обратными вызовами, вы можете переопределить RequestTask и реализовать onPostExecute с помощью различные реализации обратного вызова. Вы можете использовать анонимный класс для имитации закрытия, которое обычно использует обратный вызов JavaScript:

new RequestTask(){
    @Override
    public void onPostExecute(String result) {
        // Implementation has read only access to 
        // final variables in calling scope. 
    }
}.execute("http://stackoverflow.com");

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


в Котлин

во-первых, создайте класс AsyncTaskHelper, как показано ниже.

class AsyncTaskHelper() : AsyncTask<Callable<Void>, Void, Boolean>() {

    var taskListener: AsyncListener? = null

    override fun doInBackground(vararg params: Callable<Void>?): Boolean {
        params.forEach {
            it?.call()
        }
        return true
    }

    override fun onPreExecute() {
        super.onPreExecute()
    }

    override fun onPostExecute(result: Boolean?) {
        super.onPostExecute(result)
        taskListener?.onFinished(result as Any)
    }

}

interface AsyncListener {
    fun onFinished(obj: Any)
}

код ниже вы можете использовать, когда вы хотите использовать асинхронную задачу.

    AsyncTaskHelper().let {
        it.execute(Callable<Void> {
                //this area is the process do you want to do it in background
                // dosomething()
                }
            }
            null
        })
        it.taskListener = object : AsyncListener{
            override fun onFinished(obj: Any) {
                // this area is for the process will want do after finish dosomething() from Callable<Void> callback


            }

        }

из кода выше. если вы хотите разделить свой процесс на несколько задач. вы можете сделать, как этот код ниже.

AsyncTaskHelper().let {
            it.execute(Callable<Void> {
                // task 1 do in background
                null
            },Callable<Void>{
                // task 2 do in background
                null
            },Callable<Void>{
                // task 3 do in background
                null
            })

            it.taskListener = object : AsyncListener {
                override fun onFinished(obj: Any) {
                    // when task1,task2,task3 has been finished . it will do in this area
                }

            }
        }