Szukam usługi, której mogę używać do nawiązywania połączeń z internetowym interfejsem API REST.
Zasadniczo chcę uruchomić usługę w aplikacji init, a następnie chcę móc poprosić tę usługę o podanie adresu URL i zwrócenie wyników. W międzyczasie chcę być w stanie wyświetlić okno postępu lub coś podobnego.
Stworzyłem usługę, która obecnie korzysta z IDL, przeczytałem gdzieś, że naprawdę potrzebujesz jej tylko do komunikacji między aplikacjami, więc pomyśl, że trzeba się rozebrać, ale nie wiesz, jak wykonać wywołania zwrotne bez tego. Również po uderzeniu post(Config.getURL("login"), values)
aplikacja wydaje się na chwilę wstrzymywać (wydaje się dziwna - pomyślałam, że za usługą działa inny wątek!)
Obecnie mam usługę z pocztą i metodami HTTP, kilka plików AIDL (do komunikacji dwukierunkowej), ServiceManager, który zajmuje się uruchamianiem, zatrzymywaniem, wiązaniem itp. Z usługą i dynamicznie tworzę moduł obsługi z określonym kodem w razie potrzeby oddzwaniania.
Nie chcę, aby ktokolwiek dał mi pełną bazę kodu do pracy, ale niektóre wskazówki byłyby bardzo mile widziane.
Kod w (przeważnie) pełny:
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) {...}
};
Kilka plików AIDL:
package com.something.android
oneway interface IRemoteServiceCallback {
void userLogIn(String result);
}
i
package com.something.android
import com.something.android.IRemoteServiceCallback;
interface IRestfulService {
void doLogin(in String username, in String password);
void registerCallback(IRemoteServiceCallback cb);
}
i kierownik serwisu:
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;
}
}
Usługa inicjowania i wiązania:
// this I'm calling on app onCreate
servicemanager = new ServiceManager(this);
servicemanager.startService();
servicemanager.bindService();
application = (ApplicationState)this.getApplication();
application.setServiceManager(servicemanager);
wywołanie funkcji serwisowej:
// 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);
}
}
};
źródło
Odpowiedzi:
Jeśli twoja usługa będzie częścią twojej aplikacji, czynisz ją znacznie bardziej złożoną, niż powinna. Ponieważ masz prosty przypadek użycia niektórych danych z usługi sieci Web RESTful, powinieneś zajrzeć do ResultReceiver i IntentService .
Ten wzorzec Service + ResultReceiver działa poprzez uruchomienie lub powiązanie z usługą za pomocą startService (), gdy chcesz wykonać jakąś akcję. Możesz określić operację do wykonania i przekazać w ResultReceiver (działanie) poprzez dodatki w celu.
W usłudze implementujesz onHandleIntent, aby wykonać operację określoną w parametrze Intent. Po zakończeniu operacji używasz przekazanego w ResultReceiver, aby wysłać wiadomość z powrotem do działania, w którym to momencie zostanie wywołany onReceiveResult .
Na przykład chcesz pobrać niektóre dane z usługi sieci Web.
Wiem, że wspomniałeś, że nie chcesz bazy kodu, ale aplikacja Google I / O 2010 typu open source korzysta z usługi w taki sposób, który opisuję.
Zaktualizowano, aby dodać przykładowy kod:
Aktywność.
Obsługa:
Rozszerzenie ResultReceiver - edytowane w celu wdrożenia MyResultReceiver.Receiver
źródło
stopSelf
, jeśli podklasyIntentService
bo jeśli to zrobisz, stracisz żadnych oczekujących żądań do samIntentService
.IntentService
zabije się, gdy zadanie zostanie ukończone, więc niethis.stopSelf()
jest konieczne.Tworzenie aplikacji klienckich REST dla Androida było dla mnie niesamowitym zasobem. Mówca nie pokazuje żadnego kodu, po prostu omawia rozważania projektowe i techniki w tworzeniu solidnego Rest Api w Androidzie. Jeśli jesteś osobą z podcastu, czy nie, polecam posłuchać tego co najmniej jednego, ale osobiście słuchałem go jak dotąd 4 lub 5 razy i prawdopodobnie będę go ponownie słuchał.
Tworzenie aplikacji klienckich REST dla Androida
Autor: Virgil Dobjanschi
Opis:
W tej sesji zostaną przedstawione rozważania architektoniczne dotyczące tworzenia aplikacji RESTful na platformie Android. Koncentruje się na wzorcach projektowych, integracji platformy i problemach wydajnościowych specyficznych dla platformy Android.
I jest tak wiele uwag, których tak naprawdę nie wziąłem pod uwagę w pierwszej wersji mojego interfejsu API, które musiałem zreformować
źródło
Nie, musisz samodzielnie utworzyć wątek, usługa lokalna domyślnie działa w wątku interfejsu użytkownika.
źródło
Wiem, że @Martyn nie chce pełnego kodu, ale myślę, że ta adnotacja jest dobra na to pytanie:
10 otwartych aplikacji na Androida, na które musi spojrzeć każdy programista Androida
Foursquared dla Androida jest oprogramowaniem typu open source i ma interesujący wzorzec kodu współdziałający z interfejsem API REST foursquare.
źródło
Gorąco polecam modernizację klienta REST .
Uważam, że ten dobrze napisany post na blogu jest niezwykle pomocny, zawiera również prosty przykładowy kod. Autor używa Retrofit do wykonywania połączeń sieciowych, a Otto do implementacji wzorca magistrali danych:
http://www.mdswanson.com/blog/2014/04/07/durable-android-rest-clients.html
źródło
Chciałem tylko skierować was wszystkich w stronę niezależnej klasy, którą rzuciłem, która zawiera wszystkie funkcje.
http://github.com/StlTenny/RestService
Wykonuje żądanie jako nieblokujące i zwraca wyniki w łatwej do wdrożenia procedurze obsługi. Nawet pochodzi z przykładową implementacją.
źródło
Powiedzmy, że chcę uruchomić usługę w przypadku zdarzenia - onItemClicked () przycisku. W takim przypadku mechanizm odbiornika nie działałby, ponieważ:
a) przekazałem odbiornik do usługi (jak w przypadku zamiaru dodatkowego) z funkcji onItemClicked ()
b) aktywność przenosi się w tło. W funkcji onPause () ustawiam wartość referencyjną odbiornika w ResultReceiver na null, aby uniknąć wycieku działania.
c) Aktywność ulega zniszczeniu.
d) Aktywność zostanie utworzona ponownie. Jednak w tym momencie usługa nie będzie mogła oddzwonić do działania, ponieważ utracono referencję odbiorcy.
Mechanizm ograniczonej emisji lub PendingIntent wydaje się bardziej przydatny w takich scenariuszach - patrz Powiadomienie o aktywności z usługi
źródło
Zauważ, że w jakiś sposób brakuje rozwiązania z Robby Pond: w ten sposób zezwalasz tylko na jedno wywołanie API naraz, ponieważ usługa IntentService obsługuje tylko jedną intencję na raz. Często chcesz wykonywać równoległe połączenia API. Jeśli chcesz to zrobić, musisz rozszerzyć usługę zamiast IntentService i utworzyć własny wątek.
źródło
W takim przypadku lepiej jest użyć asynctask, który działa na innym wątku i po zakończeniu zwraca wynik z powrotem do wątku interfejsu użytkownika.
źródło
Jest tutaj inne podejście, które zasadniczo pomaga zapomnieć o całym zarządzaniu żądaniami. Opiera się na metodzie kolejki asynchronicznej i odpowiedzi opartej na wywołaniu zwrotnym / wywołaniu zwrotnym. Główną zaletą jest to, że dzięki tej metodzie będziesz w stanie uczynić cały proces (żądanie, odpowiedź i parsowanie odpowiedzi, sabe do db) całkowicie przejrzysty. Po otrzymaniu kodu odpowiedzi praca jest już zakończona. Następnie wystarczy zadzwonić do bazy danych i gotowe. Pomaga również w problematyce tego, co dzieje się, gdy twoja aktywność nie jest aktywna. To, co się tutaj stanie, to zapisanie wszystkich danych w lokalnej bazie danych, ale odpowiedź nie zostanie przetworzona przez Twoją aktywność, to idealny sposób.
Pisałem o ogólnym podejściu tutaj http://ugiagonzalez.com/2012/07/02/theres-life-after-asynctasks-in-android/
W nadchodzących postach wstawię konkretny przykładowy kod. Mam nadzieję, że to pomoże, skontaktuj się ze mną w sprawie podzielenia się tym podejściem i rozwiązania potencjalnych wątpliwości lub problemów.
źródło
Robby zapewnia świetną odpowiedź, choć widzę, że wciąż szukasz więcej informacji. Zaimplementowałem wywołania interfejsu API REST w łatwy, ALE niewłaściwy sposób. Dopiero po obejrzeniu tego wideo we / wy Google zrozumiałem, gdzie popełniłem błąd. Nie jest to tak proste, jak połączenie AsyncTask z wywołaniem HttpUrlConnection get / put.
źródło