Передача функции в качестве параметра в java

Я познакомился с платформой Android и Java и хотел создать общий класс "NetworkHelper", который обрабатывал бы большинство сетевых кодов, позволяющих мне просто вызывать веб-страницы из него.

Я выполнил эту статью с сайта developer.android.com, чтобы создать свой сетевой класс: http://developer.android.com/training/basics/network-ops/connecting.html

код:

package com.example.androidapp;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.util.Log;



/**
 * @author tuomas
 * This class provides basic helper functions and features for network communication.
 */


public class NetworkHelper 
{
private Context mContext;


public NetworkHelper(Context mContext)
{
    //get context
    this.mContext = mContext;
}


/**
 * Checks if the network connection is available.
 */
public boolean checkConnection()
{
    //checks if the network connection exists and works as should be
    ConnectivityManager connMgr = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();

    if (networkInfo != null && networkInfo.isConnected())
    {
        //network connection works
        Log.v("log", "Network connection works");
        return true;
    }
    else
    {
        //network connection won't work
        Log.v("log", "Network connection won't work");
        return false;
    }

}

public void downloadUrl(String stringUrl)
{
    new DownloadWebpageTask().execute(stringUrl);

}



//actual code to handle download
private class DownloadWebpageTask extends AsyncTask<String, Void, String>
{



    @Override
    protected String doInBackground(String... urls)
    {
        // params comes from the execute() call: params[0] is the url.
        try {
            return downloadUrl(urls[0]);
        } catch (IOException e) {
            return "Unable to retrieve web page. URL may be invalid.";
        }
    }

    // Given a URL, establishes an HttpUrlConnection and retrieves
    // the web page content as a InputStream, which it returns as
    // a string.
    private String downloadUrl(String myurl) throws IOException 
    {
        InputStream is = null;
        // Only display the first 500 characters of the retrieved
        // web page content.
        int len = 500;

        try {
            URL url = new URL(myurl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(10000 );
            conn.setConnectTimeout(15000);
            conn.setRequestMethod("GET");
            conn.setDoInput(true);
            // Starts the query
            conn.connect();
            int response = conn.getResponseCode();
            Log.d("log", "The response is: " + response);
            is = conn.getInputStream();

            // Convert the InputStream into a string
            String contentAsString = readIt(is, len);
            return contentAsString;

        // Makes sure that the InputStream is closed after the app is
        // finished using it.
        } finally {
            if (is != null) {
                is.close();
            } 
        }
    }

    // Reads an InputStream and converts it to a String.
    public String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException 
    {
        Reader reader = null;
        reader = new InputStreamReader(stream, "UTF-8");        
        char[] buffer = new char[len];
        reader.read(buffer);
        return new String(buffer);
    }


    // onPostExecute displays the results of the AsyncTask.
    @Override
    protected void onPostExecute(String result) 
    {
        //textView.setText(result);
        Log.v("log", result);

    }

} 

}

В моем классе активности я использую класс следующим образом:

connHelper = new NetworkHelper(this);

...

if (connHelper.checkConnection())
    {
        //connection ok, download the webpage from provided url
        connHelper.downloadUrl(stringUrl);
    }

Проблема, с которой я сталкиваюсь, заключается в том, что я должен каким-то образом вернуть обратный вызов к активности, и она должна быть определена в функции "downloadUrl()". Например, когда загрузка заканчивается, функция publicoid "handleWebpage (String data)" в активности вызывается с загруженной строкой в ​​качестве ее параметра.

Я сделал несколько поисковых запросов и обнаружил, что мне нужно каким-то образом использовать интерфейсы для достижения этой функциональности. После рассмотрения нескольких похожих вопросов/ответов stackoverflow я не получил работу, и я не уверен, правильно ли я понял интерфейсы: Как передать метод в качестве параметра в Java? Честно говоря, анонимные классы для меня новы, и я не совсем уверен, где и как я должен применять фрагменты кода примера в упомянутом потоке.

Итак, мой вопрос заключается в том, как передать функцию обратного вызова в мой сетевой класс и вызвать его после завершения загрузки? Где декларация интерфейса идет, реализует ключевое слово и так далее? Обратите внимание, что я начинаю с Java (у меня есть другой фон программирования), поэтому я буду благодарен за объяснение:) Спасибо!

Ответ 1

Используйте интерфейс обратного вызова или абстрактный класс с абстрактными методами обратного вызова.

Пример интерфейса обратного вызова:

public class SampleActivity extends Activity {

    //define callback interface
    interface MyCallbackInterface {

        void onDownloadFinished(String result);
    }

    //your method slightly modified to take callback into account 
    public void downloadUrl(String stringUrl, MyCallbackInterface callback) {
        new DownloadWebpageTask(callback).execute(stringUrl);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //example to modified downloadUrl method
        downloadUrl("http://google.com", new MyCallbackInterface() {

            @Override
            public void onDownloadFinished(String result) {
                // Do something when download finished
            }
        });
    }

    //your async task class
    private class DownloadWebpageTask extends AsyncTask<String, Void, String> {

        final MyCallbackInterface callback;

        DownloadWebpageTask(MyCallbackInterface callback) {
            this.callback = callback;
        }

        @Override
        protected void onPostExecute(String result) {
            callback.onDownloadFinished(result);
        }

        //except for this leave your code for this class untouched...
    }
}

Второй вариант еще более краткий. Вам даже не нужно определять абстрактный метод для "onDownloaded event", поскольку onPostExecute выполняет именно то, что необходимо. Просто добавьте DownloadWebpageTask к анонимному встроенному классу внутри вашего метода downloadUrl.

    //your method slightly modified to take callback into account 
    public void downloadUrl(String stringUrl, final MyCallbackInterface callback) {
        new DownloadWebpageTask() {

            @Override
            protected void onPostExecute(String result) {
                super.onPostExecute(result);
                callback.onDownloadFinished(result);
            }
        }.execute(stringUrl);
    }

    //...

Ответ 2

Да, интерфейс - лучший способ ИМХО. Например, GWT использует шаблон команды с таким интерфейсом:

public interface Command{
    void execute();
}

Таким образом, вы можете передавать функцию из метода в другой

public void foo(Command cmd){
  ...
  cmd.execute();
}

public void bar(){
  foo(new Command(){
     void execute(){
        //do something
     }
  });
}

Ответ 3

Исходное решение состоит в том, что это невозможно в Java. Java не принимает Функции более высокого порядка. Это может быть достигнуто хотя бы некоторыми "трюками". Обычно интерфейс используется так, как вы видели. Пожалуйста, посмотрите здесь для получения дополнительной информации. Вы также можете использовать отражение для его достижения, но это склонно к ошибкам.

Ответ 4

Использование интерфейсов может быть лучшим в архитектуре Java Coding.

Но прохождение объекта Runnable также может работать, и было бы гораздо более практичным и гибким, я думаю.

 SomeProcess sp;

 public void initSomeProcess(Runnable callbackProcessOnFailed) {
     final Runnable runOnFailed = callbackProcessOnFailed; 
     sp = new SomeProcess();
     sp.settingSomeVars = someVars;
     sp.setProcessListener = new SomeProcessListener() {
          public void OnDone() {
             Log.d(TAG,"done");
          }
          public void OnFailed(){
             Log.d(TAG,"failed");
             //call callback if it is set
             if (runOnFailed!=null) {
               Handler h = new Handler();
               h.post(runOnFailed);
             }
          }               
     };
}

/****/

initSomeProcess(new Runnable() {
   @Override
   public void run() {
       /* callback routines here */
   }
});

Ответ 5

Отражение никогда не является хорошей идеей, так как сложнее читать и отлаживать, но если вы на 100% уверены, что делаете, вы можете просто называть что-то вроде set_method (R.id.button_profile_edit, "toggle_edit" ) для присоединения метод для представления. Это полезно в фрагменте, но, опять же, некоторые люди будут рассматривать его как анти-шаблон, поэтому его следует предупредить.

public void set_method(int id, final String a_method)
{
    set_listener(id, new View.OnClickListener() {
        public void onClick(View v) {
            try {
                Method method = fragment.getClass().getMethod(a_method, null);
                method.invoke(fragment, null);
            } catch (Exception e) {
                Debug.log_exception(e, "METHOD");
            }
        }
    });
}
public void set_listener(int id, View.OnClickListener listener)
{
    if (root == null) {
        Debug.log("WARNING fragment", "root is null - listener not set");
        return;
    }
    View view = root.findViewById(id);
    view.setOnClickListener(listener);
}

Ответ 6

НЕТ интерфейс, НЕТ lib, НЕТ Java 8 необходимо!

Просто используя Callable<V> от java.util.concurrent

public static void superMethod(String simpleParam, Callable<Void> methodParam) {

    //your logic code [...]

    //call methodParam
    try {
        methodParam.call();

    } catch (Exception e) {
        e.printStackTrace();
    }
}

Как его использовать:

 superMethod("Hello world", new Callable<Void>() {
                public Void call() {
                    myParamMethod();
                    return null;
                }
            }
    );

Где myParamMethod() - наш переданный метод как параметр (в данном случае methodParam).