Estoy buscando hacer un servicio que pueda usar para hacer llamadas a una API REST basada en la web.
Básicamente, quiero iniciar un servicio en la aplicación init, luego quiero poder pedirle a ese servicio que solicite una URL y devuelva los resultados. Mientras tanto, quiero poder mostrar una ventana de progreso o algo similar.
Actualmente he creado un servicio que usa IDL, he leído en alguna parte que solo necesitas esto para la comunicación entre aplicaciones, así que piensa que estas necesidades se eliminan pero no estoy seguro de cómo hacer devoluciones de llamada sin él. Además, cuando llego a la post(Config.getURL("login"), values)
aplicación, la aplicación parece detenerse por un tiempo (parece extraño, ¡pensé que la idea detrás de un servicio era que se ejecuta en un hilo diferente!)
Actualmente tengo un servicio con publicación y obtengo métodos http dentro, un par de archivos AIDL (para comunicación bidireccional), un ServiceManager que se encarga de iniciar, detener, vincular, etc. al servicio y estoy creando dinámicamente un controlador con código específico para las devoluciones de llamada según sea necesario.
No quiero que nadie me dé una base de código completa para trabajar, pero algunos punteros serían muy apreciados.
Código en (en su mayoría) completo:
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) {...}
};
Un par de archivos AIDL:
package com.something.android
oneway interface IRemoteServiceCallback {
void userLogIn(String result);
}
y
package com.something.android
import com.something.android.IRemoteServiceCallback;
interface IRestfulService {
void doLogin(in String username, in String password);
void registerCallback(IRemoteServiceCallback cb);
}
y el gerente de servicio:
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;
}
}
Servicio init y bind:
// this I'm calling on app onCreate
servicemanager = new ServiceManager(this);
servicemanager.startService();
servicemanager.bindService();
application = (ApplicationState)this.getApplication();
application.setServiceManager(servicemanager);
llamada a la función de servicio:
// 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);
}
}
};
fuente
Respuestas:
Si su servicio va a ser parte de su aplicación, entonces lo está haciendo mucho más complejo de lo que debe ser. Como tiene un caso de uso simple de obtener algunos datos de un servicio web RESTful, debe buscar ResultReceiver e IntentService .
Este patrón de Service + ResultReceiver funciona iniciando o vinculando el servicio con startService () cuando desea realizar alguna acción. Puede especificar la operación a realizar y pasar su ResultReceiver (la actividad) a través de los extras en la Intención.
En el servicio, implemente onHandleIntent para realizar la operación que se especifica en la intención. Cuando se completa la operación, utiliza el pasado en ResultReceiver para enviar un mensaje de regreso a la Actividad, en cuyo punto se llamará a OnReceiveResult .
Entonces, por ejemplo, desea extraer algunos datos de su servicio web.
Sé que mencionaste que no querías una base de código, pero la aplicación de código abierto Google I / O 2010 utiliza un servicio de la manera que describo.
Actualizado para agregar código de muestra:
La actividad.
El servicio:
Extensión ResultReceiver: editada para implementar MyResultReceiver.Receiver
fuente
stopSelf
si subclaseIntentService
porque si lo hace, perderá todas las solicitudes pendientes a la mismaIntentService
.IntentService
se suicidará cuando se complete la tarea, por lo quethis.stopSelf()
es innecesario.El desarrollo de aplicaciones cliente REST de Android ha sido un recurso increíble para mí. El orador no muestra ningún código, solo repasa las consideraciones de diseño y las técnicas para armar una Rest Api sólida en Android. Si eres un podcast un poco o no, te recomiendo que escuches este al menos una vez, pero personalmente lo he escuchado 4 o cinco veces hasta ahora y probablemente lo volveré a escuchar.
Desarrollo de aplicaciones cliente REST de Android
Autor: Virgil Dobjanschi
Descripción:
Esta sesión presentará consideraciones arquitectónicas para desarrollar aplicaciones RESTful en la plataforma Android. Se centra en patrones de diseño, integración de plataformas y problemas de rendimiento específicos de la plataforma Android.
Y hay tantas consideraciones que realmente no había hecho en la primera versión de mi API que tuve que refactorizar
fuente
No, tiene que crear un subproceso usted mismo, un servicio local se ejecuta en el subproceso de la interfaz de usuario de forma predeterminada.
fuente
Sé que @Martyn no quiere el código completo, pero creo que esta anotación es buena para esta pregunta:
10 aplicaciones de Android de código abierto que todo desarrollador de Android debe analizar
Foursquared para Android es de código abierto y tiene un patrón de código interesante que interactúa con la API REST cuadrangular.
fuente
Recomiendo encarecidamente el cliente REST Retrofit .
He encontrado esta publicación de blog bien escrita extremadamente útil, también contiene un código de ejemplo simple. El autor usa Retrofit para hacer las llamadas de red y Otto para implementar un patrón de bus de datos:
http://www.mdswanson.com/blog/2014/04/07/durable-android-rest-clients.html
fuente
Solo quería señalarles a todos en la dirección de una clase independiente que hice rodar que incorpora toda la funcionalidad.
http://github.com/StlTenny/RestService
Ejecuta la solicitud como no bloqueante y devuelve los resultados en un controlador fácil de implementar. Incluso viene con un ejemplo de implementación.
fuente
Digamos que quiero iniciar el servicio en un evento: onItemClicked () de un botón. El mecanismo del receptor no funcionaría en ese caso porque: -
a) pasé el receptor al servicio (como en Intent extra) desde onItemClicked ()
b) La actividad se mueve al fondo. En onPause () configuré la referencia del receptor dentro del ResultReceiver como nula para evitar filtrar la Actividad.
c) La actividad se destruye.
d) La actividad se vuelve a crear. Sin embargo, en este punto, el Servicio no podrá realizar una devolución de llamada a la Actividad ya que se pierde esa referencia del receptor.
El mecanismo de una transmisión limitada o un PendingIntent parece ser más útil en tales escenarios; consulte Notificar actividad del servicio
fuente
Tenga en cuenta que la solución de Robby Pond es de alguna manera faltante: de esta manera solo permite hacer una llamada API a la vez, ya que IntentService solo maneja una intención a la vez. A menudo, desea realizar llamadas de API paralelas. Si desea hacer esto, debe extender el Servicio en lugar de IntentService y crear su propio hilo.
fuente
En este caso, es mejor usar asynctask, que se ejecuta en un subproceso diferente y devuelve el resultado al subproceso ui al finalizar.
fuente
Aquí hay otro enfoque que básicamente le ayuda a olvidarse de toda la gestión de las solicitudes. Se basa en un método de cola asíncrona y una respuesta basada en llamada / devolución de llamada. La principal ventaja es que al usar este método podrá hacer que todo el proceso (solicitud, obtener y analizar la respuesta, sabe a db) sea completamente transparente para usted. Una vez que obtenga el código de respuesta, el trabajo ya está hecho. Después de eso, solo necesita hacer una llamada a su base de datos y listo. También ayuda con la problemática de lo que sucede cuando su actividad no está activa. Lo que sucederá aquí es que tendrá todos sus datos guardados en su base de datos local, pero la respuesta no será procesada por su actividad, esa es la forma ideal.
Escribí sobre un enfoque general aquí http://ugiagonzalez.com/2012/07/02/theres-life-after-asynctasks-in-android/
Pondré un código de muestra específico en las próximas publicaciones. Espero que ayude, no dude en ponerse en contacto conmigo para compartir el enfoque y resolver posibles dudas o problemas.
fuente
Robby ofrece una excelente respuesta, aunque puedo ver que aún buscas más información. Implementé llamadas REST api de manera fácil PERO incorrecta. No fue hasta que vi este video de Google I / O que entendí dónde me equivoqué. No es tan simple como armar una AsyncTask con una llamada get / put HttpUrlConnection.
fuente