在 java 中将函数作为参数传递

我正在熟悉 Android 框架和 Java,并希望创建一个通用的“ NetworkHelper”类,它可以处理大部分网络代码,使我能够从中调用网页。

我按照这篇文章的 developer.android.com 创建了我的网络类: http://developer.android.com/training/basics/network-ops/connecting.html

密码:

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


}


}

}

在我的活动课上,我是这样使用这门课的:

connHelper = new NetworkHelper(this);

...

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

我遇到的问题是,我应该以某种方式回调该活动,并且它应该在“ downloadUrl ()”函数中可定义。例如,当下载完成时,调用活动中的 public void“ handleWebpage (String data)”函数,并将加载的字符串作为其参数。

我在谷歌上搜索了一下,发现我应该以某种方式使用接口来实现这个功能。在回顾了几个类似的堆栈溢出问题/答案之后,我不知道它是否能正确地工作,我不确定我是否理解了接口: 如何在 Java 中将方法作为参数传递?说实话,使用匿名类对我来说是新的,我不确定我应该在哪里或如何应用上述线程中的示例代码片段。

所以我的问题是如何将回调函数传递给我的网络类,并在下载完成后调用它?接口声明去哪里,实现关键字等等? 请注意,我是 Java 初学者(虽然有其他编程背景) ,所以我希望得到一个贯穿始终的解释:)谢谢!

98586 次浏览

使用具有抽象回调方法的回调接口或抽象类。

回调接口示例:

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

第二种选择甚至更简洁。您甚至不需要为“ onDownloadevent”定义一个抽象方法,因为 onPostExecute完全满足您的需要。只需在 downloadUrl方法中使用匿名内联类扩展 DownloadWebpageTask即可。

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


//...

是的,接口是 IMHO 的最佳方式。例如,GWT 使用命令模式的接口如下:

public interface Command{
void execute();
}

通过这种方式,您可以将函数从一个方法传递给另一个方法

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


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

开箱即用的解决方案是这在 Java 中是不可能的。Java 不接受 高阶函数。这可以通过一些“技巧”来实现。正常情况下,接口就是你看到的那个。请看看 给你进一步的信息。您也可以使用反射来实现它,但是这很容易出错。

在 Java 编码体系结构中,使用接口可能是最好的方法。

但是,传递 Runnable 对象也可以工作,而且我认为它会更加实用和灵活。

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

反射从来都不是一个好主意,因为它很难阅读和调试,但是如果你100% 确定你在做什么,你可以简单地调用 set _ method (R.id.ton _ profile _ edit,“ toggle _ edit”)来将一个方法附加到一个视图上。这在片段中是有用的,但是同样,有些人会将其视为反模式,因此需要提醒一下。

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

没有接口,没有库,没有 Java 8需要!

只是使用 java.util.concurrent中的 Callable<V>

public static void superMethod(String simpleParam, Callable<Void> methodParam) {


//your logic code [...]


//call methodParam
try {
methodParam.call();


} catch (Exception e) {
e.printStackTrace();
}
}

使用方法:

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

其中 myParamMethod()是作为参数传递的方法(在本例中为 methodParam)。

接口回调支持泛型类型。

在 Callbackable.java 中

public interface Callbackable<T> {
void call(T obj);
}

使用方法:

public class MainActivity extends AppCompatActivity {


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);


doSomeNetworkAction(new Callbackable<Integer>() {
@Override
public void call(Integer obj) {
System.out.println("have received: " + obj + " from network");
}
});


}
    

// You can change Integer to String or to any model class like Customer, Profile, Address...
public void doSomeNetworkAction(Callbackable<Integer> callback) {
// acb xyz...
callback.call(666);
}
}