安卓REST客户端,示例?

117
即使这个帖子已经有了被接受的答案,也可以自由地提出其他想法,只要你使用或喜欢它们。

我遇到了这些文章:

这引导我观看了关于REST客户端应用程序的Google I / O 2010视频。

自从现在开始,我一直在我的应用程序控制器类中将REST组件创建为静态组件。
从现在开始,我认为我应该改变这种模式。某人指出Google IOSched应用程序是如何在Android上编写REST客户端的很好的示例。其他人说这种方式太过复杂。
那么,有人能否以简短简单的方式展示最佳实践呢?IOSched应用程序对于示例用例来说太复杂了。

你好,通常我会为Web服务开发一个名为“ws”的独立包,我有一个通用类叫做“WebServicUtils.java”。WebServiceUtils.java类中有访问Web服务的方法。我不确定我的技术是否最佳,但每次我将我的ws包复制到Android应用程序中时,它都是可重用的。如果您想了解更多关于我的技术,请告诉我。 - Ketan Parmar
我认为YouTube评论者没有更好的选择。即使Android的API经常是非常复杂和啰嗦的废话,我们也必须在其内部工作。 - Timmmm
顺便提一下,Mechanoid是一个开源的Eclipse插件,可使用简单的DSL生成JSON-REST客户端。有关如何使用它的指南可以在此处找到:http://robotoworks.com/mechanoid-plugin/service-client-dsl/(我是该插件的作者,抱歉有些自夸!) - Ian Warwick
这对于学习Android REST客户端实现的人来说可能非常有帮助。Dobjanschi的演示已转录为PDF格式,链接:https://drive.google.com/file/d/0B2dn_3573C3RdlVpU2JBWXdSb3c/edit?usp=sharing - Kay Zed
7个回答

101

编辑 2 (2017年10月):

现在是2017年。只需使用Retrofit即可。几乎没有理由使用其他任何东西。

编辑:

原始答案已经超过一年半了。虽然原始答案中提出的概念仍然有效,但正如其他答案所指出的那样,现在有一些库可以让您更轻松地完成此任务。更重要的是,其中一些库会为您处理设备配置更改。

以下是一些我评估过的Rest客户端库列表,请花时间检查它们是否适合您的用例。这个列表并不是详尽无遗的。


原始答案:

介绍一下我的方法,用于在Android上拥有REST客户端。虽然我不认为这是最好的方法:)但请注意,这是我根据我的需求想出来的。如果您的用例需要,您可能需要添加更多的层/增加更多的复杂性。例如,我根本没有本地存储;因为我的应用程序可以容忍丢失几个REST响应。

我的方法仅在内部使用AsyncTask。在我的情况下,我从我的Activity实例"调用"这些任务;但是为了完全考虑到像屏幕旋转这样的情况,您可能会选择从一个Service或类似的位置调用它们。

我有意将我的REST客户端本身作为API。这意味着使用我的REST客户端的应用程序甚至不需要知道实际的REST URL和使用的数据格式。

客户端将具有2个层:

  1. 顶层:此层的目的是提供与REST API功能相对应的方法。例如,您可以为REST API中的每个URL(甚至为GET和POST各一个)拥有一个Java方法。
    这是REST客户端API的入口点。这是应用程序通常使用的层。它可以是单例,但不一定要是。
    REST调用的响应由此层解析为POJO并返回给应用程序。

  2. 这是较低级别的AsyncTask层,它使用HTTP客户端方法实际上进行REST调用。

此外,我选择使用回调机制将AsyncTask的结果传递回应用程序。
够了文字。现在让我们看一些代码。假设我们有一个REST API URL:http://myhypotheticalapi.com/user/profile 顶层可能如下所示:
   /**
 * Entry point into the API.
 */
public class HypotheticalApi{   
    public static HypotheticalApi getInstance(){
        //Choose an appropriate creation strategy.
    }
    
    /**
     * Request a User Profile from the REST server.
     * @param userName The user name for which the profile is to be requested.
     * @param callback Callback to execute when the profile is available.
     */
    public void getUserProfile(String userName, final GetResponseCallback callback){
        String restUrl = Utils.constructRestUrlForProfile(userName);
        new GetTask(restUrl, new RestTaskCallback (){
            @Override
            public void onTaskComplete(String response){
                Profile profile = Utils.parseResponseAsProfile(response);
                callback.onDataReceived(profile);
            }
        }).execute();
    }
    
    /**
     * Submit a user profile to the server.
     * @param profile The profile to submit
     * @param callback The callback to execute when submission status is available.
     */
    public void postUserProfile(Profile profile, final PostCallback callback){
        String restUrl = Utils.constructRestUrlForProfile(profile);
        String requestBody = Utils.serializeProfileAsString(profile);
        new PostTask(restUrl, requestBody, new RestTaskCallback(){
            public void onTaskComplete(String response){
                callback.onPostSuccess();
            }
        }).execute();
    }
}


/**
 * Class definition for a callback to be invoked when the response data for the
 * GET call is available.
 */
public abstract class GetResponseCallback{
    
    /**
     * Called when the response data for the REST call is ready. <br/>
     * This method is guaranteed to execute on the UI thread.
     * 
     * @param profile The {@code Profile} that was received from the server.
     */
    abstract void onDataReceived(Profile profile);
    
    /*
     * Additional methods like onPreGet() or onFailure() can be added with default implementations.
     * This is why this has been made and abstract class rather than Interface.
     */
}

/**
 * 
 * Class definition for a callback to be invoked when the response for the data 
 * submission is available.
 * 
 */
public abstract class PostCallback{
    /**
     * Called when a POST success response is received. <br/>
     * This method is guaranteed to execute on the UI thread.
     */
    public abstract void onPostSuccess();

}

请注意,此应用程序不直接使用由REST API返回的JSON或XML(或其他任何格式)。相反,应用程序只看到bean Profile
然后,较低层(AsyncTask层)可能如下所示:
/**
 * An AsyncTask implementation for performing GETs on the Hypothetical REST APIs.
 */
public class GetTask extends AsyncTask<String, String, String>{
    
    private String mRestUrl;
    private RestTaskCallback mCallback;
    
    /**
     * Creates a new instance of GetTask with the specified URL and callback.
     * 
     * @param restUrl The URL for the REST API.
     * @param callback The callback to be invoked when the HTTP request
     *            completes.
     * 
     */
    public GetTask(String restUrl, RestTaskCallback callback){
        this.mRestUrl = restUrl;
        this.mCallback = callback;
    }
    
    @Override
    protected String doInBackground(String... params) {
        String response = null;
        //Use HTTP Client APIs to make the call.
        //Return the HTTP Response body here.
        return response;
    }
    
    @Override
    protected void onPostExecute(String result) {
        mCallback.onTaskComplete(result);
        super.onPostExecute(result);
    }
}

    /**
     * An AsyncTask implementation for performing POSTs on the Hypothetical REST APIs.
     */
    public class PostTask extends AsyncTask<String, String, String>{
        private String mRestUrl;
        private RestTaskCallback mCallback;
        private String mRequestBody;
        
        /**
         * Creates a new instance of PostTask with the specified URL, callback, and
         * request body.
         * 
         * @param restUrl The URL for the REST API.
         * @param callback The callback to be invoked when the HTTP request
         *            completes.
         * @param requestBody The body of the POST request.
         * 
         */
        public PostTask(String restUrl, String requestBody, RestTaskCallback callback){
            this.mRestUrl = restUrl;
            this.mRequestBody = requestBody;
            this.mCallback = callback;
        }
        
        @Override
        protected String doInBackground(String... arg0) {
            //Use HTTP client API's to do the POST
            //Return response.
        }
        
        @Override
        protected void onPostExecute(String result) {
            mCallback.onTaskComplete(result);
            super.onPostExecute(result);
        }
    }
    
    /**
     * Class definition for a callback to be invoked when the HTTP request
     * representing the REST API Call completes.
     */
    public abstract class RestTaskCallback{
        /**
         * Called when the HTTP request completes.
         * 
         * @param result The result of the HTTP request.
         */
        public abstract void onTaskComplete(String result);
    }

以下是一个应用程序如何在ActivityService中使用API的示例:
HypotheticalApi myApi = HypotheticalApi.getInstance();
        myApi.getUserProfile("techie.curious", new GetResponseCallback() {

            @Override
            void onDataReceived(Profile profile) {
                //Use the profile to display it on screen, etc.
            }
            
        });
        
        Profile newProfile = new Profile();
        myApi.postUserProfile(newProfile, new PostCallback() {
            
            @Override
            public void onPostSuccess() {
                //Display Success
            }
        });

我希望评论足以解释设计;但如果需要更多信息,我很乐意提供。


1
这可能并不值得一提,因为这并没有遵循Virgil Dobjanschi所描述的标准RESTful MVC模式。你需要整合一个完整的ContentProvider层,使用一个SQLite数据库,然后在应用程序中直接使用它。否则,这是一个很好的、轻量级的Android REST客户端。 - Display name
1
有一件小事,你需要在那些Get/Post任务上调用execute。 - Mo Kargas
1
这真的很棒。我该如何使GetResponseCallback更通用,以便它不仅返回Profile,或者您建议为API中每种类型的数据创建单独的GetResponseCallback? - user901309
1
@MichaelHerbig 是的,有办法使 GetResponseCallback 更通用。我喜欢的方法是使用 标记接口:比如 interface IGetResopnse{} 来表示所有可能成为响应的类。然后,我有 class Profile implements IGetResponse 等等。最后,使用 IGetResponse 作为上限,将 GetResponseCallback 设为通用类型public abstract class GetResponseCallback<? extends IGetResponse> - curioustechizen
RoboSplice在其Github页面上有一个相当不错的竞争对手列表:https://github.com/octo-online/robospice - Victor Sergienko
显示剩余2条评论

11
《开发Android REST客户端应用程序》一文引起了很多讨论,因为在会话期间没有展示源代码,也没有在会后提供。
我所知道的唯一参考实现(如果您知道更多,请评论)可在Datadroid找到(Google IO会议在/presentation下提到)。这是一个库,可在您自己的应用程序中使用。
第二个链接询问“最佳”REST框架,在stackoverflow上进行了广泛讨论。对我而言,应用程序大小比实现的性能更重要。
  • 通常我使用简单的org.json实现,它自API级别1以来就是Android的一部分,因此不会增加应用程序大小。
  • 对我而言,有趣的信息是在JSON解析器性能的评论中找到的:自Android 3.0 Honeycomb以来,GSON的流解析器作为android.util.JsonReader包含在内。不幸的是,这些评论已不再可用。
  • Spring Android(我有时使用)支持Jackson和GSON。《Spring Android RestTemplate模块文档》指向一个示例应用程序
因此,对于较为复杂的场景,我会选择org.json或GSON。对于org.json实现的架构,我使用代表服务器用例(例如findPerson、getPerson)的静态类。我从服务中调用这个功能,并使用实用程序类执行映射(特定于项目)和网络IO(用于普通GET或POST的REST模板)。我尽量避免使用反射。

4
《Programming Android》这本O'Reilly书籍在第12-13章节中展示了Dobjanschi的RESTful MVC模式的完整实现。 - Display name
感谢提示:我在亚马逊上找到了这句话:“第12章和第13章涉及内容提供程序。通过示例代码和样例应用程序的广泛处理,让我对这项技术的工作原理以及如何在实际编程情况下使用它有了几个新的见解。使用URI存储和引用数据的内容提供程序框架是Android操作系统的一项新特性。非常好地阐述了这项技术的步骤!” - ChrLipp
2
代码在 https://github.com/bmeike/ProgrammingAndroid2Examples (但是章节缺失,在第一版的代码 https://github.com/bmeike/ProgrammingAndroidExamples 中可以找到它们) - ChrLipp
有人能在ICS+上运行这段代码吗?FinchVideo示例下的待办事项文件简洁地说明了“在ICS下崩溃”。购买这本书后发现代码示例已经失效,我感到有些失望... - eageranalyst

7
请勿使用AsynTask执行网络请求或需要持久化的操作。Async Task与您的Activity紧密关联,如果用户更改屏幕方向,因为应用程序被重新创建,AsyncTask将停止。
我建议您使用Intent Service和ResultReceiver实现服务模式。看一下RESTDroid。这是一个库,允许您异步执行任何类型的REST请求,并通过实现Virgil Dobjanschi的服务模式的请求监听器通知您的UI。

3

有另一个库,其API更加清晰且数据类型更安全。 https://github.com/kodart/Httpzoid

以下是一个简单的使用示例:

Http http = HttpFactory.create(context);
http.post("http://example.com/users")
    .data(new User("John"))
    .execute();

或者使用回调函数让它更加复杂。
Http http = HttpFactory.create(context);
http.post("http://example.com/users")
    .data(new User("John"))
    .handler(new ResponseHandler<Void>() {
        @Override
        public void success(Void ignore, HttpResponse response) {
        }

        @Override
        public void error(String message, HttpResponse response) {
        }

        @Override
        public void failure(NetworkError error) {
        }

        @Override
        public void complete() {
        }
    }).execute();

这是一项非常前沿的技术,看起来十分有前途。


它似乎是在AsyncTask上运行的,这对于长时间运行的请求和活动之间的切换不太好,因为当Activity退出时,AsyncTask将被终止。 - Malachiasz

1
免责声明:我参与了rest2mobile开源项目。
另一个作为REST客户端的替代方案是使用rest2mobile
该方法略有不同,它使用具体的rest示例生成REST服务的客户端代码。该代码用本地Java方法和POJO替换REST URL和JSON负载。它还自动处理服务器连接、异步调用和POJO到/from JSON转换。
请注意,此工具有不同的版本(cli、插件、android/ios/js支持),您可以使用android studio插件将API直接生成到您的应用程序中。
所有代码都可以在github这里找到。

3
请将第一个链接替换为实际目标链接,而非宣传您的网站。 - Skydan

1
有很多库可供使用,我正在使用这个:https://github.com/nerde/rest-resource。这是由我创建的,正如您在文档中所看到的那样,它比其他库更清晰、更简单。虽然它不是专门为 Android 设计的,但我正在使用它,并且表现非常出色。
它支持 HTTP 基本认证。它会自动序列化和反序列化 JSON 对象,让你轻松愉悦。如果您的 API 类似于 Rails,您一定会喜欢它的。

0

我们已经将我们的轻量级异步REST客户端库开源,如果您有最小的要求并且不想自己处理多线程,那么您可能会发现它很有用 - 它非常适合基本通信,但不是完整的REST客户端库。

它被称为libRESTfulClient,可以在GitHub上找到


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接