2013-05-28 16 views
46

Mi sto familiarizzando con il framework Android e Java e ho voluto creare una classe generale "NetworkHelper" che gestisse la maggior parte del codice di rete, permettendomi di chiamare solo pagine web da esso.Passaggio della funzione come parametro in java

Ho seguito questo articolo dal developer.android.com per creare la mia classe di rete: http://developer.android.com/training/basics/network-ops/connecting.html

Codice:

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

    } 

} 

}

Nella mia classe di attività Io uso la classe in questo modo :

connHelper = new NetworkHelper(this); 

...

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

problema che sto avendo è che dovrei in qualche modo fare un callback torna all'attività e dovrebbe essere definibile in "downloadURL) (" la funzione. Ad esempio al termine del download, la funzione "handleWebpage (String data)" pubblica in attività viene chiamata con la stringa caricata come parametro.

Ho fatto qualche ricerca su google e ho scoperto che avrei dovuto utilizzare in qualche modo le interfacce per ottenere questa funzionalità. Dopo aver esaminato alcune domande/risposte StackOverflow simili, non ho funzionato e non sono sicuro di aver compreso correttamente le interfacce: How do I pass method as a parameter in Java? Per essere onesto, l'utilizzo delle classi anonime è nuovo per me e non sono sicuro di dove o come dovrebbe applicare i frammenti di codice di esempio nel thread menzionato.

Quindi la mia domanda è come potrei passare la funzione di callback alla mia classe di rete e chiamarla dopo il download? Dove va la dichiarazione dell'interfaccia, implementa la parola chiave e così via? Nota che sono principiante con Java (ho un altro background di programmazione) quindi apprezzerei una spiegazione completa :) Grazie!

risposta

75

Utilizzare un'interfaccia di callback o una classe astratta con metodi di callback astratti.

richiamata esempio interfaccia:

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 seconda opzione è ancora più conciso. Non è nemmeno necessario definire un metodo astratto per "evento scaricato" come onPostExecute fa esattamente ciò che è necessario. Estendi semplicemente il tuo DownloadWebpageTask con una classe inline anonima all'interno del tuo metodo 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); 
    } 

    //... 
+1

Grazie, questo mi ha aiutato a risolvere il problema e ora penso di capire le basi delle interfacce :) – Tumetsu

+1

Interessante vedere come le interfacce svolgono un ruolo importante nella programmazione Java generale. –

+3

Questo è così prima java 8 ... –

20

Sì, un'interfaccia è il modo migliore IMHO. Ad esempio, GWT utilizza il modello di comando con un'interfaccia simile:

public interface Command{ 
    void execute(); 
} 

In questo modo, è possibile passare funzione da un metodo all'altro

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

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

Che cosa è GWT e come passare qualsiasi parametro? – Buksy

+0

@Buksy è ciò che cerchi? comando interfaccia pubblica { void execute (Object ... object); } Per passare oggetti illimitati: D –

9

La dalla soluzione scatola è che questo non è possibile in Java. Java non accetta Higher-order functions. Può essere ottenuto attraverso alcuni "trucchi". Normalmente l'interfaccia è quella usata come hai visto. Si prega di dare un'occhiata here per ulteriori informazioni. Puoi anche usare la riflessione per raggiungerlo, ma questo è soggetto a errori.

+1

Questo non è veramente degno come risposta, dal momento che tutto ciò che si suggerisce è che cosa guardare in esso sarebbe più appropriato come commento. –

+8

Dato che ha meno di 50 rappresentanti, non può commentare, solo può rispondere. Non mi è sempre piaciuto. –

+1

Risposta concettuale molto utile, per i programmatori esperti che si spostano in Java. Grazie, @olorin! – Fattie

4

L'utilizzo delle interfacce può essere il modo migliore in Java Coding Architecture.

Tuttavia, il passaggio di un oggetto Runnable potrebbe funzionare allo stesso modo, e sarebbe molto più pratico e flessibile, penso.

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

Implementazione molto pulita e ordinata. – SolidSnake

0

riflessione è mai una buona idea, dato che è più difficile da leggere e correggere, ma se si è sicuri al 100% quello che stai facendo, si può semplicemente chiamare qualcosa come set_method (R.id.button_profile_edit, "toggle_edit ") per allegare un metodo a una vista. Questo è utile in frammenti, ma ancora una volta, alcune persone lo considererebbero come anti-pattern, quindi sii avvertito.

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

NO interfaccia, NO lib, NO Java 8 risultato garantito!

Usando solo Callable<V> da 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(); 
    } 
} 

Come si usa:

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

Dove myParamMethod() è il nostro metodo passato come parametro (in questo caso methodParam).

Problemi correlati