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(ただし、他のプログラミングのバックグラウンドを持っています)の初心者であることに注意してください。
コールバックインターフェイスまたは抽象コールバックメソッドを持つ抽象クラスを使用します。
コールバックインターフェイスの例:
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);
}
//...
インターフェイスなし、ライブラリなし、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
)。
はい、インターフェイスは私見の最良の方法です。たとえば、GWTは次のようなインターフェイスでコマンドパターンを使用します。
public interface Command{
void execute();
}
このようにして、メソッドから別のメソッドに関数を渡すことができます
public void foo(Command cmd){
...
cmd.execute();
}
public void bar(){
foo(new Command(){
void execute(){
//do something
}
});
}
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.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);
}