Usługa Restful API

Szukam usługi, której mogę użyć do wykonywania połączeń z internetowym API REST.

Zasadniczo chcę uruchomić usługę na INIT aplikacji, 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 obecnie usługę, która korzysta z IDL, czytałem gdzieś, że naprawdę potrzebujesz tego tylko do komunikacji między aplikacjami, więc myślę, że te potrzeby ale nie jesteś pewien, jak bez niego wykonywać połączenia zwrotne. Również, gdy trafiłem post(Config.getURL("login"), values) aplikacja wydaje się pauzować na chwilę (wydaje się dziwne - myślałem, że idea usługi było to, że działa na innym wątku!)

Obecnie mam usługę z metodami post I get http wewnątrz, kilka plików AIDL( do dwukierunkowej komunikacji), ServiceManager, który zajmuje się uruchamianiem, zatrzymywaniem, wiązaniem itp. z usługą i dynamicznie tworzę Handler z określonym kodem dla wywołań zwrotnych jako potrzebne.

Nie chcę, aby ktokolwiek dawał mi kompletną bazę kodu do pracy, ale niektóre wskazówki byłyby bardzo mile widziane.

Kod w (najczęściej) pełnym:

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 init i bind:

// 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 Usługi:

// 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);
        }

    }

};
Author: Michael Currie, 2010-07-07

11 answers

Jeśli Twoja usługa ma być częścią twojej aplikacji, robisz ją bardziej złożoną, niż musi być. Ponieważ masz prosty przypadek użycia pobierania niektórych danych z usługi internetowej RESTful, powinieneś przyjrzeć się 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 przekazania w swoim ResultReceiver (działanie) poprzez dodatki w intencji.

W usłudze implementujesz onHandleIntent aby wykonać operację określoną w intencji. Gdy operacja zostanie zakończona, należy użyć przekazanego w ResultReceiver do wysłać wiadomość z powrotem do działania, w którym to momencie zostanie wywołany onReceiveResult.

Więc na przykład, chcesz pobrać niektóre dane z usługi internetowej.

  1. tworzysz intencję i wywołujesz startService.
  2. operacja w usłudze rozpoczyna się i wysyła do działania komunikat informujący, że została uruchomiona
  3. działanie przetwarza wiadomość i pokazuje postęp.
  4. usługa kończy operację i wysyła niektóre dane z powrotem do Twojej aktywności.
  5. Twoja aktywność przetwarza Dane i umieszcza je w widoku listy
  6. usługa wysyła Ci wiadomość, że jest zrobione, i zabija się.
  7. działanie otrzymuje komunikat finish i ukrywa okno postępu.

Wiem, że wspomniałeś, że nie chcesz bazy kodu, ale aplikacja open source Google I / O 2010 używa usługi w ten sposób, który opisuję.

Zaktualizowano, aby dodać przykładowy kod:

Aktywność.

public class HomeActivity extends Activity implements MyResultReceiver.Receiver {

    public MyResultReceiver mReceiver;

    public void onCreate(Bundle savedInstanceState) {
        mReceiver = new MyResultReceiver(new Handler());
        mReceiver.setReceiver(this);
        ...
        final Intent intent = new Intent(Intent.ACTION_SYNC, null, this, QueryService.class);
        intent.putExtra("receiver", mReceiver);
        intent.putExtra("command", "query");
        startService(intent);
    }

    public void onPause() {
        mReceiver.setReceiver(null); // clear receiver so no leaks.
    }

    public void onReceiveResult(int resultCode, Bundle resultData) {
        switch (resultCode) {
        case RUNNING:
            //show progress
            break;
        case FINISHED:
            List results = resultData.getParcelableList("results");
            // do something interesting
            // hide progress
            break;
        case ERROR:
            // handle the error;
            break;
    }
}

Serwis:

public class QueryService extends IntentService {
    protected void onHandleIntent(Intent intent) {
        final ResultReceiver receiver = intent.getParcelableExtra("receiver");
        String command = intent.getStringExtra("command");
        Bundle b = new Bundle();
        if(command.equals("query") {
            receiver.send(STATUS_RUNNING, Bundle.EMPTY);
            try {
                // get some data or something           
                b.putParcelableArrayList("results", results);
                receiver.send(STATUS_FINISHED, b)
            } catch(Exception e) {
                b.putString(Intent.EXTRA_TEXT, e.toString());
                receiver.send(STATUS_ERROR, b);
            }    
        }
    }
}

ResultReceiver extension-edytowane o zaimplementowaniu MyResultReceiver.Odbiornik

public class MyResultReceiver implements ResultReceiver {
    private Receiver mReceiver;

    public MyResultReceiver(Handler handler) {
        super(handler);
    }

    public void setReceiver(Receiver receiver) {
        mReceiver = receiver;
    }

    public interface Receiver {
        public void onReceiveResult(int resultCode, Bundle resultData);
    }

    @Override
    protected void onReceiveResult(int resultCode, Bundle resultData) {
        if (mReceiver != null) {
            mReceiver.onReceiveResult(resultCode, resultData);
        }
    }
}
 281
Author: Robby Pond,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-02 20:12:22

Tworzenie aplikacji klienta REST na Androida było dla mnie niesamowitym zasobem. Głośnik nie pokazuje żadnego kodu, on po prostu przechodzi przez względy projektowe i techniki w tworzeniu solidnego API Rest w Androidzie. Jeśli twój podcast rodzaj osoby, czy nie, polecam dać ten jeden co najmniej jeden słuchać, ale, osobiście słuchałem go z 4 lub 5 razy do tej pory i prawdopodobnie będę go słuchać ponownie.

Tworzenie klienta Android REST aplikacje
Autor: Virgil Dobjanschi
Opis:

Podczas tej sesji zaprezentowane zostaną zagadnienia architektoniczne związane z tworzeniem aplikacji RESTful na platformie Android. Skupia się na wzorcach projektowych, integracji platformy i kwestiach wydajności specyficznych dla Platformy Android.

I jest tak wiele rozważań, których tak naprawdę nie poczyniłem w pierwszej wersji mojego api, że musiałem refaktorować

 16
Author: Terrance,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-18 12:25:26

Również kiedy uderzyłem the post(Config.getURL ("logowanie"), wartości) aplikacja wydaje się pauzować na while (wydaje się dziwne-myślałem, że pomysł za serwisem było to, że działa na inny wątek!)

Nie musisz utworzyćwątek samodzielnie, a lokalna usługa działa domyślnie w wątku interfejsu użytkownika.

 15
Author: Soumya Simanta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-07 18:34:35

Wiem, że @Martyn nie chce pełnego kodu, ale myślę, że ta adnotacja jest dobra na to pytanie:

10 aplikacje Open Source na Androida, do których każdy programista Androida musi zajrzeć

Foursquared dla Androida jest open-source, i mają ciekawy wzór kodu interakcji z Foursquare REST API.
 11
Author: panchicore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-12 19:38:47

Gorąco polecam REST client Retrofit .

Uważam ten dobrze napisany wpis na blogu za bardzo pomocny, zawiera również prosty przykładowy kod. Autor używa Retrofit do wykonywania połączeń sieciowych i Otto do implementacji wzorca szyny danych:

Http://www.mdswanson.com/blog/2014/04/07/durable-android-rest-clients.html

 6
Author: Pete,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-27 10:04:52

Chciałem tylko wskazać wam wszystkim kierunek samodzielnej klasy, którą zwijałem, która zawiera wszystkie funkcje.

Http://github.com/StlTenny/RestService

Wykonuje żądanie jako nieblokujące i zwraca wyniki w łatwym do zaimplementowania programie obsługi. Nawet pochodzi z przykładowej implementacji.

 5
Author: StlTenny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-11 18:01:09

Powiedzmy, że chcę uruchomić usługę na event-onItemClicked () przycisku. Mechanizm odbiornika nie działałby w takim przypadku, ponieważ: -
a) przekazałem odbiornik do serwisu (jak w Intent extra) z onItemClicked ()
b) aktywność przenosi się do tła. W onpause() ustawiłem referencję odbiornika w ResultReceiver na null, aby uniknąć wycieku aktywności.
C) aktywność zostaje zniszczona.
D) aktywność zostaje utworzona ponownie. Jednak w tym momencie usługa nie będzie być w stanie dokonać połączenia zwrotnego do aktywności, jak to odwołanie odbiornika jest utracone.
Mechanizm ograniczonej transmisji lub PendingIntent wydaje się być bardziej przydatny w takich scenariuszach - patrz powiadom aktywność z usługi

 4
Author: Nikhil_Katre,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:46:57

Zauważ, że w jakiś sposób brakuje rozwiązania Robby Pond: w ten sposób zezwalasz na tylko jedno wywołanie api na raz, ponieważ IntentService obsługuje tylko jedną intencję na raz. Często chcesz wykonywać równoległe wywołania api. Jeśli chcesz to zrobić, musisz rozszerzyć usługę zamiast Usługi IntentService i utworzyć własny wątek.

 4
Author: TjerkW,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-28 08:15:11

Również kiedy trafiłem na post(Config.getURL ("login"), wartości) aplikacja wydaje się pauzować na chwilę (wydaje się dziwne - myślałem, że idea usługi było to, że działa na innym wątku!)

W tym przypadku lepiej jest użyć asynctask, który działa na innym wątku i zwraca wynik z powrotem do wątku interfejsu po zakończeniu.

 2
Author: Aakash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-04 03:49:13

Istnieje inne podejście, które w zasadzie pomaga zapomnieć o całym zarządzaniu wnioskami. Jest on oparty na metodzie kolejki asynchronicznej i odpowiedzi callable / callback based. Główną zaletą tej metody jest to, że cały proces (request, get i parse response, sabe to db) będzie dla ciebie całkowicie przejrzysty. Po otrzymaniu kodu odpowiedzi praca jest już wykonana. Po tym wystarczy zadzwonić do db i gotowe. Pomaga jak również z problematyką tego, co się dzieje, gdy Twoja aktywność nie jest aktywna. To, co się tutaj stanie, to to, że wszystkie Twoje dane zostaną zapisane w lokalnej bazie danych, ale odpowiedź nie będzie przetwarzana przez Twoją aktywność, to idealny sposób.

O ogólnym podejściu pisałem tutaj http://ugiagonzalez.com/2012/07/02/theres-life-after-asynctasks-in-android/

W nadchodzących postach będę umieszczał konkretny przykładowy kod. Mam nadzieję, że to pomoże, skontaktuj się ze mną, aby podzielić się podejście i rozwiązywanie potencjalnych wątpliwości lub problemów.

 2
Author: Jose L Ugia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-05 14:59:01

Robby daje świetną odpowiedź, choć widzę, że nadal szukasz więcej informacji. Zaimplementowałem wywołania REST api w łatwy, ale zły sposób. Dopiero oglądając ten Wideo Google I/o zrozumiałem, gdzie popełniłem błąd. To nie jest tak proste, jak złożenie AsyncTask z httpurlconnection get/put wywołania.

 0
Author: Andrew Halloran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-08 18:06:20