Передача функции в качестве параметра в 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
{



    @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 ()" функция. Например, когда загрузка заканчивается, общественность пусто "handleWebpage (Строковые данные)" функция в действии называют с загруженной строкой как ее параметр.

Я сделал некоторый поиск с помощью Google и нашел, что должен так или иначе использовать интерфейсы для достижения этой функциональности. После рассмотрения немногих подобных stackoverflow вопросов/ответов я не получил его работа, и я не уверен, понял ли я интерфейсы правильно: Как я передаю метод в качестве параметра в Java? Быть честным использованием анонимных классов является новым для меня, и я не действительно уверен, где или как я должен применить отрывки примера кода в упомянутом потоке.

Таким образом, мой вопрос состоит в том, как я мог передать функцию обратного вызова своему классу сети и назвать его после того, как загрузка заканчивается? То, куда объявление интерфейса идет, реализует ключевое слово и так далее? Обратите внимание на то, что я - новичок с Java (имейте другой фон программирования хотя), таким образом, я ценил бы в течение объяснения :) Спасибо!

62
задан 23 May 2017 в 15:17

6 ответов

Используйте интерфейс обратного вызова или абстрактный класс с абстрактными методами обратного вызова.

пример Интерфейса обратного вызова:

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 событие", поскольку onPostExecute делает точно, что необходимо. Просто расширьте Ваш 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);
    }

    //...
98
ответ дан 31 October 2019 в 13:27

интерфейс NO, НИКАКОЙ lib, НИКАКОЙ 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).

27
ответ дан 31 October 2019 в 13:27

Да, интерфейс является лучшим способом, по моему скромному мнению. Например, GWT использует шаблон "команда" с интерфейсом как это:

public interface Command{
    void execute();
}

Таким образом, можно передать функцию от метода до другого

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

public void bar(){
  foo(new Command(){
     void execute(){
        //do something
     }
  });
}
21
ответ дан 31 October 2019 в 13:27

Из готового решения то, что это не возможно в Java. Java не принимает функции Высшего порядка . Это может быть достигнуто хотя некоторыми "приемами". Обычно интерфейс является тем, используемым, как Вы видели. Смотрите здесь для получения дополнительной информации. Можно также использовать отражение для достижения его, но это подвержено ошибкам.

10
ответ дан 31 October 2019 в 13:27

Используя Интерфейсы может быть лучший способ в Архитектуре Кодирования Java.

, Но, передавая Выполнимый объект мог работать также, и это будет намного более практичным и гибким, я думаю.

 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
ответ дан 31 October 2019 в 13:27

Отражение никогда не является хорошей идеей, так как более трудно считать и отладить, но если Вы на 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
ответ дан 31 October 2019 в 13:27

Другие вопросы по тегам:

Похожие вопросы: