2010-07-07 5 views
210

Я ищу, чтобы сделать сервис, который я могу использовать для совершения звонков на веб-интерфейс REST API.Служба Restful API

В принципе, я хочу запустить службу в приложении init, тогда я хочу попросить эту службу запросить URL-адрес и вернуть результаты. Тем временем я хочу иметь возможность отображать окно прогресса или что-то подобное.

Я создал службу в настоящее время, которая использует IDL, я где-то читал, что вам действительно нужно это только для общения через приложение, поэтому подумайте, что эти потребности нужно удалить, но не уверены, как делать обратные вызовы без него. Также, когда я нахожусь в post(Config.getURL("login"), values), приложение, похоже, некоторое время приостанавливается (кажется странным - мысль об услуге заключалась в том, что она работает в другом потоке!)

В настоящее время у меня есть служба с сообщением и получение http-методов внутри , пару файлов AIDL (для двухсторонней связи), ServiceManager, который имеет дело с запуском, остановкой, привязкой и т. д. к службе, и я динамически создаю обработчик с конкретным кодом для обратных вызовов по мере необходимости.

Я не хочу, чтобы кто-нибудь дал мне полную базу кода для работы, но некоторые указатели были бы весьма благодарны.

код в (основном) полный:

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

Пару AIDL файлов:

package com.something.android 

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

и

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

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

и менеджер службы:

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

Служба инициализации и привязки:

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

служба вызова функции:

// 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

Это может быть очень полезным для людей, изучающих реализацию клиента Android REST. Представление Добянски транскрибируется в PDF-файл: https://drive.google.com/file/d/0B2dn_3573C3RdlVpU2JBWXdSb3c/edit?usp=sharing –

+0

Поскольку многие люди рекомендовали презентацию Вергилия Добжански и ссылку на IO 2010, вот прямая ссылка к видео YT: http://www.youtube.com/watch?v=xHXn3Kg2IQE –

ответ

278

Если ваша служба будет частью вашего приложения, то вы делаете это путь более сложный, чем это должно быть , Поскольку у вас есть простой пример получения некоторых данных из веб-службы RESTful, вы должны посмотреть на ResultReceiver и IntentService.

Этот шаблон Service + ResultReceiver работает путем запуска или привязки к службе с помощью startService(), когда вы хотите выполнить какое-либо действие. Вы можете указать операцию для выполнения и передачи в ResultReceiver (активность) через дополнительные функции в Intent.

В службе вы реализуете onHandleIntent, чтобы выполнить операцию, указанную в намерении. Когда операция завершена, вы используете переданное в ResultReceiver сообщение send с сообщением обратно на операцию, в которой будет вызываться onReceiveResult.

Так, например, вы хотите извлечь некоторые данные из своей веб-службы.

  1. Вы создаете намерение и вызываете startService.
  2. Операция в службе начинается, и она отправляет в действие сообщение о начале работы
  3. Активность обрабатывает сообщение и показывает прогресс.
  4. Служба завершает операцию и отправляет данные обратно в вашу деятельность.
  5. Ваша деятельность обрабатывает данные и помещается в список.
  6. Служба отправляет вам сообщение о том, что это сделано, и оно убивает себя.
  7. Активность получает сообщение завершения и скрывает диалог выполнения.

Я знаю, что вы упомянули, что вам не нужна база кода, но с открытым исходным кодом Google I/O 2010 приложение использует сервис таким образом, что я описываю.

Обновлено добавить примеры кода:

Активность.

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

Служба:

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 расширение - отредактированный собирается реализовать 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); 
     } 
    } 
} 
+0

Хороший ответ, хотя шаблон немного трудно полностью обернуть вокруг меня. Не могли бы вы добавить пример кода? Это было бы здорово! – idolize

+0

да. Это довольно просто, как только вы увидите, что он работает. –

+1

Отлично - спасибо! Я попал в приложение iosched Google и ничего себе ... это довольно сложно, но у меня есть что-то, что работает, теперь мне просто нужно разобраться, почему это работает! Но да, это основная модель, с которой я работаю. спасибо. – Martyn

15

Кроме того, когда я ударил пост (Config.getURL ("Войти "), значений) приложение кажется приостановлено для , пока (кажется странным - tho ught идею за сервис было то, что она работает на другом потоке!)

Нет, вы должны создать волоске себя, Местный сервис работает в потоке пользовательского интерфейса по умолчанию.

4

Допустим, я хочу запустить сервис на событии - onItemClicked() кнопки. Механизм приемника не будет работать в этом случае, потому что: -
a) Я передал приемник на службу (как в Intent extra) из onItemClicked()
b) Действие перемещается на задний план. В onPause() я установил ссылку получателя в ResultReceiver равным null, чтобы избежать утечки Activity.
c) Активность разрушается.
d) Действие снова создается. Однако в этот момент Служба не сможет выполнить обратный вызов для Activity, поскольку эта информация приемника потеряна.
механизм ограниченного вещания или PendingIntent, кажется, более полезным в таких scenarios- относится к Notify activity from service

+1

есть проблема с тем, что вы говорите. то есть когда действие перемещается на задний план, оно не уничтожается ... поэтому приемник все еще существует и контекст активности. – DArkO

+0

@DArkO Когда действие приостановлено или остановлено, оно может быть убито системой Android в ситуациях с низкой памятью. См. [Жизненный цикл активности] (https://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle). – jk7

4

Следует отметить, что решение от Robby пруда как-то не хватает: таким образом вы позволяете только Todo один апите заход в так как IntentService обрабатывает только одно намерение за раз. Часто вы хотите выполнять параллельные вызовы api. Если вы хотите сделать это, вам нужно расширить Service вместо IntentService и создать свой собственный поток.

+1

Вы по-прежнему можете выполнять множественные вызовы в IntentService, делегируя вызовы api webservice службе-потоку-потоку, представляя в качестве переменной-члена вашего производного класса IntentService. – Viren

11

Я знаю @Martyn не хочет полный кода, но я думаю, что эта аннотация его хорошо на этот вопрос:

10 Open Source Android Apps which every Android developer must look into

Foursquared для Android является open-source, и имеет интересный шаблон кода, взаимодействующий с квадратным REST API.

+3

. Это потрясающая ссылка, спасибо за публикацию! – Martyn

16

Разработка клиентских приложений Android REST была для меня удивительным ресурсом. Динамик не показывает никакого кода, он просто рассматривает конструктивные соображения и методы, чтобы собрать рок-массив Rest Api в android. Если ваш подкаст какой-то человек или нет, я бы рекомендовал дать ему хотя бы один прослушивать, но лично я слушал его как 4 или пять раз, и, вероятно, я снова его послушаю.

Developing Android REST client applications
Автор: Вирджил Dobjanschi
Описание:

Эта сессия представит архитектурные соображения для разработки RESTful приложений на Android платформе. Он фокусируется на шаблонах проектирования, интеграции платформы и производительности, характерных для платформы Android.

И есть так много соображений, я действительно не сделал в первой версии моего апи, что я должен был реорганизовать

+4

+1 Это содержит те соображения, о которых вы никогда не подумаете, когда начнете. –

+0

Да, мой первый набег на разработку клиента Rest был почти точно его описанием того, что именно не делать (к счастью, я понял, что многое из этого было неправильным, прежде чем я смотрел это). Я немного укусил. – Terrance

+0

Я видел это видео несколько раз, и я реализую второй образец. Моя проблема в том, что мне нужно использовать транзакции в моей сложной модели базы данных для обновления локальных данных из свежих данных, поступающих с сервера, а интерфейс ContentProvider не предоставляет мне способ сделать это. У вас есть какие-то предложения, Терренс? –

2

Кроме того, когда я попал в штангу (Config.getURL («Вход»), ценности) приложение, кажется, чтобы приостановить на некоторое время (кажется странным - думал идея сервиса в том, что он работает в другом потоке)

в этом случае его лучше использовать AsyncTask, который работает на другой поток и верните результат обратно в поток ui при завершении.

2

Здесь есть другой подход, который в основном поможет вам забыть обо всем управлении запросами. Он основан на методе асинхронной очереди и ответе на вызов/обратный вызов. Главное преимущество заключается в том, что с помощью этого метода вы сможете полностью обработать весь процесс (запрос, получить и проанализировать ответ, sabe to db), полностью прозрачный для вас. Как только вы получите код ответа, работа уже выполнена. После этого вам просто нужно позвонить в свой db, и все готово. Это также помогает с проблемой того, что происходит, когда ваша активность не активна. Что будет здесь, так это то, что все ваши данные будут сохранены в вашей локальной базе данных, но ответ не будет обработан вашей деятельностью, это идеальный способ.

Я писал об общем подходе здесь http://ugiagonzalez.com/2012/07/02/theres-life-after-asynctasks-in-android/

Я буду помещать конкретный пример кода в будущих постах. Надеюсь, что это поможет, не стесняйтесь обращаться ко мне за то, чтобы поделиться своим подходом и решить потенциальные сомнения или проблемы.

+0

Мертвая ссылка. Срок действия домена истек. – jk7

5

Просто хотел указать всех вас в сторону отдельного класса, который я прокатил, который включает в себя все функциональные возможности.

http://github.com/StlTenny/RestService

Он выполняет запрос как неблокируемые, и возвращает результаты в удобном для реализации обработчика. Даже поставляется с примером реализации.

0

Robby дает отличный ответ, хотя я вижу, что вы все еще ищете дополнительную информацию. Я реализовал REST api - это просто, но не так. Только наблюдая за этим Google I/O video, я понял, где я ошибся. Это не так просто, как собрать AsyncTask с вызовом get/put HttpUrlConnection.

+0

Мертвая ссылка. Вот обновленная версия - [Google I/O 2010 - клиентские приложения Android REST] (http://goo.gl/95zfmf) – zim

5

Я бы очень рекомендовал клиента REST Retrofit.

Я нашел это хорошо написанное сообщение в блоге чрезвычайно полезным, оно также содержит простой пример кода. Автор использует Retrofit сделать сетевые вызовы и Otto реализовать шаблон шины данных:

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