Passing a function as a parameter in java

I became familiar with the Android and Java platforms and wanted to create a common "NetworkHelper" class that would handle most network codes, allowing me to simply call web pages from it.

I followed this article from developer.android.com to create my network class: http://developer.android.com/training/basics/network-ops/connecting.html

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

}

In my activity class, I use the class as follows:

 connHelper = new NetworkHelper(this); 

...

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

The problem I am facing is that I have to somehow return the callback to the activity, and it must be defined in the "downloadUrl ()" function. For example, when the download ends, the publicoid function "handleWebpage (String data)" in the activity is called with the loaded string as its parameter.

I did some search queries and found that I need to use interfaces in some way to achieve this functionality. After considering several similar stackoverflow questions / answers, I didn’t get the job, and I'm not sure if I understood the interfaces correctly: How to pass a method as a parameter in Java? Honestly, anonymous classes are new to me, and I'm not quite sure where and how I should apply the code fragments of the example in the mentioned stream.

So my question is how to pass the callback function to my network class and call it after the download is complete? Where does the interface declaration go, implement the keyword and so on? Please note that I am starting with Java (I have a different programming background), so I will be grateful for the explanation :) Thanks!

+45
java function android parameters interface
May 28 '13 at 20:16
source share
6 answers

Use a callback interface or abstract class with abstract callback methods.

Example callback interface:

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

The second option is even more concise. You do not even need to define an abstract method for the "onDownloaded event", since onPostExecute does exactly what you need. Just add DownloadWebpageTask to the anonymous inline class inside your downloadUrl method.

  //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); } //... 
+73
May 28 '13 at 20:19
source share

Yes, the interface is the best way IMHO. For example, GWT uses a command template with this interface:

 public interface Command{ void execute(); } 

This way you can pass a function from a method to another

 public void foo(Command cmd){ ... cmd.execute(); } public void bar(){ foo(new Command(){ void execute(){ //do something } }); } 
+19
May 28 '13 at 20:20
source share

The original solution is that this is not possible in Java. Java does not accept higher order functions . This can be achieved with at least some tricks. Usually the interface is used as you saw. Please see here for more information. You can also use reflection to achieve it, but it is prone to errors.

+9
May 28 '13 at 20:27
source share

Using interfaces may be the best in Java Coding architecture.

But passing a Runnable object can also work, and would be much more practical and flexible, I think.

  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
May 20 '14 at 11:37
source share

Reflecting is never a good idea, since it is harder to read and debug, but if you are 100% sure what you are doing, you can simply call something like set_method (R.id.button_profile_edit, "toggle_edit") to attach the method for presentation . This is useful in the snippet, but, again, some people will consider it an anti-pattern, so it should be warned.

 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
Aug 08 '14 at 2:15
source share

NO interface, NO lib, NO Java 8 needed!

Just using Callable<V> from 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(); } } 

How to use it:

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

Where myParamMethod() is our passed method as a parameter (in this case methodParam ).

0
Oct. 25 '17 at 13:13
source share



All Articles