Estou procurando fazer um serviço que possa ser usado para fazer chamadas para uma API REST baseada na Web.
Basicamente, eu quero iniciar um serviço no init do aplicativo e desejo solicitar ao serviço que solicite um URL e retorne os resultados. Enquanto isso, quero poder exibir uma janela de progresso ou algo semelhante.
Atualmente, criei um serviço que usa IDL. Li em algum lugar que você realmente só precisa disso para comunicação entre aplicativos. Portanto, pense nelas, mas sem saber como fazer retornos de chamada sem ele. Além disso, quando acertei o post(Config.getURL("login"), values)
aplicativo, ele parou por um tempo (parece estranho - a idéia por trás de um serviço era que ele roda em um segmento diferente!)
Atualmente, tenho um serviço com métodos post e http dentro, alguns arquivos AIDL (para comunicação bidirecional), um ServiceManager que lida com iniciar, parar, vincular etc ao serviço e estou criando dinamicamente um manipulador com código específico para os retornos de chamada, conforme necessário.
Não quero que ninguém me dê uma base de código completa para trabalhar, mas alguns indicadores serão muito apreciados.
Código (na maior parte) 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) {...}
};
Alguns arquivos AIDL:
package com.something.android
oneway interface IRemoteServiceCallback {
void userLogIn(String result);
}
e
package com.something.android
import com.something.android.IRemoteServiceCallback;
interface IRestfulService {
void doLogin(in String username, in String password);
void registerCallback(IRemoteServiceCallback cb);
}
e o gerente de serviço:
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;
}
}
Serviço init e bind:
// this I'm calling on app onCreate
servicemanager = new ServiceManager(this);
servicemanager.startService();
servicemanager.bindService();
application = (ApplicationState)this.getApplication();
application.setServiceManager(servicemanager);
chamada de função de serviço:
// 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);
}
}
};
fonte
Respostas:
Se o seu serviço fará parte do seu aplicativo, você estará tornando-o muito mais complexo do que precisa. Como você tem um caso de uso simples de obter alguns dados de um serviço da Web RESTful, deve procurar em ResultReceiver e IntentService .
Esse padrão Service + ResultReceiver funciona iniciando ou vinculando o serviço com startService () quando você deseja executar alguma ação. Você pode especificar a operação para executar e transmitir seu ResultReceiver (a atividade) pelos extras no Intent.
No serviço você implementa onHandleIntent para executar a operação especificada no Intent. Quando a operação é concluída, você utiliza o ResultReceiver transmitido para enviar uma mensagem de volta à Atividade, na qual o ponto emReceiveResult será chamado.
Por exemplo, você deseja extrair alguns dados do seu serviço da Web.
Sei que você mencionou que não queria uma base de código, mas o aplicativo Google I / O 2010 de código aberto usa um serviço dessa maneira que estou descrevendo.
Atualizado para adicionar código de exemplo:
A atividade.
O serviço:
Extensão ResultReceiver - editada para implementar MyResultReceiver.Receiver
fonte
stopSelf
se subclassificarIntentService
porque, se o fizer, perderá solicitações pendentes para o mesmoIntentService
.IntentService
se matará quando a tarefa estiver concluída, por issothis.stopSelf()
é desnecessário.O desenvolvimento de aplicativos clientes REST Android tem sido um recurso incrível para mim. O alto-falante não mostra nenhum código, ele apenas repassa as considerações e técnicas de design para montar um Rest Api sólido no Android. Se você é um podcast ou não, recomendo ouvir este pelo menos uma vez, mas, pessoalmente, já o ouvi quatro ou cinco vezes até agora e provavelmente vou ouvi-lo novamente.
Desenvolvendo aplicativos cliente REST Android
Autor: Virgil Dobjanschi
Descrição:
Esta sessão apresentará considerações arquitetônicas para o desenvolvimento de aplicativos RESTful na plataforma Android. Ele se concentra nos padrões de design, integração de plataforma e problemas de desempenho específicos da plataforma Android.
E há tantas considerações que eu realmente não tinha feito na primeira versão da minha API que tive que refatorar
fonte
Não, você precisa criar um encadeamento, um serviço local é executado no encadeamento da interface do usuário por padrão.
fonte
Eu sei que @Martyn não quer código completo, mas acho que esta anotação é boa para esta pergunta:
10 aplicativos Android de código aberto que todos os desenvolvedores Android devem analisar
O Foursquared para Android é de código aberto e possui um padrão de código interessante que interage com a API REST do quadrangular.
fonte
Eu recomendo o Retrofit do cliente REST .
Eu achei este post de blog bem escrito extremamente útil, mas também contém um código de exemplo simples. O autor usa o Retrofit para fazer chamadas de rede e o Otto para implementar um padrão de barramento de dados:
http://www.mdswanson.com/blog/2014/04/07/durable-android-rest-clients.html
fonte
Só queria apontar todos vocês na direção de uma classe autônoma que eu rolei que incorpora toda a funcionalidade.
http://github.com/StlTenny/RestService
Ele executa a solicitação como não-bloqueante e retorna os resultados em um manipulador fácil de implementar. Mesmo vem com um exemplo de implementação.
fonte
Digamos que eu queira iniciar o serviço em um evento - onItemClicked () de um botão. O mecanismo Receiver não funcionaria nesse caso porque: -
a) Passei o Receiver para o serviço (como no Intent extra) de onItemClicked ()
b) A atividade se move em segundo plano. Em onPause (), defino a referência do receptor no ResultReceiver como null para evitar vazamento da atividade.
c) A atividade é destruída.
d) A atividade é criada novamente. No entanto, neste ponto, o Serviço não poderá fazer um retorno de chamada para a Atividade, pois a referência do receptor será perdida.
O mecanismo de uma transmissão limitada ou de um PendingIntent parece ser mais útil nesses cenários - consulte Notificar atividade do serviço
fonte
Observe que a solução da Robby Pond está faltando de alguma forma: dessa maneira, você só permite fazer uma chamada de API por vez, pois o IntentService lida apenas com uma intenção de cada vez. Muitas vezes, você deseja executar chamadas paralelas da API. Se você quiser fazer isso, precisará estender o Serviço em vez do IntentService e criar seu próprio encadeamento.
fonte
Nesse caso, é melhor usar o asynctask, que é executado em um thread diferente e retorna o resultado de volta ao thread da interface do usuário após a conclusão.
fonte
Há outra abordagem aqui, que basicamente ajuda você a esquecer todo o gerenciamento das solicitações. Ele é baseado em um método de fila assíncrona e em uma resposta baseada em chamada / retorno de chamada. A principal vantagem é que, usando esse método, você poderá tornar todo o processo (solicitar, obter e analisar respostas, sabe para db) completamente transparente para você. Depois de obter o código de resposta, o trabalho já está concluído. Depois disso, você só precisa fazer uma chamada para o seu banco de dados e pronto. Também ajuda na problemática do que acontece quando sua atividade não está ativa. O que acontecerá aqui é que você terá todos os seus dados salvos no banco de dados local, mas a resposta não será processada pela sua atividade, é o caminho ideal.
Eu escrevi sobre uma abordagem geral aqui http://ugiagonzalez.com/2012/07/02/theres-life-after-asynctasks-in-android/
Colocarei um exemplo de código específico nas próximas postagens. Espero que ajude, sinta-se à vontade para entrar em contato comigo para compartilhar a abordagem e solucionar possíveis dúvidas ou problemas.
fonte
Robby fornece uma ótima resposta, embora eu possa ver você ainda procurando mais informações. Eu implementei api REST chama o caminho fácil, mas errado. Não foi até assistir a este vídeo de E / S do Google que eu entendi onde errei. Não é tão simples quanto montar um AsyncTask com uma chamada get / put HttpUrlConnection.
fonte