Restful API сервис - PullRequest
       27

Restful API сервис

219 голосов
/ 07 июля 2010

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

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

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

В настоящее время у меня есть служба с post и методами 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);
        }

    }

};

Ответы [ 11 ]

283 голосов
/ 07 июля 2010

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

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

В службе, которую вы реализуете onHandleIntent , чтобы выполнить операцию, указанную вНамерение.Когда операция завершена, вы используете переданный в ResultReceiver для отправки сообщения обратно в Activity, в котором будет вызываться 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.Приемник

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);
        }
    }
}
17 голосов
/ 20 сентября 2011

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

Разработка клиентских приложений REST для Android Автор: Вирджил ДобянскийОписание:

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

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

16 голосов
/ 07 июля 2010

Также, когда я ударил сообщение (Config.getURL («логин»), значения) приложение, кажется, делает паузу для пока (кажется странным - подумал мысль за службой было то, что он работает на другая тема!)

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

11 голосов
/ 12 сентября 2011

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

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

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

6 голосов
/ 27 сентября 2014

Я очень рекомендую REST-клиент Дооснащение .

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

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

5 голосов
/ 11 июля 2012

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

http://github.com/StlTenny/RestService

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

4 голосов
/ 28 апреля 2011

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

4 голосов
/ 17 марта 2011

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

2 голосов
/ 05 июля 2012

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

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

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

2 голосов
/ 04 ноября 2011

Кроме того, когда я нажимаю на сообщение (Config.getURL («login»), значения), приложение на некоторое время приостанавливается (кажется странным - мысль о том, что служба основана на том, что она работает в другом потоке!) 1002 *

В этом случае лучше использовать asynctask, который выполняется в другом потоке и по завершении возвращает результат обратно в поток пользовательского интерфейса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...