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


Я знакомлюсь с Android framework и 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 ()". Например, когда загрузка заканчивается, функция public void " handleWebpage (String data)" в activity вызывается с загруженной строкой в качестве ее параметра.

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

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

6 54

6 ответов:

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

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

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);
    }

    //...

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

public interface Command{
    void execute();
}

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

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

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

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

нет интерфейса, нет 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).

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

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

 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 */
   }
});

отражение никогда не является хорошей идеей, так как его труднее читать и отлаживать, но если вы на 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);
}