Je cherche à créer un service que je peux utiliser pour passer des appels à une API REST basée sur le Web.
Fondamentalement, je veux démarrer un service sur l'application init puis je veux pouvoir demander à ce service de demander une URL et de renvoyer les résultats. En attendant, je veux pouvoir afficher une fenêtre de progression ou quelque chose de similaire.
J'ai actuellement créé un service qui utilise IDL, j'ai lu quelque part que vous n'en avez vraiment besoin que pour la communication inter-applications, alors pensez que ces besoins doivent être supprimés, mais ne savez pas comment faire des rappels sans. De plus, lorsque je clique sur l' post(Config.getURL("login"), values)
application, l'application semble faire une pause pendant un certain temps (cela semble étrange - je pensais que l'idée derrière un service était qu'il fonctionnait sur un thread différent!)
Actuellement, j'ai un service avec des méthodes post et get http à l'intérieur, quelques fichiers AIDL (pour la communication bidirectionnelle), un ServiceManager qui traite du démarrage, de l'arrêt, de la liaison, etc. au service et je crée dynamiquement un gestionnaire avec du code spécifique pour les rappels au besoin.
Je ne veux pas que quelqu'un me donne une base de code complète sur laquelle travailler, mais certains pointeurs seraient grandement appréciés.
Code complet (principalement):
public class RestfulAPIService extends Service {
final RemoteCallbackList<IRemoteServiceCallback> mCallbacks = new RemoteCallbackList<IRemoteServiceCallback>();
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
}
public IBinder onBind(Intent intent) {
return binder;
}
public void onCreate() {
super.onCreate();
}
public void onDestroy() {
super.onDestroy();
mCallbacks.kill();
}
private final IRestfulService.Stub binder = new IRestfulService.Stub() {
public void doLogin(String username, String password) {
Message msg = new Message();
Bundle data = new Bundle();
HashMap<String, String> values = new HashMap<String, String>();
values.put("username", username);
values.put("password", password);
String result = post(Config.getURL("login"), values);
data.putString("response", result);
msg.setData(data);
msg.what = Config.ACTION_LOGIN;
mHandler.sendMessage(msg);
}
public void registerCallback(IRemoteServiceCallback cb) {
if (cb != null)
mCallbacks.register(cb);
}
};
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
// Broadcast to all clients the new value.
final int N = mCallbacks.beginBroadcast();
for (int i = 0; i < N; i++) {
try {
switch (msg.what) {
case Config.ACTION_LOGIN:
mCallbacks.getBroadcastItem(i).userLogIn( msg.getData().getString("response"));
break;
default:
super.handleMessage(msg);
return;
}
} catch (RemoteException e) {
}
}
mCallbacks.finishBroadcast();
}
public String post(String url, HashMap<String, String> namePairs) {...}
public String get(String url) {...}
};
Quelques fichiers AIDL:
package com.something.android
oneway interface IRemoteServiceCallback {
void userLogIn(String result);
}
et
package com.something.android
import com.something.android.IRemoteServiceCallback;
interface IRestfulService {
void doLogin(in String username, in String password);
void registerCallback(IRemoteServiceCallback cb);
}
et le chef de service:
public class ServiceManager {
final RemoteCallbackList<IRemoteServiceCallback> mCallbacks = new RemoteCallbackList<IRemoteServiceCallback>();
public IRestfulService restfulService;
private RestfulServiceConnection conn;
private boolean started = false;
private Context context;
public ServiceManager(Context context) {
this.context = context;
}
public void startService() {
if (started) {
Toast.makeText(context, "Service already started", Toast.LENGTH_SHORT).show();
} else {
Intent i = new Intent();
i.setClassName("com.something.android", "com.something.android.RestfulAPIService");
context.startService(i);
started = true;
}
}
public void stopService() {
if (!started) {
Toast.makeText(context, "Service not yet started", Toast.LENGTH_SHORT).show();
} else {
Intent i = new Intent();
i.setClassName("com.something.android", "com.something.android.RestfulAPIService");
context.stopService(i);
started = false;
}
}
public void bindService() {
if (conn == null) {
conn = new RestfulServiceConnection();
Intent i = new Intent();
i.setClassName("com.something.android", "com.something.android.RestfulAPIService");
context.bindService(i, conn, Context.BIND_AUTO_CREATE);
} else {
Toast.makeText(context, "Cannot bind - service already bound", Toast.LENGTH_SHORT).show();
}
}
protected void destroy() {
releaseService();
}
private void releaseService() {
if (conn != null) {
context.unbindService(conn);
conn = null;
Log.d(LOG_TAG, "unbindService()");
} else {
Toast.makeText(context, "Cannot unbind - service not bound", Toast.LENGTH_SHORT).show();
}
}
class RestfulServiceConnection implements ServiceConnection {
public void onServiceConnected(ComponentName className, IBinder boundService) {
restfulService = IRestfulService.Stub.asInterface((IBinder) boundService);
try {
restfulService.registerCallback(mCallback);
} catch (RemoteException e) {}
}
public void onServiceDisconnected(ComponentName className) {
restfulService = null;
}
};
private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
public void userLogIn(String result) throws RemoteException {
mHandler.sendMessage(mHandler.obtainMessage(Config.ACTION_LOGIN, result));
}
};
private Handler mHandler;
public void setHandler(Handler handler) {
mHandler = handler;
}
}
Service init et bind:
// this I'm calling on app onCreate
servicemanager = new ServiceManager(this);
servicemanager.startService();
servicemanager.bindService();
application = (ApplicationState)this.getApplication();
application.setServiceManager(servicemanager);
appel de fonction de service:
// this lot i'm calling as required - in this example for login
progressDialog = new ProgressDialog(Login.this);
progressDialog.setMessage("Logging you in...");
progressDialog.show();
application = (ApplicationState) getApplication();
servicemanager = application.getServiceManager();
servicemanager.setHandler(mHandler);
try {
servicemanager.restfulService.doLogin(args[0], args[1]);
} catch (RemoteException e) {
e.printStackTrace();
}
...later in the same file...
Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case Config.ACTION_LOGIN:
if (progressDialog.isShowing()) {
progressDialog.dismiss();
}
try {
...process login results...
}
} catch (JSONException e) {
Log.e("JSON", "There was an error parsing the JSON", e);
}
break;
default:
super.handleMessage(msg);
}
}
};
la source
Réponses:
Si votre service va faire partie de votre application, vous le rendez beaucoup plus complexe qu'il ne devrait l'être. Étant donné que vous avez un cas d'utilisation simple pour obtenir des données à partir d'un service Web RESTful, vous devez examiner ResultReceiver et IntentService .
Ce modèle Service + ResultReceiver fonctionne en démarrant ou en se liant au service avec startService () lorsque vous souhaitez effectuer une action. Vous pouvez spécifier l'opération à effectuer et transmettre votre ResultReceiver (l'activité) via les extras de l'intention.
Dans le service, vous implémentez onHandleIntent pour effectuer l'opération spécifiée dans l'intention. Lorsque l'opération est terminée, vous utilisez le passé dans ResultReceiver pour renvoyer un message à l'activité à quel point onReceiveResult sera appelé.
Ainsi, par exemple, vous souhaitez extraire certaines données de votre service Web.
Je sais que vous avez mentionné que vous ne vouliez pas de base de code, mais l'application open source Google I / O 2010 utilise un service de la manière que je décris.
Mis à jour pour ajouter un exemple de code:
L'activité.
Le service:
Extension ResultReceiver - modifiée sur le point d'implémenter MyResultReceiver.Receiver
la source
stopSelf
si vous sousIntentService
- classe car si vous le faites, vous perdrez toutes les demandes en attente à la mêmeIntentService
.IntentService
se tuera une fois la tâche terminée, doncthis.stopSelf()
inutile.Le développement d'applications clientes Android REST a été une ressource formidable pour moi. Le haut-parleur ne montre aucun code, il passe simplement en revue les considérations et les techniques de conception pour assembler un Rest Api solide comme le roc dans Android. Si vous êtes un podcast un peu ou non, je recommanderais de donner à celui-ci au moins une écoute mais, personnellement, je l'ai écouté quatre ou cinq fois jusqu'à présent et je vais probablement l'écouter à nouveau.
Développement d'applications client Android REST
Auteur: Virgil Dobjanschi
Description:
Cette session présentera des considérations architecturales pour le développement d'applications RESTful sur la plate-forme Android. Il se concentre sur les modèles de conception, l'intégration de la plate-forme et les problèmes de performances spécifiques à la plate-forme Android.
Et il y a tellement de considérations que je n'avais vraiment pas faites dans la première version de mon api que j'ai dû refactoriser
la source
Non, vous devez créer un thread vous-même, un service local s'exécute dans le thread d'interface utilisateur par défaut.
la source
Je sais que @Martyn ne veut pas de code complet, mais je pense que cette annotation est bonne pour cette question:
10 applications Android Open Source que chaque développeur Android doit étudier
Foursquared pour Android est open-source et possède un modèle de code intéressant interagissant avec l'API REST de foursquare.
la source
Je recommanderais fortement le client REST Retrofit .
J'ai trouvé cet article de blog bien écrit extrêmement utile, il contient également un exemple de code simple. L'auteur utilise Retrofit pour effectuer les appels réseau et Otto pour implémenter un modèle de bus de données:
http://www.mdswanson.com/blog/2014/04/07/durable-android-rest-clients.html
la source
Je voulais juste vous indiquer tous dans la direction d'une classe autonome que j'ai roulée qui intègre toutes les fonctionnalités.
http://github.com/StlTenny/RestService
Il exécute la demande comme non bloquante et renvoie les résultats dans un gestionnaire facile à implémenter. Vient même avec un exemple d'implémentation.
la source
Disons que je veux démarrer le service sur un événement - onItemClicked () d'un bouton. Le mécanisme du récepteur ne fonctionnerait pas dans ce cas car: -
a) j'ai transmis le récepteur au service (comme dans Intent extra) à partir de onItemClicked ()
b) l'activité se déplace en arrière-plan. Dans onPause (), je définis la référence du récepteur dans le ResultReceiver sur null pour éviter de fuir l'activité.
c) L'activité est détruite.
d) L'activité est à nouveau créée. Cependant, à ce stade, le service ne sera pas en mesure d'effectuer un rappel de l'activité car cette référence de récepteur est perdue.
Le mécanisme d'une diffusion limitée ou d'un PendingIntent semble être plus utile dans de tels scénarios - se référer à Notifier l'activité du service
la source
Notez que la solution de Robby Pond fait en quelque sorte défaut: de cette façon, vous n'autorisez qu'un seul appel api à la fois car l'IntentService ne gère qu'une seule intention à la fois. Souvent, vous souhaitez effectuer des appels API parallèles. Si vous voulez faire cela, vous devez étendre le service au lieu de IntentService et créer votre propre thread.
la source
Dans ce cas, il vaut mieux utiliser asynctask, qui s'exécute sur un thread différent et renvoie le résultat au thread ui à la fin.
la source
Il existe une autre approche ici qui vous aide essentiellement à oublier toute la gestion des demandes. Il est basé sur une méthode de file d'attente asynchrone et une réponse basée sur l'appel / le rappel. Le principal avantage est qu'en utilisant cette méthode, vous serez en mesure de rendre l'ensemble du processus (demande, obtenir et analyser la réponse, sabe à db) complètement transparent pour vous. Une fois que vous obtenez le code de réponse, le travail est déjà terminé. Après cela, il vous suffit d'appeler votre base de données et vous avez terminé. Cela aide également à résoudre la problématique de ce qui se passe lorsque votre activité n'est pas active. Ce qui se passera ici, c'est que vous aurez toutes vos données enregistrées dans votre base de données locale, mais la réponse ne sera pas traitée par votre activité, c'est le moyen idéal.
J'ai écrit ici sur une approche générale http://ugiagonzalez.com/2012/07/02/theres-life-after-asynctasks-in-android/
Je mettrai un exemple de code spécifique dans les prochains articles. J'espère que cela vous aide, n'hésitez pas à me contacter pour partager l'approche et résoudre les doutes ou problèmes potentiels.
la source
Robby fournit une excellente réponse, bien que je puisse vous voir toujours à la recherche de plus d'informations. J'ai implémenté les appels api REST de la manière simple MAIS dans le mauvais sens. Ce n'est qu'en regardant cette vidéo Google I / O que j'ai compris où je me suis trompé. Ce n'est pas aussi simple que d'assembler une AsyncTask avec un appel get / put HttpUrlConnection.
la source