web-dev-qa-db-ja.com

javaのパラメーターとして関数を渡す

AndroidフレームワークとJavaに慣れてきたので、ほとんどのネットワークコードを処理して、そこからWebページを呼び出すだけの一般的な「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の質問/回答をレビューした後、私はそれを動作させませんでしたし、インターフェイスを正しく理解しているかどうかわかりません: Javaでパラメータとしてメソッドを渡す方法は? 匿名クラスは私にとって新しいものであり、前述のスレッドのサンプルコードスニペットをどこでどのように適用すべきかはよくわかりません。

だから私の質問は、コールバック関数をネットワーククラスに渡し、ダウンロードが完了した後に呼び出す方法ですか?インターフェイス宣言がどこに行くか、キーワードなどを実装しますか?私はJava(ただし、他のプログラミングのバックグラウンドを持っています)の初心者であることに注意してください。

62
Tumetsu

コールバックインターフェイスまたは抽象コールバックメソッドを持つ抽象クラスを使用します。

コールバックインターフェイスの例:

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

2番目のオプションはさらに簡潔です。 onPostExecuteは必要なことを正確に行うため、「onDownloadedイベント」の抽象メソッドを定義する必要さえありません。 DownloadWebpageTaskメソッド内の匿名インラインクラスで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);
    }

    //...
96
Tomasz Gawel

インターフェイスなし、ライブラリなし、Java 8が必要!

Callable<V>から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();
    }
}

使用方法:

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

ここで、myParamMethod()はパラメーターとして渡されたメソッドです(この場合はmethodParam)。

24
Vasile Doe

はい、インターフェイスは私見の最良の方法です。たとえば、GWTは次のようなインターフェイスでコマンドパターンを使用します。

public interface Command{
    void execute();
}

このようにして、メソッドから別のメソッドに関数を渡すことができます

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

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

すぐに使用できるソリューションは、これはJavaでは不可能であるということです。 Javaは 高階関数 を受け入れません。しかし、いくつかの「トリック」によって達成することができます。通常、インターフェイスは見たとおりに使用されます。詳細については、 こちら をご覧ください。リフレクションを使用してそれを達成することもできますが、これはエラーを起こしやすいです。

10
olorin

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

リフレクションは読みやすくデバッグするのが難しいので決して良い考えではありませんが、あなたが何をしているのか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);
}
0
superarts.org