Función de paso como parámetro en java

Me estoy familiarizando con el framework de Android y Java y quería crear una clase "NetworkHelper" general que manejaría la mayor parte del código de red, lo que me permitiría simplemente llamar a páginas web desde el mismo.

Seguí este artículo de developer.android.com para crear mi clase de red: http://developer.android.com/training/basics/network-ops/connecting.html

Código:

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

}

En mi clase de actividad uso la clase de esta manera:

 connHelper = new NetworkHelper(this); 

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

Problema que estoy teniendo es que de alguna manera hacer una devolución de llamada a la actividad y que debe ser definible en la función "downloadUrl ()". Por ejemplo, cuando se termina la descarga, la función public void "handleWebpage (String data)" en la actividad se llama con la cadena cargada como su parámetro.

Hice algunas google y encontré que de alguna manera debería usar interfaces para lograr esta funcionalidad. Después de revisar algunas preguntas / respuestas de stackoverflow similares, no lo hice funcionar y no estoy seguro si entendí las interfaces correctamente: ¿Cómo paso el método como un parámetro en Java? Para ser honesto usando las clases anónimas es nuevo para mí y no estoy muy seguro de dónde o cómo debo aplicar los fragmentos de código de ejemplo en el hilo mencionado.

¿Así que mi pregunta es cómo podría pasar la función de devolución de llamada a mi clase de red y llamarla después de los acabados de descarga? Donde la declaración de interfaz va, implementa la palabra clave y así sucesivamente? Observe por favor que soy principiante con Java (tenga el otro fondo de programación sin embargo) así que agradecería una explicación entera 🙂 ¡Gracias!

Utilice una interfaz de devolución de llamada o una clase abstracta con métodos de devolución de llamada abstractos.

Ejemplo de interfaz de devolución de llamada:

 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 segunda opción es aún más concisa. Ni siquiera tiene que definir un método abstracto para "onDownloaded event", ya que onPostExecute hace exactamente lo que se necesita. Simplemente extienda su DownloadWebpageTask con una clase inline anónima dentro de su método 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); } //... 

Sí, una interfaz es la mejor manera IMHO. Por ejemplo, GWT utiliza el patrón de comandos con una interfaz como ésta:

 public interface Command{ void execute(); } 

De esta manera, puede pasar la función de un método a otro

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

La solución fuera de la caja es que esto no es posible en Java. Java no acepta funciones de orden superior . Se puede lograr aunque por algunos "trucos". Normalmente, la interfaz es la que se utiliza como se vio. Por favor, eche un vistazo aquí para más información. También puede utilizar la reflexión para lograrlo, pero esto es propenso a errores.

El uso de interfaces puede ser la mejor manera en Java Coding Architecture.

Pero, pasar un objeto Runnable podría funcionar también, y sería mucho más práctico y flexible, creo.

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

La reflexión nunca es una buena idea, ya que es más difícil de leer y depurar, pero si estás 100% seguro de lo que estás haciendo, simplemente puedes llamar algo como set_method (R.id.button_profile_edit, "toggle_edit") para adjuntar un método a una vista. Esto es útil en el fragmento, pero una vez más, algunas personas lo consideran como anti-patrón, así que se advirtió.

 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); } 
  • Datepicker y timepicker - establece valores máximos y mínimos en android
  • Uso de Proguard para obfuscar la aplicación Android con las bibliotecas de Dropbox.com
  • RealmObject AND Parcelable
  • ¿Cómo escalar sprites en libgdx según las resoluciones de pantalla?
  • Paginación de texto en Android
  • Android - reproducir sonido diferente en cada segundo clic
  • Hacer un juego en LibGDX con colisión y gravedad sin usar Box2D
  • Android - Creación de una máscara circular en vídeo
  • Android Studio - Gradle 1.5 - opción de la biblioteca
  • Selección de un proyecto ORM para Android (nivel mínimo de API 7)
  • Nullpointerexception en dispatchDraw con ViewPager en fragmento anidado con PageTransformer
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.