我已经熟悉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()"函数中定义它。例如,下载完成后,将使用加载的字符串作为其参数来调用活动中的公共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 {
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);
}
//...
谢谢,这帮助我解决了这个问题,我想我现在已经了解了接口的基本知识:)
有趣的是,了解接口如何在一般的Java编程中发挥重要作用。
不需要接口,不需要库,不需要Java 8!
仅使用java.util.concurrent中的Callable
public static void superMethod(String simpleParam, Callable methodParam) {
//your logic code [...]
//call methodParam
try {
methodParam.call();
} catch (Exception e) {
e.printStackTrace();
}
}
如何使用它:
superMethod("Hello world", new Callable() {
public Void call() {
myParamMethod();
return null;
}
}
);
其中myParamMethod()是我们作为参数传递的方法(在本例中为methodParam)。
谢谢,这正是我在寻找=)
感谢您的回答。但是,该示例并不清楚(我整夜都忙了,所以请原谅我的头疼的问题)如何将myParamMethod传递给simpleParam。例如,我在Ion周围有一个包装器,将服务器参数和Json中封装的目标URL传递给该包装器,我是否要进行superMethod(serverParams,callEndpointIon);还是我必须每次都重写Callable?
真的很喜欢,...就像一种魅力,可以解决我的问题.... :-D谢谢!
如果您使用Callable,则没有真正的理由不使用Runnable,因为您仍然会返回Void。它将消除对该return null;语句的需要。
...并且没有输入参数))
是的,接口是恕我直言的最好方法。例如,GWT通过以下接口使用命令模式:
public interface Command{
void execute();
}
这样,您可以将函数从方法传递给另一个方法
public void foo(Command cmd){
...
cmd.execute();
}
public void bar(){
foo(new Command(){
void execute(){
//do something
}
});
}
什么是GWT,以及如何传递任何参数?
@Buksy是您要寻找的吗?公共接口命令{void execute(Object ... object); }传递无限的对象:D
开箱即用的解决方案是这在Java中是不可能的。 Java不接受高阶函数。尽管可以通过一些"技巧"来实现。通常,该接口是您所看到的那种接口。请在这里查看更多信息。您也可以使用反射来实现,但这很容易出错。
这并不是一个真正值得回答的答案,因为您所做的所有事情都会建议您对其进行研究,将其作为注释更合适。
由于他的代表人数少于50,因此他无法发表评论,只能回答。我一直不喜欢那个。
对于有经验的程序员使用Java来说,这是非常有用的概念性答案。谢谢,@ 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 */
}
});
非常干净整洁的实现。
反射从来都不是一个好主意,因为它很难阅读和调试,但是如果您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);
}