Restful API服务

问题描述:

我正在寻找一个服务,我可以使用它来调用基于Web的REST API。Restful API服务

基本上我想在app 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和绑定:

// 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客户端实现非常有帮助。 Dobjanschi的演示文稿转录为PDF:https://drive.google.com/file/d/0B2dn_3573C3RdlVpU2JBWXdSb3c/edit?usp=sharing – 2013-12-27 05:34:41

+0

由于许多人都推荐Virgil Dobjanschi的演示文稿,并且现在IO 2010的链接已损坏,因此这里是直接链接到YT视频:http://www.youtube.com/watch?v = xHXn3Kg2IQE – 2014-03-31 19:20:27

如果您的服务将是,那么你使它方式复杂得多,它需要你的应用程序的一部分。由于您从RESTful Web服务获取某些数据的用例很简单,因此您应该查看ResultReceiverIntentService

此服务+ ResultReceiver模式的工作方式是,当您想要执行某项操作时,通过启动或绑定到服务startService()。您可以指定操作来执行并通过Intent中的附加组件传递ResultReceiver(活动)。

在您实施onHandleIntent的服务中执行意图中指定的操作。当操作完成后,您使用传入的ResultReceiver返回send一条消息回到活动,在该点onReceiveResult将被调用。

因此,例如,您想从Web服务中提取一些数据。

  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 2010-07-08 02:16:13

+0

是的。一旦你看到它的工作,这很简单。 – 2010-07-08 02:41:09

+1

非常好 - 谢谢! 我最终通过谷歌iOS应用程序和哇......这是非常复杂的,但我有一些工作,现在我只需要解决它为什么工作!但是,是的,这是我工作的基本模式。 非常感谢。 – Martyn 2010-07-08 13:47:08

此外,当我打 后(Config.getURL(“登录“), 值)应用程序似乎暂停为 while(似乎很奇怪 - 寿ught想法 后面的服务是它运行在 不同的线程!)

不,你必须创建线程自己,本地服务在默认情况下,UI线程中运行。

可以说我想在一个事件onItemClicked()的按钮上启动服务。接收机制在这种情况下将不起作用,因为: -
a)我从onItemClicked() 将接收机传递给了服务(与Intent相同)b)活动移至背景。在onPause()中,我将ResultReceiver中的接收者引用设置为null以避免泄露Activity。
c)活动被破坏。
d)再次创建活动。但是,此时服务将无法对该活动进行回调,因为该接收者引用丢失。
有限广播的机制或的PendingIntent似乎更加有用在这样scenarios-指Notify activity from service

+1

你说的话有问题。那就是当活动移动到背景时它不会被销毁......所以接收器仍然存在,并且活动上下文也是。 – DArkO 2011-09-05 06:31:12

+0

@DArkO当活动暂停或停止时,它可以在内存不足的情况下被Android系统杀死。请参阅[活动生命周期](https://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle)。 – jk7 2017-03-14 17:37:42

注意,从罗比池解决方案以某种方式缺乏:在这种方式,您只允许在待办事项一个API调用因为IntentService一次只处理一个意图。通常你想执行并行API调用。如果你想要这个,你必须扩展Service而不是IntentService并创建你自己的线程。

+1

您仍然可以通过将webservice api调用委托给executor-thread-service(作为IntentService派生类的成员变量提供)来对IntentService执行多个调用 – Viren 2011-07-25 08:05:32

我知道@Martyn不希望完整的代码,但是我觉得这个注​​释了好这个问题:

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

Foursquared Android版open-source,并有一个有趣的代码模式与Foursquare进行交互REST API。

+3

这是一个非常棒的链接,感谢您发布它! – Martyn 2011-09-13 12:08:33

开发Android REST客户端应用程序对我来说是一个很棒的资源。演讲者没有显示任何代码,他只是在设计考虑和技术上将Android中的一个坚实的Rest Api放在一起。如果你的播客有点不喜欢,我建议至少给一个听一个听,但个人来说,我已经听过这样的4到5次了,我可能会再听一次。

Developing Android REST client applications
作者:维吉尔Dobjanschi
说明:

本次会议将介绍建筑方面的考虑开发Android平台的RESTful应用程序。它专注于Android平台特有的设计模式,平台集成和性能问题。

而且有我真的没有在我的API的第一个版本,我已经有重构

+4

+1这包含您开始时不会想到的各种考虑事项。 – 2011-09-21 14:29:57

+0

是的,我第一次尝试开发一个Rest客户端几乎就是他对什么都不做的描述(谢天谢地,在我看到这个之前我意识到这很错误)。我对此表示赞同。 – Terrance 2011-09-21 14:33:30

+0

我已经多次看过此视频,并且正在实施第二种模式。我的问题是,我需要使用复杂数据库模型中的事务来更新来自服务器的新数据的本地数据,而ContentProvider接口并不能为我提供一种方法。你有什么建议吗,Terrance? – 2011-10-19 20:06:17

此外,当我打的岗位上做出了这么多的因素(Config.getURL(“登录” )值)的应用程序似乎停顿了一会儿(似乎不可思议! - 想到了一个服务背后的想法是,它运行在不同的线程)

在这种情况下,它能够更好地使用的AsyncTask,它运行在一个不同的线程并在完成时将结果返回给UI线程。

这里还有另一种方法,基本上可以帮助您忘记请求的整个管理。它基于异步队列方法和基于可回调/回调的响应。 主要优点是通过使用这种方法,您可以使整个过程(请求,获取和解析响应,sabe到数据库)对您完全透明。一旦你得到响应代码,工作已经完成。之后,你只需要打个电话给你的分贝,你就完成了。 当您的活动未处于活动状态时,会发生什么问题也会有所帮助。 这里会发生什么是您将所有数据保存在本地数据库中,但响应不会被您的活动处理,这是理想的方式。

我写了一个通用的方法在这里 http://ugiagonzalez.com/2012/07/02/theres-life-after-asynctasks-in-android/

我会投入具体的示例代码中即将到来的职位。 希望它有帮助,随时与我联系分享方法和解决潜在的疑虑或问题。

+0

无效链接。该域名已过期。 – jk7 2017-03-14 16:54:06

只是想指出你所有的方向,我把一个独立的类,它包含了所有的功能。

http://github.com/StlTenny/RestService

它执行请求作为无阻塞,并且返回以容易的结果,以实现处理程序。甚至附带一个示例实现。

罗比提供了一个很好的答案,尽管我可以看到你仍然在寻找更多的信息。我实现了REST API调用easy但错误的方式。直到看到这个Google I/O video,我才明白我出错的地方。这不像使用HttpUrlConnection get/put调用将AsyncTask放在一起那么简单。

+0

死链接。以下是更新后的版本 - [Google I/O 2010 - Android REST客户端应用程序](http://goo.gl/95zfmf) – zim 2014-04-03 20:23:04

我强烈推荐REST客户端Retrofit

我发现这篇写得很好的博客文章非常有帮助,它还包含简单的示例代码。 笔者采用Retrofit使网络电话和Otto实现数据总线模式:

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