2013-05-28 21 views
46

我對Android框架和Java很熟悉,想要創建一個通用的「NetworkHelper」類,它將處理大部分網絡代碼,使我能夠從中調用網頁。在java中傳遞函數作爲參數

我跟着本文從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); 

    } 

} 

}

在我的活動I類使用類這樣:

connHelper = new NetworkHelper(this); 

...

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

問題我遇到的情況是,我應該以某種方式回調活動,並且它應該在「downloadUrl()」函數中定義。例如,當下載完成時,活動中的public void「handleWebpage(String data)」函數將以加載的字符串作爲參數進行調用。

我做了一些Google搜索,發現我應該以某種方式使用接口來實現此功能。在回顧了幾個類似的stackoverflow問題/答案後,我沒有得到它的工作,我不知道我是否正確理解接口:How do I pass method as a parameter in Java?說實話,使用匿名類對我來說是新的,我不確定我在哪裏或如何應該在提到的線程中應用示例代碼片段。

所以我的問題是如何將回調函數傳遞給我的網絡類並在下載完成後調用它?接口聲明去的地方,實現關鍵字等等? 請注意,我是Java初學者(雖然有其他編程背景),所以我會很感激整個解釋:)謝謝!

回答

75

使用回調接口或具有抽象回調方法的抽象類。

回調接口例如:

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完全不需要。只需在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); 
    } 

    //... 
+1

謝謝,這幫助我解決了這個問題,我想我現在瞭解接口的基礎知識:) – Tumetsu

+1

有趣的是,看看接口如何在一般的java編程中發揮重要作用。 –

+3

這是之前Java 8 ... –

20

是的,接口是最好的方式恕我直言。例如,GWT使用像這樣的接口的命令模式:

public interface Command{ 
    void execute(); 
} 

以這種方式,可以從一個方法傳遞函數到另一個

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

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

Whats GWT以及如何傳遞任何參數? – Buksy

+0

@Buksy是你在找什麼?公共接口命令void execute(Object ... object); }要通過無限制的對象:D –

9

的開箱溶液的是,這在Java中是不可能的。 Java不接受Higher-order functions。它可以通過一些「技巧」來實現。通常界面是你看到的那個界面。請參閱here瞭解更多信息。您也可以使用反射來實現它,但這很容易出錯。

+1

這並不是真的值得回答,因爲你所做的一切都暗示着看什麼可能更適合作爲評論。 –

+8

由於他不到50個代表他不能評論,只能回答。我一直不喜歡那樣。 –

+1

對於有經驗的程序員轉向Java非常有用的概念答案。謝謝,@olorin! – Fattie

4

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

非常乾淨整潔的實施。 – SolidSnake

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

NO接口,NO lib,NO需要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)。