首页 文章

将函数作为参数传递给java

提问于
浏览
57

我熟悉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)”函数,并将加载的字符串作为其参数 .

我做了一些谷歌搜索,发现我应该以某种方式使用接口来实现这个功能 . 在审查了几个类似的stackoverflow问题/答案后,我没有确定我是否正确理解了接口:How do I pass method as a parameter in Java?老实说,使用匿名类对我来说是新的,我不确定我应该在哪里或如何应用示例代码片段提到的线程 .

所以我的问题是如何将回调函数传递给我的网络类并在下载完成后调用它?接口声明的位置,实现关键字等等?请注意我是Java的初学者(虽然有其他编程背景)所以我很感激整个解释:)谢谢!

6 回答

  • 89

    使用带有抽象回调方法的回调接口或抽象类 .

    回调接口示例:

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

    第二种选择更简洁 . 你甚至不需要为"onDownloaded event"定义一个抽象方法,因为 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);
        }
    
        //...
    
  • 4

    是的,界面是恕我直言的最佳方式 . 例如,GWT使用带有如下界面的命令模式:

    public interface Command{
        void execute();
    }
    

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

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

    NO interface, NO lib, NO Java 8 needed!

    刚刚从 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();
        }
    }
    

    How to use it:

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

    myParamMethod() 是我们传递的方法作为参数(在本例中为 methodParam ) .

  • 20

    开箱即用的解决方案是Java中不可能实现这一点 . Java不接受Higher-order functions . 它可以通过一些"tricks"来实现 . 通常,界面是您看到的那个 . 请查看here了解更多信息 . 您也可以使用反射来实现它,但这很容易出错 .

  • 12

    使用接口可能是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 */
       }
    });
    
  • 0

    反射从来都不是一个好主意,因为它更难以阅读和调试,但如果你100%确定你正在做什么,你可以简单地调用类似set_method(R.id.button_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);
    }
    

相关问题