Dinlendirici API hizmeti


226

Ben web tabanlı bir REST API çağrı yapmak için kullanabileceğiniz bir hizmet yapmak için arıyorum.

Temelde ben uygulama init bir hizmet başlatmak istiyorum o zaman bir url istemek ve sonuçları döndürmek için bu hizmet istemek istiyorum. Bu arada bir ilerleme penceresi veya benzer bir şey görüntüleyebilmek istiyorum.

Şu anda IDL kullanan bir hizmet oluşturdum, sadece çapraz uygulama iletişimi için buna gerçekten ihtiyacınız olan bir yerde okudum, bu yüzden bu sıyırma ihtiyaçlarını düşünün, ancak onsuz geri aramaların nasıl yapılacağından emin olun. Ayrıca vurduğumda post(Config.getURL("login"), values)uygulama bir süre duraklıyor gibi görünüyor (garip görünüyor - bir hizmetin arkasındaki fikir farklı bir iş parçacığında çalıştığıydı!)

Şu anda posta ile bir hizmet var ve http yöntemleri içinde almak, AIDL dosyaları (iki yönlü iletişim için), hizmet, başlangıç, durdurma, bağlama vb ile ilgili bir ServiceManager birkaç ve dinamik olarak belirli bir kod ile bir Handler oluşturma gerektiğinde geri aramalar için.

Kimse bana üzerinde çalışmak için tam bir kod tabanı vermek istemiyorum, ancak bazı işaretçiler büyük mutluluk duyacağız.

Kod (çoğunlukla) dolu:

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

Birkaç AIDL dosyası:

package com.something.android

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

ve

package com.something.android
import com.something.android.IRemoteServiceCallback;

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

ve servis müdürü:

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

Hizmet başlatma ve bağlama:

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

servis fonksiyon çağrısı:

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

    }

};

5
Bu, Android REST istemci uygulamasını öğrenen kişiler için çok yararlı olabilir. Dobjanschi'nin sunumu PDF'ye çevrildi: drive.google.com/file/d/0B2dn_3573C3RdlVpU2JBWXdSb3c/…
Kay Zed

Birçok kişi Virgil Dobjanschi'nin sunumunu önerdiğinden ve IO 2010'a bağlantı kesildiğinden, YT videosuna doğrudan bağlantı: youtube.com/watch?v=xHXn3Kg2IQE
Jose_GD 31:14

Yanıtlar:


283

Hizmetiniz uygulamanızın bir parçası olacaksa, onu olması gerekenden daha karmaşık hale getiriyorsunuz. RESTful Web Hizmetinden bazı verileri almak için basit bir kullanım örneğiniz olduğundan, ResultReceiver ve IntentService'e bakmalısınız .

Bu Service + ResultReceiver kalıbı , bazı eylemler yapmak istediğinizde , hizmeti startService () ile başlatarak veya ciltlemeye bağlayarak çalışır . ResultReceiver (etkinlik) işleminizi gerçekleştirme ve iletme işlemini Intent'teki ekstralar aracılığıyla belirtebilirsiniz.

Hizmetinde uygulamak onHandleIntent Niyet belirtilen işlemi yapmak. İşlem tamamlandığında, ResultReceiver öğesinde iletilen işlemi, Etkinliğe onReceiveResult öğesinin çağrıldığı noktaya geri göndermek için kullanırsınız .

Örneğin, Web Servisinizden bazı veriler almak istiyorsunuz.

  1. Niyeti yaratır ve startService'i çağırırsınız.
  2. Hizmetteki işlem başlar ve etkinliğe başladığını bildiren bir ileti gönderir
  3. Etkinlik mesajı işler ve bir ilerleme gösterir.
  4. Hizmet işlemi tamamlar ve bazı verileri etkinliğinize geri gönderir.
  5. Etkinliğiniz verileri işler ve liste görünümüne ekler
  6. Hizmet size tamamlandığını söyleyen bir mesaj gönderir ve kendini öldürür.
  7. Etkinlik bitiş mesajını alır ve ilerleme durumu iletişim kutusunu gizler.

Kod tabanı istemediğinizden bahsettiğinizi biliyorum, ancak açık kaynaklı Google I / O 2010 uygulaması bu şekilde tarif ettiğim bir hizmeti kullanıyor.

Örnek kod eklemek için güncellendi:

Aktivite.

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

Hizmet:

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 uzantısı - MyResultReceiver.Receiver'ı uygulamak üzere düzenlendi

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

1
Harika teşekürler! Google iosched uygulamasından geçtim ve vay be ... oldukça karmaşık ama çalışan bir şeyim var, şimdi neden işe yaradığını bulmam gerekiyor! Ama evet, çalıştığım temel kalıp bu. çok teşekkür ederim.
Martyn

29
Cevaba küçük bir ilave: mReceiver.setReceiver (null) gibi; onPause yönteminde mReceiver.setReceiver (this) yapmalısınız; onResume yönteminde. Ayrıca, etkinliğiniz yeniden oluşturulmadan devam ettirilirse etkinlikleri
almayabilirsiniz

7
Dokümanlar stopSelf'i aramak zorunda olmadığınızı söylemiyor, çünkü IntentService bunu sizin için yapıyor mu?
Mikael Ohlson

2
Doğru @MikaelOhlson, sen gerektiğini değil diyoruz stopSelfsen alt sınıfı ise IntentServicebunu yaparsanız, aynı herhangi bekleyen istekler kaybedersiniz çünkü IntentService.
quietmint

1
IntentServicegörev tamamlandığında kendini öldürür, bu yüzden this.stopSelf()gereksizdir.
Euporie

17

Android REST istemci uygulamaları geliştirmek benim için harika bir kaynak oldu. Konuşmacı herhangi bir kod göstermiyor, sadece android'de bir kaya katı Rest Api'yi bir araya getirirken tasarım hususlarını ve tekniklerini gözden geçiriyor. Eğer bir podcast tür kişi ya da değil, bu en az bir dinlemek vermenizi tavsiye ederim, ama şahsen şimdiye kadar 4 ya da beş kez dinledim ve muhtemelen tekrar dinleyeceğim.

Android REST istemci uygulamaları geliştirme
Yazar: Virgil Dobjanschi
Açıklama:

Bu oturumda, Android platformunda RESTful uygulamaları geliştirmek için mimari konular ele alınacaktır. Android platformuna özgü tasarım kalıplarına, platform entegrasyonuna ve performans sorunlarına odaklanır.

Ve API'mın ilk sürümünde gerçekten yapmadığım çok fazla husus var.


4
+1 Bu, işe başladığınızda asla düşünmemeniz gereken konuları içerir.
Thomas Ahle

Evet, bir Rest müşterisi geliştirmeye ilk adımım neredeyse tam olarak ne yapmamaya ilişkin açıklamasıydı (Neyse ki bunu izlemeden önce bunun çoğunun yanlış olduğunu fark ettim). Biraz gittim.
Terrance

Bu videoyu bir kereden fazla izledim ve ikinci kalıbı uyguluyorum. Benim sorunum, sunucudan gelen yeni verilerden yerel verileri güncellemek için karmaşık veritabanı modelimdeki işlemleri kullanmam gerektiğidir ve ContentProvider arabirimi bana bunu yapmanın bir yolunu sağlamaz. Önerin var mı Terrance?
Flávio Faria

2
NB: Dobjanschi'nin HttpClient hakkındaki yorumları artık geçerli değil. Bkz. Stackoverflow.com/a/15524143/939250
Donal Lafferty

Evet, HttpURLConneksiyon artık tercih edilmektedir. Ayrıca, Android 6.0'ın çıkmasıyla birlikte, Apache HTTP İstemcisi desteği resmi olarak kaldırıldı .
RonR

16

Ayrıca yazıyı vurduğumda (Config.getURL ("giriş"), değerler) uygulama bir süre duraklıyor gibi görünüyor (garip görünüyor - bir hizmetin arkasındaki fikir farklı bir iş parçacığında çalıştığıydı!)

Kendiniz bir iş parçacığı oluşturmak zorunda değilsiniz, varsayılan olarak UI iş parçacığında bir Yerel hizmet çalışır.




5

Sadece hepinizi, tüm işlevselliği bir araya getiren bağımsız bir sınıf yönüne yönlendirmek istedim.

http://github.com/StlTenny/RestService

İsteği engellemeyen olarak yürütür ve sonuçları, uygulaması kolay bir işleyiciyle döndürür. Hatta örnek bir uygulama ile geliyor.


4

Diyelim ki hizmeti bir olay - onItemClicked () üzerinde başlatmak istiyorum. Alıcı mekanizması bu durumda çalışmaz çünkü: -
a) Alıcıyı onItemClicked () 'den hizmete geçirdim (ekstra niyette olduğu gibi)
b) Etkinlik arka plana taşınır. OnPause () öğesinde, Etkinliğin sızdırılmasını önlemek için ResultReceiver içindeki alıcı referansını null değerine ayarladım.
c) Etkinlik yok edilir.
d) Etkinlik yeniden oluşturulur. Ancak bu noktada Hizmet, bu alıcı referansı kaybolduğundan Etkinliğe geri arama yapamaz.
Sınırlı bir yayının veya bir PendingIntent'in mekanizması bu tür senaryolarda daha yararlı gibi görünmektedir - Etkinliği hizmetten bildir


1
söylediklerinizle ilgili bir sorun var. yani etkinlik arka plana geçtiğinde yok olmaz ... böylece alıcı hala var ve etkinlik içeriği de var.
DArkO

@DArkO Bir Etkinlik duraklatıldığında veya durdurulduğunda, düşük bellek durumlarında Android sistemi tarafından öldürülebilir. Bakınız Etkinlik Yaşam Döngüsü .
jk7

4

Robby Pond'un çözümünün bir şekilde eksik olduğuna dikkat edin: Bu şekilde, IntentService her seferinde yalnızca bir niyet gerçekleştirdiğinden, bir seferde yalnızca bir api çağrısı yapmanıza izin verirsiniz. Genellikle paralel api çağrıları yapmak istersiniz. Bunu yapmak istiyorsanız, IntentService yerine Hizmeti genişletmeniz ve kendi iş parçacığınızı oluşturmanız gerekir.


1
IntentService üzerinde webservice api çağrılarını, IntentService türevli sınıfınızın üye değişkeni olarak sunulan bir executre thread hizmetine devrederek yine de IntentService üzerinde birden fazla çağrı yapabilirsiniz
Viren

2

Ayrıca yazıyı vurduğumda (Config.getURL ("giriş"), değerler) uygulama bir süre duraklıyor gibi görünüyor (garip görünüyor - bir hizmetin arkasındaki fikir farklı bir iş parçacığında çalıştığıydı!)

Bu durumda, farklı bir iş parçacığı üzerinde çalışan ve sonuç tamamlandığında ui iş parçacığına geri dönen asynctask kullanmak daha iyidir.


2

Burada temel olarak isteklerin tüm yönetimini unutmanıza yardımcı olan başka bir yaklaşım var. Bir zaman uyumsuz kuyruk yöntemine ve çağrılabilir / geri çağrı tabanlı bir yanıta dayanır. Ana avantajı, bu yöntemi kullanarak tüm süreci (istek, alma ve yanıt ayrıştırma, sabe to db) sizin için tamamen şeffaf hale getirebilmenizdir. Yanıt kodunu aldıktan sonra iş zaten yapılır. Bundan sonra sadece db'nize bir çağrı yapmanız gerekir ve işiniz bitti. Etkinliğiniz aktif olmadığında ne olacağı sorununa da yardımcı olur. Burada olacak olan, tüm verilerinizin yerel veritabanınıza kaydedilmesidir, ancak yanıt etkinliğiniz tarafından işlenmeyecektir, bu ideal bir yoldur.

Burada genel bir yaklaşım hakkında yazdım http://ugiagonzalez.com/2012/07/02/theres-life-after-asynctasks-in-android/

Önümüzdeki gönderilere belirli bir örnek kod koyacağım. Umarım yardımcı olur, yaklaşımı paylaşmak ve potansiyel şüpheleri veya sorunları çözmek için benimle iletişime geçmekten çekinmeyin.


Ölü bağlantı. Alan adının süresi doldu.
jk7

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.