Служба Restful API
Я ищу, чтобы сделать сервис, который я могу использовать для совершения звонков на веб-интерфейс REST 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;
}
}
Сервис init и bind:
// 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);
}
}
};
Ответы
Ответ 1
Если ваша служба будет частью вашего приложения, вы делаете ее более сложной, чем она должна быть. Поскольку у вас есть простой пример получения некоторых данных из веб-службы RESTful, вы должны изучить ResultReceiver и IntentService.
Этот шаблон Service + ResultReceiver работает путем запуска или привязки к сервису с помощью startService(), когда вы хотите выполнить какое-либо действие. Вы можете указать операцию для выполнения и передачи в свой ResultReceiver (активность) через дополнительные функции в намерении.
В службе вы реализуете onHandleIntent, чтобы выполнить операцию, указанную в намерении. Когда операция завершена, вы используете переданную в ResultReceiver в send сообщение обратно в Activity, в котором точка onReceiveResult.
Итак, например, вы хотите извлечь некоторые данные из своей веб-службы.
- Вы создаете намерение и вызываете startService.
- Операция в службе запускается и отправляет ей сообщение с сообщением о запуске
- Активность обрабатывает сообщение и показывает прогресс.
- Служба завершает операцию и отправляет некоторые данные обратно в вашу активность.
- Ваша деятельность обрабатывает данные и помещается в представление списка.
- Служба отправляет вам сообщение о том, что оно сделано, и оно убивает себя.
- Действие получает сообщение о завершении и скрывает диалог выполнения.
Я знаю, что вы упомянули, что вам не нужна база кода, но приложение 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);
}
}
}
Ответ 2
Также, когда я ударил сообщение (Config.getURL( "login" ), значения) приложение кажется приостановленным для в то время как (кажется странным - мысль за службой было то, что она работает на другой поток!)
Нет, вам нужно создать поток самостоятельно, по умолчанию работает локальная служба в потоке пользовательского интерфейса.
Ответ 3
Разработка клиентских приложений REST для Android была для меня удивительным ресурсом. Динамик не показывает никакого кода, он просто рассматривает конструктивные соображения и методы, чтобы собрать рок-массив Rest Api в android. Если ваш подкаст какой-то человек или нет, я бы рекомендовал дать ему хотя бы один прослушивать, но лично я слушал его как 4 или 5 раз, и, вероятно, я снова его послушаю.
Разработка клиентских приложений REST для Android
Автор: Вергилий Добжанши
Описание:
В этом сеансе будут представлены архитектурные соображения для разработки приложений RESTful на платформе Android. Он фокусируется на шаблонах проектирования, интеграции платформы и производительности, характерных для платформы Android.
И есть так много соображений, которые я действительно не сделал в первой версии моего api, что мне пришлось реорганизовать
Ответ 4
Я знаю, что @Martyn не хочет полного кода, но я думаю, что это аннотирование полезно для этого вопроса:
10 приложений с открытым исходным кодом для Android, которые каждый разработчик Android должен изучить
Foursquared for Android - open-source и содержит интересный код, взаимодействующий с API REST с четырьмя запросами.
Ответ 5
Просто хотел указать всех вас в сторону отдельного класса, который я прокатил, который включает в себя все функциональные возможности.
http://github.com/StlTenny/RestService
Он выполняет запрос как неблокирующий и возвращает результаты в легко реализуемом обработчике. Даже поставляется с примером реализации.
Ответ 6
Я бы очень рекомендовал клиент REST Retrofit.
Я нашел эту хорошо написанную запись в блоге чрезвычайно полезной, она также содержит простой пример кода.
Автор использует Retrofit для выполнения сетевых вызовов и Otto для реализации шаблона шины данных:
http://www.mdswanson.com/blog/2014/04/07/durable-android-rest-clients.html
Ответ 7
Предположим, что я хочу запустить сервис на событии - onItemClicked() кнопки. Механизм приемника не будет работать в этом случае, потому что: -
a) Я передал приемник на службу (как в Intent extra) из onItemClicked()
б) Действие перемещается на задний план. В onPause() я установил ссылку получателя в ResultReceiver равным нулю, чтобы избежать утечки Activity.
c) Активность уничтожается.
d) Активность снова создается. Однако на данный момент Служба не сможет выполнить обратный вызов для Activity, поскольку эта информация получателя потеряна.
Механизм ограниченной широковещательной передачи или PendingIntent представляется более полезным в таких сценариях - см. Уведомлять активность из службы
Ответ 8
Обратите внимание, что решения от Robby Pond так или иначе не хватает: таким образом вы разрешаете только один вызов api за раз, поскольку IntentService обрабатывает только одно намерение за раз. Часто вы хотите выполнять параллельные вызовы api. Если вы хотите сделать это, вам нужно расширить Service вместо IntentService и создать свой собственный поток.
Ответ 9
Также, когда я ударяю сообщение (значения Config.getURL( "login" ), значения), приложение, кажется, некоторое время приостанавливается (кажется странным - считалось, что идея службы заключается в том, что она работает в другом потоке!)
В этом случае лучше использовать asynctask, которая выполняется в другом потоке и возвращает результат обратно в поток ui при завершении.
Ответ 10
Здесь есть другой подход, который в основном поможет вам забыть обо всем управлении запросами. Он основан на методе асинхронной очереди и ответе на вызов/обратный вызов.
Главное преимущество заключается в том, что с помощью этого метода вы сможете полностью обработать весь процесс (запрос, получить и проанализировать ответ, sabe to db), полностью прозрачный для вас. Как только вы получите код ответа, работа уже выполнена. После этого вам просто нужно позвонить в свой db, и все готово.
Это также помогает с проблемой того, что происходит, когда ваша активность не активна.
Что будет здесь, так это то, что все ваши данные будут сохранены в вашей локальной базе данных, но ответ не будет обрабатываться вашей деятельностью, что идеально подходит.
Я написал об общем подходе здесь
http://ugiagonzalez.com/2012/07/02/theres-life-after-asynctasks-in-android/
Я буду помещать конкретный пример кода в предстоящие сообщения.
Надеюсь, что это поможет, не стесняйтесь обращаться ко мне за то, чтобы поделиться этим предложением и решить потенциальные сомнения или проблемы.
Ответ 11
Робби дает отличный ответ, хотя я вижу, что вы все еще ищете дополнительную информацию. Я реализовал REST api - это просто, но не так. Только после просмотра этого Google I/O видео я понял, где я ошибся. Это не так просто, как собрать AsyncTask с вызовом get/put HttpUrlConnection.