web-dev-qa-db-fra.com

Fonction de passage en tant que paramètre dans java

Je me familiarise avec Android framework et Java et je voulais créer une classe générale "NetworkHelper" qui gèrerait la plupart du code de réseau, ce qui me permettrait de simplement appeler des pages Web à partir de celui-ci.

J'ai suivi cet article du développeur.Android.com pour créer ma classe de réseautage: http://developer.Android.com/training/basics/network-ops/connecting.html

Code:

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

    }

} 

}

Dans mon cours d'activité, j'utilise le cours de cette façon:

connHelper = new NetworkHelper(this);

...

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

Le problème que je rencontre est que je devrais en quelque sorte rappeler l’activité et qu’elle devrait pouvoir être définie dans la fonction "downloadUrl ()". Par exemple, lorsque le téléchargement est terminé, la fonction publique "handleWebpage (String data)" en activité est appelée avec la chaîne chargée en tant que paramètre.

J'ai fait quelques recherches sur Google et j'ai trouvé que je devais utiliser les interfaces pour obtenir cette fonctionnalité. Après avoir passé en revue quelques questions/réponses de stackoverflow similaires, je n’ai pas réussi à le faire fonctionner et je ne suis pas sûr d’avoir bien compris les interfaces: Comment puis-je passer la méthode en tant que paramètre en Java? Pour être honnête en utilisant Les classes anonymes sont nouvelles pour moi et je ne sais pas trop où ni comment appliquer les exemples de code dans le fil de discussion mentionné.

Ma question est donc de savoir comment je pourrais transmettre la fonction de rappel à ma classe de réseau et l'appeler une fois le téléchargement terminé. Où la déclaration d'interface va, implémente le mot-clé et ainsi de suite? S'il vous plaît noter que je suis débutant avec Java (ont d'autres antécédents de programmation cependant) donc je vous serais reconnaissant une explication tout au long :) :) Merci!

62
Tumetsu

Utilisez une interface de rappel ou une classe abstraite avec des méthodes de rappel abstraites.

Exemple d'interface de rappel:

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

La deuxième option est encore plus concise. Vous n'avez même pas besoin de définir une méthode abstraite pour "l'événement onDownloaded" car onPostExecute fait exactement ce qui est nécessaire. Étendez simplement votre DownloadWebpageTask avec une classe en ligne anonyme dans votre méthode 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);
    }

    //...
96
Tomasz Gawel

NO interface, NO lib, NO Java 8 nécessaires!

En utilisant simplement Callable<V> À partir de 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();
    }
}

Comment l'utiliser:

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

myParamMethod() est notre méthode passée en paramètre (dans ce cas, methodParam).

24
Vasile Doe

Oui, une interface est la meilleure façon à mon humble avis. Par exemple, GWT utilise le modèle de commande avec une interface comme celle-ci:

public interface Command{
    void execute();
}

De cette façon, vous pouvez passer la fonction d'une méthode à une autre

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

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

La solution clé en main est que cela n’est pas possible en Java. Java n'accepte pas fonctions d'ordre supérieur . Il peut être obtenu à l'aide de quelques "astuces". Normalement, l'interface est celle utilisée comme vous l'avez vu. Regardez ici pour plus d'informations Vous pouvez également utiliser la réflexion pour y parvenir, mais cela est sujet aux erreurs.

10
olorin

L'utilisation d'interfaces peut être la meilleure solution dans Java Coding Architecture.

Mais passer un objet Runnable pourrait aussi bien fonctionner, et ce serait beaucoup plus pratique et flexible, je pense.

 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
Infinity

La réflexion n’est jamais une bonne idée car il est plus difficile de lire et de déboguer, mais si vous êtes sûr à 100% de ce que vous faites, vous pouvez simplement appeler quelque chose comme set_method (R.id.button_profile_edit, "toggle_edit") à laquelle attacher une méthode. une vue. Ceci est utile dans fragment, mais encore une fois, certaines personnes le considéreraient comme anti-modèle alors soyez prévenus.

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
superarts.org