I'm getting familiar with Android framework and Java and wanted to create a general "NetworkHelper" class which would handle most of the networking code enabling me to just call web-pages from it.
我熟悉Android框架和Java,并希望创建一个通用的“NetworkHelper”类,它可以处理大多数网络代码,使我能够从中调用网页。
I followed this article from the developer.android.com to create my networking class: http://developer.android.com/training/basics/network-ops/connecting.html
我按照developer.android.com上的这篇文章来创建我的网络类:http://developer.android.com/training/basics/network-ops/connecting.html
Code:
码:
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);
}
}
}
}
In my activity class I use the class this way:
在我的活动类中,我以这种方式使用类:
connHelper = new NetworkHelper(this);
...
...
if (connHelper.checkConnection())
{
//connection ok, download the webpage from provided url
connHelper.downloadUrl(stringUrl);
}
Problem I'm having is that I should somehow make a callback back to the activity and it should be definable in "downloadUrl()" function. For example when download finishes, public void "handleWebpage(String data)" function in activity is called with loaded string as its parameter.
我遇到的问题是我应该以某种方式回调活动,它应该可以在“downloadUrl()”函数中定义。例如,当下载完成时,将调用活动中的public void“handleWebpage(String data)”函数,并将加载的字符串作为其参数。
I did some googling and found that I should somehow use interfaces to achieve this functionality. After reviewing few similar * questions/answers I didn't get it working and I'm not sure if I understood interfaces properly: How do I pass method as a parameter in Java? To be honest using the anonymous classes is new for me and I'm not really sure where or how I should apply the example code snippets in the mentioned thread.
我做了一些谷歌搜索,发现我应该以某种方式使用接口来实现这个功能。在查看了几个类似的*问题/答案后,我没有得到它的工作,我不确定我是否正确理解了接口:如何在Java中将方法作为参数传递?说实话,使用匿名类对我来说是新的,我不确定我应该在提到的线程中应用示例代码片段的地方或方式。
So my question is how I could pass the callback function to my network class and call it after download finishes? Where the interface declaration goes, implements keyword and so on? Please note that I'm beginner with Java (have other programming background though) so I'd appreciate a throughout explanation :) Thank you!
所以我的问题是如何将回调函数传递给我的网络类并在下载完成后调用它?接口声明的位置,实现关键字等等?请注意我是Java的初学者(虽然有其他编程背景)所以我很感激整个解释:)谢谢!
6 个解决方案
#1
83
Use a callback interface or an abstract class with abstract callback methods.
使用带有抽象回调方法的回调接口或抽象类。
Callback interface example:
回调接口示例:
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...
}
}
The second option is even more concise. You do not even have to define an abstract method for "onDownloaded event" as onPostExecute
does exactly what is needed. Simply extend your DownloadWebpageTask
with an anonymous inline class inside your downloadUrl
method.
第二种选择更简洁。您甚至不必为“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);
}
//...
#2
20
Yes, an interface is the best way IMHO. For example, GWT uses the command pattern with an interface like this:
是的,界面是恕我直言的最佳方式。例如,GWT使用带有如下界面的命令模式:
public interface Command{
void execute();
}
In this way, you can pass function from a method to another
通过这种方式,您可以将函数从方法传递到另一个方法
public void foo(Command cmd){
...
cmd.execute();
}
public void bar(){
foo(new Command(){
void execute(){
//do something
}
});
}
#3
9
The out of the box solution is that this is not possible in Java. Java does not accept Higher-order functions. It can be achieved though by some "tricks". Normally the interface is the one used as you saw. Please take a look here for further information. You can also use reflection to achieve it, but this is error prone.
开箱即用的解决方案是Java中不可能实现这一点。 Java不接受高阶函数。它可以通过一些“技巧”来实现。通常,界面是您看到的那个。请查看此处了解更多信息。您也可以使用反射来实现它,但这很容易出错。
#4
7
NO interface, NO lib, NO Java 8 needed!
没有接口,没有lib,不需要Java 8!
Just using Callable<V>
from java.util.concurrent
只需使用java.util.concurrent中的Callable
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;
}
}
);
Where myParamMethod()
is our passed method as parameter (in this case methodParam
).
其中myParamMethod()是我们传递给参数的方法(在本例中为methodParam)。
#5
4
Using Interfaces may be the best way in Java Coding Architecture.
使用接口可能是Java编码体系结构中的最佳方式。
But, passing a Runnable object could work as well, and it would be much more practical and flexible, I think.
但是,传递一个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 */
}
});
#6
0
Reflection is never a good idea since it's harder to read and debug, but if you are 100% sure what you're doing, you can simply call something like set_method(R.id.button_profile_edit, "toggle_edit") to attach a method to a view. This is useful in fragment, but again, some people would consider it as anti-pattern so be warned.
反射从来都不是一个好主意,因为它更难以阅读和调试,但如果你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);
}
#1
83
Use a callback interface or an abstract class with abstract callback methods.
使用带有抽象回调方法的回调接口或抽象类。
Callback interface example:
回调接口示例:
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...
}
}
The second option is even more concise. You do not even have to define an abstract method for "onDownloaded event" as onPostExecute
does exactly what is needed. Simply extend your DownloadWebpageTask
with an anonymous inline class inside your downloadUrl
method.
第二种选择更简洁。您甚至不必为“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);
}
//...
#2
20
Yes, an interface is the best way IMHO. For example, GWT uses the command pattern with an interface like this:
是的,界面是恕我直言的最佳方式。例如,GWT使用带有如下界面的命令模式:
public interface Command{
void execute();
}
In this way, you can pass function from a method to another
通过这种方式,您可以将函数从方法传递到另一个方法
public void foo(Command cmd){
...
cmd.execute();
}
public void bar(){
foo(new Command(){
void execute(){
//do something
}
});
}
#3
9
The out of the box solution is that this is not possible in Java. Java does not accept Higher-order functions. It can be achieved though by some "tricks". Normally the interface is the one used as you saw. Please take a look here for further information. You can also use reflection to achieve it, but this is error prone.
开箱即用的解决方案是Java中不可能实现这一点。 Java不接受高阶函数。它可以通过一些“技巧”来实现。通常,界面是您看到的那个。请查看此处了解更多信息。您也可以使用反射来实现它,但这很容易出错。
#4
7
NO interface, NO lib, NO Java 8 needed!
没有接口,没有lib,不需要Java 8!
Just using Callable<V>
from java.util.concurrent
只需使用java.util.concurrent中的Callable
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;
}
}
);
Where myParamMethod()
is our passed method as parameter (in this case methodParam
).
其中myParamMethod()是我们传递给参数的方法(在本例中为methodParam)。
#5
4
Using Interfaces may be the best way in Java Coding Architecture.
使用接口可能是Java编码体系结构中的最佳方式。
But, passing a Runnable object could work as well, and it would be much more practical and flexible, I think.
但是,传递一个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 */
}
});
#6
0
Reflection is never a good idea since it's harder to read and debug, but if you are 100% sure what you're doing, you can simply call something like set_method(R.id.button_profile_edit, "toggle_edit") to attach a method to a view. This is useful in fragment, but again, some people would consider it as anti-pattern so be warned.
反射从来都不是一个好主意,因为它更难以阅读和调试,但如果你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);
}