Przekazywanie funkcji jako parametru w Javie

Zapoznaję się z Android framework i Java i chciałem stworzyć ogólną klasę "NetworkHelper", która obsłużyłaby większość kodu sieciowego, umożliwiając mi po prostu wywoływanie stron internetowych z niego.

Śledziłem ten artykuł z developer.android.com aby utworzyć moją klasę networkingu: http://developer.android.com/training/basics/network-ops/connecting.html

Kod:

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

    }

} 

}

W mojej klasie activity używam klasy this sposób:

connHelper = new NetworkHelper(this);

...

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

Problem, który mam polega na tym, że powinienem jakoś wykonać oddzwanianie do aktywności i powinno być definiowalne w funkcji " downloadUrl ()". Na przykład po zakończeniu pobierania zostanie wywołana funkcja public void "handleWebpage(String data)" w aktywności z załadowanym łańcuchem jako parametrem.

Poszperałem trochę w googlach i stwierdziłem, że powinienem jakoś użyć interfejsów, aby osiągnąć tę funkcjonalność. Po zapoznaniu się z kilkoma podobnymi pytaniami/odpowiedziami stoskoverflow I nie działa i nie jestem pewien, czy dobrze zrozumiałem interfejsy: Jak przekazać metodę jako parametr w Javie? szczerze mówiąc używanie anonimowych klas jest dla mnie nowością i nie jestem do końca pewien, gdzie i jak powinienem zastosować przykładowe fragmenty kodu we wspomnianym wątku.

Więc moje pytanie brzmi, Jak mogę przekazać funkcję zwrotną do mojej klasy sieci i wywołać ją po zakończeniu pobierania? Gdzie idzie deklaracja interfejsu, implementuje słowo kluczowe i tak dalej? Uwaga że jestem początkujący z Java (mają inne podstawy programowania choć), więc byłbym wdzięczny za całym Wyjaśnienie :) Dziękuję!

Author: Community, 2013-05-29

6 answers

Użyj interfejsu zwrotnego lub abstrakcyjnej klasy z abstrakcyjnymi metodami zwrotnymi.

Przykład interfejsu zwrotnego:

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...
    }
}

Druga opcja jest jeszcze bardziej zwięzła. Nie musisz nawet definiować abstrakcyjnej metody dla "zdarzenia onDownloaded", ponieważ onPostExecute robi dokładnie to, co jest potrzebne. Po prostu rozszerz swoją DownloadWebpageTask z anonimową klasą inline wewnątrz metody 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);
    }

    //...
 88
Author: Tomasz Gawel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-29 07:20:14

Tak, Interfejs jest najlepszy IMHO. Na przykład, GWT używa wzorca poleceń z interfejsem takim jak:

public interface Command{
    void execute();
}

W ten sposób możesz przekazać funkcję z metody do innej

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

public void bar(){
  foo(new Command(){
     void execute(){
        //do something
     }
  });
}
 20
Author: Arnaud Denoyelle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-28 20:20:03

Wyjęte z pudełka rozwiązanie polega na tym, że nie jest to możliwe w Javie. Java nie akceptuje funkcji wyższego rzędu . Można to osiągnąć za pomocą pewnych "sztuczek". Zwykle interfejs jest używany tak, jak widziałeś. Proszę spojrzeć tutaj aby uzyskać więcej informacji. Możesz również użyć odbicia, aby to osiągnąć, ale jest to podatne na błędy.

 10
Author: olorin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-28 20:27:31

Brak interfejsu, brak lib, brak Java 8 potrzebne!

Wystarczy użyć Callable<V> z 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();
    }
}

Jak go używać:

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

Gdzie myParamMethod() jest naszą metodą przekazaną jako parametr(w tym przypadku methodParam).

 8
Author: Vasile Doe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-25 13:13:17

Używanie interfejsów może być najlepszym sposobem w architekturze kodowania Javy.

Ale mijanie obiektu nadającego się do pracy również mogłoby zadziałać i byłoby o wiele bardziej praktyczne i elastyczne, jak sądzę.

 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 */
   }
});
 4
Author: Infinity,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-20 11:37:31

Refleksja nigdy nie jest dobrym pomysłem, ponieważ jest trudniejsza do odczytania i debugowania, ale jeśli jesteś w 100% pewien, co robisz, możesz po prostu wywołać coś takiego jak set_method (R. id.button_profile_edit," toggle_edit"), aby dołączyć metodę do widoku. Jest to przydatne w fragmencie, ale ponownie, niektórzy ludzie uznają to za anty-wzór, więc ostrzegam.

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);
}
 0
Author: superarts.org,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-08 02:15:06