Restful API服务
我正在寻找一个服务,我可以用它来调用基于Web的REST API。
基本上我想开始一个应用程序初始化服务,然后我想能够要求该服务请求一个网址,并返回结果。 在此期间,我希望能够显示一个进度窗口或类似的东西。
我已经创建了一个使用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); } } };
如果你的服务将成为你应用程序的一部分,那么你正在使它比需要的更复杂。 由于您从RESTful Web服务获取一些数据的简单用例,您应该查看ResultReceiver和IntentService 。
这个Service + ResultReceiver模式通过startService()来启动或绑定服务,当你想做一些动作。 你可以指定这个操作来执行你的ResultReceiver(活动),并通过Intent中的extras。
在实现onHandleIntent的服务中,执行Intent中指定的操作。 当操作完成后,您使用传入的ResultReceiver 将消息发送回Activity,在此处将调用onReceiveResult 。
例如,你想从你的Web服务中提取一些数据。
- 您创建意图并调用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); } } }
另外,当我点击帖子(Config.getURL(“login”),values),应用程序似乎暂停了一段时间(似乎很奇怪 – 认为服务背后的想法是,它运行在不同的线程!
不,你必须自己创建一个线程, 本地服务默认在UI线程中运行。
开发Android REST客户端应用程序对我来说是一个很好的资源。 扬声器不显示任何代码,他只是在设计考虑和技术,在Android中放在一个坚实的Rest Api。 如果你的播客不是个人,我建议至少给这个听一个听,但是我个人已经听过这样的四五次了,我可能会再听一遍。
开发Android REST客户端应用程序
作者:Virgil Dobjanschi
描述:
本次会议将介绍在Android平台上开发RESTful应用程序的架构注意事项。 它着重于Android平台特有的设计模式,平台集成和性能问题。
在我的API的第一个版本中,我真的没有做过很多的考虑,我不得不重构
我知道@Martyn不需要完整的代码,但是我认为这个注释对于这个问题是很好的:
每个Android开发人员必须考虑的10个开源Android应用程序
Foursquared for Android是开源的 ,并且有一个有趣的代码模式与四方REST API交互。
只是想把你们全部指向一个独立的类,我把它们合并到了所有的功能中。
http://github.com/StlTenny/RestService
它以非阻塞方式执行请求,并将结果以易于实现的方式返回。 甚至附带一个示例实现。
我会强烈推荐REST客户端Retrofit 。
我发现这个写得很好的博客文章非常有帮助,它也包含简单的示例代码。 作者使用Retrofit使网络调用和Otto实现数据总线模式:
http://www.mdswanson.com/blog/2014/04/07/durable-android-rest-clients.html
比方说,我想启动一个事件的服务 – onItemClicked()的一个按钮。 接收机制在这种情况下不起作用,因为:
a)我从onItemClicked()将接收器传递给服务(如Intent extra)
b)活动移至背景。 在onPause()中,我将ResultReceiver中的接收者引用设置为null,以避免泄露Activity。
c)活动被破坏。
d)活动被重新创建。 但是,在这一点上,服务将无法回调Activity,因为接收者引用丢失。
有限广播或PendingIntent的机制似乎在这种情况下更有用 – 参考从服务通知活动
请注意Robby Pond的解决方案在某种程度上缺乏:因为IntentService一次只能处理一个意图,所以一次只能允许一个api调用。 通常你想执行并行API调用。 如果你想要这个,你必须扩展Service而不是IntentService并创建自己的线程。
另外,当我点击帖子(Config.getURL(“login”),values),应用程序似乎暂停了一段时间(似乎很奇怪 – 认为服务背后的想法是,它运行在不同的线程!
在这种情况下,最好使用asynctask,它在不同的线程上运行,并在完成时将结果返回给UI线程。
这里有另一种方法,基本上可以帮助你忘记请求的整个管理。 它基于异步队列方法和基于可回调/回调的响应。 主要的好处是通过使用这个方法,你可以使整个过程(请求,获取和解析响应,对数据库的sabe)完全透明。 一旦你得到响应代码,工作已经完成。 之后,你只需要拨打你的数据库,你就完成了。 当你的活动不活跃的时候,会有什么样的问题会有帮助。 这里会发生的是,你将所有的数据保存在本地数据库中,但是响应不会被你的活动处理,这是理想的方式。
我在这里写了一个通用的方法http://ugiagonzalez.com/2012/07/02/theres-life-after-asynctasks-in-android/
我将在即将发布的帖子中加入具体的示例代码。 希望它有帮助,随时与我联系分享方法和解决潜在的疑虑或问题。
罗比提供了一个很好的答案,但我可以看到你仍然在寻找更多的信息。 我实现了REST API调用easy但是错误的方式。 直到观看这个Google I / O视频 ,我才明白我出错的地方。 这不像使用HttpUrlConnection get / put调用将AsyncTask放在一起那么简单。