android - volley - web service rest crud




Servizio API riposante (8)

Anche quando premo il post (Config.getURL ("login"), valori) l'app sembra fermarsi un attimo (sembra strano - pensavo che l'idea dietro un servizio fosse che girasse su un thread diverso!)

In questo caso è meglio usare asynctask, che gira su un thread diverso e restituisce i risultati al thread ui al termine.

Sto cercando di creare un servizio che possa essere utilizzato per effettuare chiamate a un'API REST basata sul Web.

Fondamentalmente voglio avviare un servizio su app init, quindi voglio essere in grado di chiedere a quel servizio di richiedere un URL e restituire i risultati. Nel frattempo voglio essere in grado di visualizzare una finestra di avanzamento o qualcosa di simile.

Ho creato un servizio al momento che usa IDL, ho letto da qualche parte che ne hai davvero bisogno per la comunicazione cross-app, quindi pensa che queste esigenze si estendano ma non sei sicuro di come fare i callback senza di esso. Anche quando post(Config.getURL("login"), values) il post(Config.getURL("login"), values) l'app sembra fermarsi un attimo (sembra strano - pensavo che l'idea dietro un servizio fosse che girasse su un thread diverso!)

Attualmente ho un servizio con post e ottenere metodi http all'interno, un paio di file AIDL (per comunicazione bidirezionale), un ServiceManager che si occupa di avviare, arrestare, legare ecc. Al servizio e sto creando dinamicamente un gestore con codice specifico per i callback secondo necessità.

Non voglio che qualcuno mi dia una base di codice completa su cui lavorare, ma alcuni suggerimenti sarebbero molto apprezzati.

Codice in (principalmente) 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 paio di file 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 il responsabile del servizio:

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

Servizio 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);

chiamata funzione di servizio:

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

    }

};

Anche quando premo il post (Config.getURL ("login"), valori) l'app sembra fermarsi un attimo (sembra strano - pensavo che l'idea dietro un servizio fosse che girasse su un thread diverso!)

No, devi creare tu stesso un thread, un servizio locale viene eseguito nel thread dell'interfaccia utente per impostazione predefinita.



Diciamo che voglio avviare il servizio su un evento - onItemClicked () di un pulsante. Il meccanismo del ricevitore non funzionerebbe in quel caso perché:
a) Ho passato il ricevitore al servizio (come in Intent extra) da onItemClicked ()
b) L'attività si sposta sullo sfondo. In onPause () imposto il riferimento del ricevitore all'interno del ResultReceiver su null per evitare la perdita dell'attività.
c) L'attività viene distrutta.
d) L'attività viene creata nuovamente. Tuttavia, a questo punto, il Servizio non sarà in grado di effettuare una richiamata all'Attività in quanto tale riferimento del ricevitore viene perso.
Il meccanismo di una trasmissione limitata o PendingIntent sembra essere più utile in tali scenari- fare riferimento a Notifica attività dal servizio


Se il tuo servizio sarà parte della tua applicazione, diventerai molto più complesso di quanto dovrebbe essere. Poiché si dispone di un semplice caso di utilizzo di alcuni dati da un servizio Web RESTful, è necessario esaminare ResultReceiver e IntentService .

Questo modello Service + ResultReceiver funziona avviando o vincolando il servizio con startService() quando si desidera eseguire un'azione. È possibile specificare l'operazione da eseguire e inoltrare nel ResultReceiver (l'attività) tramite gli extra nell'Intento.

Nel servizio si implementa onHandleIntent per eseguire l'operazione specificata nell'intento. Quando l'operazione è completata, si utilizza il passato in ResultReceiver per send un messaggio all'attività a cui verrà chiamato onReceiveResult .

Ad esempio, vuoi estrarre alcuni dati dal tuo Web Service.

  1. Si crea l'intento e si chiama startService.
  2. L'operazione nel servizio inizia e invia all'attività un messaggio che lo informa dell'avvio
  3. L'attività elabora il messaggio e mostra un progresso.
  4. Il servizio termina l'operazione e invia alcuni dati alla tua attività.
  5. La tua attività elabora i dati e inserisce in una visualizzazione elenco
  6. Il servizio ti invia un messaggio che dice che è fatto e si uccide.
  7. L'attività ottiene il messaggio di completamento e nasconde la finestra di dialogo di avanzamento.

So che hai detto che non volevi una base di codice, ma l'app Google I / O 2010 open source utilizza un servizio in questo modo che sto descrivendo.

Aggiornato per aggiungere codice di esempio:

L'attività.

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

Il servizio:

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

Estensione ResultReceiver - modificata per implementare MyResultReceiver.Receiver

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

Si noti che la soluzione di Robby Pond è in qualche modo carente: in questo modo si consente la chiamata a todo one api in un momento dato che IntentService gestisce solo un intento alla volta. Spesso si desidera eseguire chiamate api parallele. Se vuoi fare questo devi estendere Servizio invece di IntentService e creare il tuo thread.


Volevo solo indicarti la direzione di una classe autonoma che ho implementato e che incorpora tutte le funzionalità.

http://github.com/StlTenny/RestService

Esegue la richiesta come non bloccante e restituisce i risultati in un gestore di facile implementazione. Anche con un'implementazione di esempio.


Lo sviluppo di applicazioni client Android REST è stata per me una risorsa fantastica. L'oratore non mostra alcun codice, si limita a considerare le considerazioni e le tecniche del design nel mettere insieme un solido RIP Api in Android. Se il tuo podcast è un tipo o meno, ti consiglio di dare a questo almeno un ascolto, ma personalmente l'ho ascoltato fino a 4 o 5 volte e probabilmente lo ascolterò di nuovo.

Sviluppo di applicazioni client Android REST
Autore: Virgil Dobjanschi
Descrizione:

Questa sessione presenterà considerazioni di architettura per lo sviluppo di applicazioni RESTful sulla piattaforma Android. Si concentra su modelli di progettazione, integrazione della piattaforma e problemi di prestazioni specifici per la piattaforma Android.

E ci sono così tante considerazioni che non avevo davvero fatto nella prima versione della mia API che ho dovuto refactoring





rest