Je me familiarise avec Android framework et Java et je voulais créer une classe générale "NetworkHelper" qui gèrerait la plupart du code de réseau, ce qui me permettrait de simplement appeler des pages Web à partir de celui-ci.
J'ai suivi cet article du développeur.Android.com pour créer ma classe de réseautage: 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);
}
}
}
Dans mon cours d'activité, j'utilise le cours de cette façon:
connHelper = new NetworkHelper(this);
...
if (connHelper.checkConnection())
{
//connection ok, download the webpage from provided url
connHelper.downloadUrl(stringUrl);
}
Le problème que je rencontre est que je devrais en quelque sorte rappeler l’activité et qu’elle devrait pouvoir être définie dans la fonction "downloadUrl ()". Par exemple, lorsque le téléchargement est terminé, la fonction publique "handleWebpage (String data)" en activité est appelée avec la chaîne chargée en tant que paramètre.
J'ai fait quelques recherches sur Google et j'ai trouvé que je devais utiliser les interfaces pour obtenir cette fonctionnalité. Après avoir passé en revue quelques questions/réponses de stackoverflow similaires, je n’ai pas réussi à le faire fonctionner et je ne suis pas sûr d’avoir bien compris les interfaces: Comment puis-je passer la méthode en tant que paramètre en Java? Pour être honnête en utilisant Les classes anonymes sont nouvelles pour moi et je ne sais pas trop où ni comment appliquer les exemples de code dans le fil de discussion mentionné.
Ma question est donc de savoir comment je pourrais transmettre la fonction de rappel à ma classe de réseau et l'appeler une fois le téléchargement terminé. Où la déclaration d'interface va, implémente le mot-clé et ainsi de suite? S'il vous plaît noter que je suis débutant avec Java (ont d'autres antécédents de programmation cependant) donc je vous serais reconnaissant une explication tout au long :) :) Merci!
Utilisez une interface de rappel ou une classe abstraite avec des méthodes de rappel abstraites.
Exemple d'interface de rappel:
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...
}
}
La deuxième option est encore plus concise. Vous n'avez même pas besoin de définir une méthode abstraite pour "l'événement onDownloaded" car onPostExecute
fait exactement ce qui est nécessaire. Étendez simplement votre DownloadWebpageTask
avec une classe en ligne anonyme dans votre méthode 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);
}
//...
NO interface, NO lib, NO Java 8 nécessaires!
En utilisant simplement Callable<V>
À partir de 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();
}
}
Comment l'utiliser:
superMethod("Hello world", new Callable<Void>() {
public Void call() {
myParamMethod();
return null;
}
}
);
Où myParamMethod()
est notre méthode passée en paramètre (dans ce cas, methodParam
).
Oui, une interface est la meilleure façon à mon humble avis. Par exemple, GWT utilise le modèle de commande avec une interface comme celle-ci:
public interface Command{
void execute();
}
De cette façon, vous pouvez passer la fonction d'une méthode à une autre
public void foo(Command cmd){
...
cmd.execute();
}
public void bar(){
foo(new Command(){
void execute(){
//do something
}
});
}
La solution clé en main est que cela n’est pas possible en Java. Java n'accepte pas fonctions d'ordre supérieur . Il peut être obtenu à l'aide de quelques "astuces". Normalement, l'interface est celle utilisée comme vous l'avez vu. Regardez ici pour plus d'informations Vous pouvez également utiliser la réflexion pour y parvenir, mais cela est sujet aux erreurs.
L'utilisation d'interfaces peut être la meilleure solution dans Java Coding Architecture.
Mais passer un objet Runnable pourrait aussi bien fonctionner, et ce serait beaucoup plus pratique et flexible, je pense.
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 */
}
});
La réflexion n’est jamais une bonne idée car il est plus difficile de lire et de déboguer, mais si vous êtes sûr à 100% de ce que vous faites, vous pouvez simplement appeler quelque chose comme set_method (R.id.button_profile_edit, "toggle_edit") à laquelle attacher une méthode. une vue. Ceci est utile dans fragment, mais encore une fois, certaines personnes le considéreraient comme anti-modèle alors soyez prévenus.
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);
}