it-swarm.com.de

Restful API Service

Ich möchte einen Dienst erstellen, mit dem ich eine webbasierte REST API aufrufen kann.

Grundsätzlich möchte ich einen Dienst auf App Init starten, dann möchte ich in der Lage sein, diesen Dienst aufzufordern, eine URL anzufordern und die Ergebnisse zurückzugeben. In der Zwischenzeit möchte ich ein Fortschrittsfenster oder ähnliches anzeigen können.

Ich habe zurzeit einen Dienst erstellt, der IDL verwendet. Ich habe irgendwo gelesen, dass Sie dies nur wirklich für die app-übergreifende Kommunikation benötigen. Denken Sie also, dass dies nicht mehr erforderlich ist, aber Sie wissen nicht, wie Sie Rückrufe ohne IDL durchführen können. Auch wenn ich post(Config.getURL("login"), values) drücke, scheint die App für eine Weile anzuhalten (seltsam - dachte, die Idee hinter einem Dienst war, dass er auf einem anderen Thread läuft!)

Derzeit habe ich einen Dienst mit Post- und HTTP-Methoden, ein paar AIDL-Dateien (für die bidirektionale Kommunikation), einen ServiceManager, der sich mit dem Starten, Stoppen, Binden usw. des Dienstes befasst, und ich erstelle dynamisch einen Handler mit spezifischem Code für die Rückrufe nach Bedarf.

Ich möchte nicht, dass mir jemand eine vollständige Codebasis zur Verfügung stellt, an der ich arbeiten kann, aber einige Hinweise wären sehr willkommen.

Code (meistens) vollständig:

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) {...}
};

Ein paar AIDL-Dateien:

package com.something.Android

oneway interface IRemoteServiceCallback {
    void userLogIn(String result);
}

und

package com.something.Android
import com.something.Android.IRemoteServiceCallback;

interface IRestfulService {
    void doLogin(in String username, in String password);
    void registerCallback(IRemoteServiceCallback cb);
}

und der Service Manager:

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 und bind:

// this I'm calling on app onCreate
servicemanager = new ServiceManager(this);
servicemanager.startService();
servicemanager.bindService();
application = (ApplicationState)this.getApplication();
application.setServiceManager(servicemanager);

servicefunktionsaufruf:

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

    }

};
219
Martyn

Wenn Ihr Dienst Teil Ihrer Anwendung sein soll, machen Sie ihn viel komplexer, als er sein muss. Da Sie einen einfachen Anwendungsfall zum Abrufen einiger Daten von einem RESTful-Webdienst haben, sollten Sie ResultReceiver und IntentService untersuchen.

Dieses Service + ResultReceiver-Muster startet oder bindet den Service mit startService () , wenn Sie eine Aktion ausführen möchten. Sie können die auszuführende Operation angeben und den ResultReceiver (die Aktivität) über die Extras im Intent übergeben.

In dem Dienst implementieren Sie onHandleIntent , um die in Intent angegebene Operation auszuführen. Wenn der Vorgang abgeschlossen ist, verwenden Sie den übergebenen ResultReceiver, um send eine Nachricht an die Aktivität zurückzusenden, an der onReceiveResult aufgerufen wird.

So möchten Sie beispielsweise einige Daten aus Ihrem Web-Service abrufen.

  1. Sie erstellen die Absicht und rufen startService auf.
  2. Der Vorgang im Dienst wird gestartet und sendet der Aktivität eine Nachricht mit dem Hinweis, dass er gestartet wurde
  3. Die Aktivität verarbeitet die Nachricht und zeigt einen Fortschritt an.
  4. Der Dienst beendet den Vorgang und sendet einige Daten an Ihre Aktivität zurück.
  5. Ihre Aktivität verarbeitet die Daten und erstellt eine Listenansicht
  6. Der Dienst sendet Ihnen eine Nachricht, die besagt, dass der Vorgang abgeschlossen ist und sich selbst beendet.
  7. Die Aktivität erhält die Abschlussmeldung und blendet den Fortschrittsdialog aus.

Ich weiß, dass Sie erwähnt haben, dass Sie keine Codebasis wollen, aber die Open Source Google I/O 201 App verwendet einen Dienst auf die von mir beschriebene Weise.

Aktualisiert, um Beispielcode hinzuzufügen:

Die Aktivität.

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

Der Service:

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-Erweiterung - Überarbeitet, um MyResultReceiver.Receiver zu implementieren

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);
        }
    }
}
283
Robby Pond

Die Entwicklung von Android REST Client-Anwendungen war eine großartige Ressource für mich Lautsprecher zeigt keinen Code, er geht nur Design-Überlegungen und Techniken bei der Zusammenstellung eines felsenfesten Rest-Api in Android durch. Wenn Sie ein Podcast-Typ sind oder nicht, würde ich empfehlen, diesem mindestens einen zuzuhören, aber ich persönlich hab es bis jetzt vier oder fünf mal angehört und ich werde es mir wahrscheinlich nochmal anhören.

Entwicklung von Android REST Client-Anwendungen
Autor: Virgil Dobjanschi
Beschreibung:

In dieser Sitzung werden Überlegungen zur Architektur für die Entwicklung von RESTful-Anwendungen auf der Android Plattform. Der Schwerpunkt liegt auf Entwurfsmustern, Plattformintegration und Leistungsproblemen, die für die Android Plattform spezifisch sind.

Und es gibt so viele Überlegungen, die ich in der ersten Version meiner API wirklich nicht angestellt habe, dass ich sie überarbeiten musste

17
Terrance

Auch wenn ich auf den Post drücke (Config.getURL ("login"), Werte), scheint die App für eine Weile anzuhalten (seltsam - dachte, die Idee hinter einem Dienst war, dass er auf einem anderen Thread ausgeführt wird!)

Nein, Sie müssen create selbst einen Thread erstellen, ein Local Dienst wird standardmäßig im UI-Thread ausgeführt.

16
Soumya Simanta

Ich weiß, dass @Martyn keinen vollständigen Code haben möchte, aber ich denke, diese Anmerkung ist gut für diese Frage:

10 Open Source Android Apps, die sich jeder Android Entwickler ansehen muss

Foursquared für Android ist Open-Source und hat ein interessantes Codemuster, das mit der foursquare REST API) interagiert.

11
panchicore

Ich kann den REST client Retrofit nur empfehlen.

Ich fand diesen gut geschriebenen Blog-Beitrag äußerst hilfreich, er enthält auch einfachen Beispielcode. Der Autor verwendet Retrofit , um die Netzwerkanrufe durchzuführen, und Otto , um ein Datenbusmuster zu implementieren:

http://www.mdswanson.com/blog/2014/04/07/durable-Android-rest-clients.html

6
Pete

Ich wollte Sie alle in die Richtung einer eigenständigen Klasse führen, die alle Funktionen enthält.

http://github.com/StlTenny/RestService

Es führt die Anforderung als nicht blockierend aus und gibt die Ergebnisse in einem einfach zu implementierenden Handler zurück. Kommt sogar mit einer Beispielimplementierung.

5
StlTenny

Nehmen wir an, ich möchte den Dienst für ein Ereignis starten - onItemClicked () einer Schaltfläche. Der Empfängermechanismus würde in diesem Fall nicht funktionieren, weil:
a) Ich habe den Empfänger von onItemClicked () an den Dienst übergeben (wie in Intent extra)
b) Aktivität tritt in den Hintergrund. In onPause () habe ich die Empfängerreferenz im ResultReceiver auf null gesetzt, um zu vermeiden, dass die Aktivität verloren geht.
c) Aktivität wird zerstört.
d) Aktivität wird neu erstellt. Zu diesem Zeitpunkt kann der Dienst jedoch keinen Rückruf an die Aktivität senden, da diese Empfängerreferenz verloren geht.
Der Mechanismus einer eingeschränkten Übertragung oder eines PendingIntent scheint in solchen Szenarien sinnvoller zu sein - siehe Benachrichtigung über Aktivität vom Dienst

4
Nikhil_Katre

Beachten Sie, dass die Lösung von Robby Pond irgendwie fehlt: Auf diese Weise können Sie immer nur einen API-Aufruf gleichzeitig ausführen, da der IntentService jeweils nur einen Intent verarbeitet. Oft möchten Sie parallele API-Aufrufe durchführen. Wenn Sie dies tun möchten, müssen Sie Service anstelle von IntentService erweitern und Ihren eigenen Thread erstellen.

4
TjerkW

Auch wenn ich auf den Post drücke (Config.getURL ("login"), Werte), scheint die App für eine Weile anzuhalten (seltsam - dachte, die Idee hinter einem Dienst war, dass er auf einem anderen Thread ausgeführt wird!)

In diesem Fall ist es besser, asynctask zu verwenden, das auf einem anderen Thread ausgeführt wird und das Ergebnis nach Abschluss an den ui-Thread zurückgibt.

2
Aakash

Es gibt hier einen anderen Ansatz, der Ihnen hilft, die gesamte Verwaltung der Anforderungen zu vergessen. Es basiert auf einer asynchronen Warteschlangenmethode und einer auf Rückrufen basierenden Antwort. Der Hauptvorteil ist, dass Sie mit dieser Methode den gesamten Prozess (Request, Get und Parse Response, Sabe to DB) für Sie völlig transparent machen können. Sobald Sie den Antwortcode erhalten haben, ist die Arbeit bereits erledigt. Danach müssen Sie nur noch Ihre db anrufen und schon sind Sie fertig. Es hilft auch bei der Problematik, was passiert, wenn Ihre Aktivität nicht aktiv ist. Was hier passieren wird, ist, dass Sie alle Ihre Daten in Ihrer lokalen Datenbank gespeichert haben, die Antwort jedoch nicht von Ihrer Aktivität verarbeitet wird. Dies ist der ideale Weg.

Ich schrieb über einen allgemeinen Ansatz hier http://ugiagonzalez.com/2012/07/02/theres-life-after-asynctasks-in-Android/

Ich werde in den kommenden Beiträgen einen bestimmten Beispielcode einfügen. Hoffe, es hilft, zögern Sie nicht, mich zu kontaktieren, um den Ansatz mitzuteilen und mögliche Zweifel oder Probleme zu lösen.

2
Jose L Ugia

Robby liefert eine großartige Antwort, obwohl ich sehe, dass Sie immer noch nach mehr Informationen suchen. Ich habe implementiert REST api nennt das einfach ABER falsch. Erst als ich das hier angeschaut habe Google I/O Video habe ich verstanden, wo ich falsch gelaufen bin. Es ist nicht so Einfach wie das Zusammenstellen einer AsyncTask mit einem HttpUrlConnection get/put-Aufruf.

1
Andrew Halloran