喜马拉雅第三方客户端开发(接口和接口数据解析)。

前言:最近闲来无事,看了网上豆瓣的第三方客户端,手有点痒,决定自己动手开发一个客户端,比较了荔枝和喜马拉雅,决定开发喜马拉雅的第三方客户端。

客户端使用了WPF开发。

1.抓取接口;

首先得解决接口数据的问题,使用了手机端的喜马拉雅,抓包看了接口。这里推荐使用fiddler2这个工具。从图中可以看到接口信息,包括接口地址和参数的一些数据。

2.通过http获取接口数据和转换接口数据格式。

这里提供一个HttpWebRequestOpt类来获取接口数据。

using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Text;

namespace XIMALAYA.PCDesktop.Untils
{
    /// <summary>
    /// 数据操作类
    /// </summary>
    public class HttpWebRequestOpt
    {
        /// <summary>
        ///
        /// </summary>
        public string UserAgent { get; set; }
        /// <summary>
        /// cookie
        /// </summary>
        private CookieContainer Cookies { get; set; }

        private HttpWebRequestOpt()
        {
            //FileVersionInfo myFileVersion = FileVersionInfo.GetVersionInfo(Path.Combine(Directory.GetCurrentDirectory(), "XIMALAYA.PCDesktop.exe"));

            this.Cookies = new CookieContainer();
            //this.UserAgent = string.Format("ting-ximalaya_v{0} name/ximalaya os/{1} osName/{2}", myFileVersion.FileVersion, OSInfo.Instance.OsInfo.VersionString, OSInfo.Instance.OsInfo.Platform.ToString());
            //this.Cookies.Add(new Cookie("4&_token", "935&d63fef280403904a8c0a5ee0dbe228f2d064", "/", ".ximalaya.com"));
        }
        /// <summary>
        /// 添加cookie
        /// </summary>
        /// <param name="cookie"></param>
        public void SetCookies(Cookie cookie)
        {
            this.Cookies.Add(cookie);
        }
        /// <summary>
        /// 添加cookie
        /// </summary>
        /// <param name="cookie"></param>
        public void SetCookies(string key, string val)
        {
            this.Cookies.Add(new Cookie(key, val, "/", ".ximalaya.com"));
        }
        /// <summary>
        /// 通过POST方式发送数据
        /// </summary>
        /// <param name="Url">url</param>
        /// <param name="postDataStr">Post数据</param>
        /// <returns></returns>
        public string SendDataByPost(string Url, string postDataStr)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);

            request.CookieContainer = this.Cookies;
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = postDataStr.Length;
            request.UserAgent = this.UserAgent;
            Stream myRequestStream = request.GetRequestStream();
            StreamWriter myStreamWriter = new StreamWriter(myRequestStream, Encoding.GetEncoding("gb2312"));
            myStreamWriter.Write(postDataStr);
            myStreamWriter.Close();

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            Stream myResponseStream = response.GetResponseStream();
            StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
            string retString = myStreamReader.ReadToEnd();
            myStreamReader.Close();
            myResponseStream.Close();

            return retString;
        }
        /// <summary>
        /// 通过GET方式发送数据
        /// </summary>
        /// <param name="Url">url</param>
        /// <param name="postDataStr">GET数据</param>
        /// <returns></returns>
        public string SendDataByGET(string Url, string postDataStr)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url + (postDataStr == "" ? "" : "?") + postDataStr);

            request.CookieContainer = this.Cookies;
            request.Method = "GET";
            request.ContentType = "text/html;charset=UTF-8";
            request.UserAgent = this.UserAgent;

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            Stream myResponseStream = response.GetResponseStream();
            StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
            string retString = myStreamReader.ReadToEnd();
            myStreamReader.Close();
            myResponseStream.Close();

            return retString;
        }
        /// <summary>
        /// 异步通过POST方式发送数据
        /// </summary>
        /// <param name="Url">url</param>
        /// <param name="postDataStr">GET数据</param>
        /// <param name="async"></param>
        public void SendDataByPostAsyn(string Url, string postDataStr, AsyncCallback async)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);

            request.CookieContainer = this.Cookies;
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = postDataStr.Length;
            request.UserAgent = this.UserAgent;
            Stream myRequestStream = request.GetRequestStream();
            StreamWriter myStreamWriter = new StreamWriter(myRequestStream, Encoding.GetEncoding("gb2312"));
            myStreamWriter.Write(postDataStr);
            myStreamWriter.Close();
            myRequestStream.Close();
            request.BeginGetResponse(async, request);
        }
        /// <summary>
        /// 异步通过GET方式发送数据
        /// </summary>
        /// <param name="Url">url</param>
        /// <param name="postDataStr">GET数据</param>
        /// <param name="async"></param>
        /// <returns></returns>
        public void SendDataByGETAsyn(string Url, string postDataStr, AsyncCallback async)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url + (postDataStr == "" ? "" : "?") + postDataStr);

            request.CookieContainer = this.Cookies;
            request.Method = "GET";
            request.ContentType = "text/html;charset=UTF-8";
            request.UserAgent = this.UserAgent;

            request.BeginGetResponse(async, request);
        }
        /// <summary>
        /// 使用HttpWebRequest POST图片等文件,带参数
        /// </summary>
        /// <param name="url"></param>
        /// <param name="file"></param>
        /// <param name="paramName"></param>
        /// <param name="contentType"></param>
        /// <param name="nvc"></param>
        /// <returns></returns>
        public string HttpUploadFile(string url, string file, string paramName, string contentType, NameValueCollection nvc)
        {
            string result = string.Empty;
            string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
            byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");

            HttpWebRequest wr = (HttpWebRequest)WebRequest.Create(url);
            wr.ContentType = "multipart/form-data; boundary=" + boundary;
            wr.Method = "POST";
            wr.KeepAlive = true;
            wr.Credentials = System.Net.CredentialCache.DefaultCredentials;

            Stream rs = wr.GetRequestStream();

            string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
            foreach (string key in nvc.Keys)
            {
                rs.Write(boundarybytes, 0, boundarybytes.Length);
                string formitem = string.Format(formdataTemplate, key, nvc[key]);
                byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
                rs.Write(formitembytes, 0, formitembytes.Length);
            }
            rs.Write(boundarybytes, 0, boundarybytes.Length);

            string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n";
            string header = string.Format(headerTemplate, paramName, file, contentType);
            byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
            rs.Write(headerbytes, 0, headerbytes.Length);

            FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read);
            byte[] buffer = new byte[4096];
            int bytesRead = 0;
            while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
            {
                rs.Write(buffer, 0, bytesRead);
            }
            fileStream.Close();

            byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
            rs.Write(trailer, 0, trailer.Length);
            rs.Close();

            WebResponse wresp = null;
            try
            {
                wresp = wr.GetResponse();
                Stream stream2 = wresp.GetResponseStream();
                StreamReader reader2 = new StreamReader(stream2);

                result = reader2.ReadToEnd();
            }
            catch (Exception ex)
            {
                if (wresp != null)
                {
                    wresp.Close();
                    wresp = null;
                }
            }
            finally
            {
                wr = null;
            }

            return result;
        }
    }
}

接口地址:http://mobile.ximalaya.com/m/index_subjects;接口数据如下:

{"ret":0,"focusImages":{"ret":0,"list":[{"id":1384,"shortTitle":"DJ张羊 谢谢你的美好(感恩特辑)","longTitle":"DJ张羊 谢谢你的美好(感恩特辑)","pic":"http://fdfs.xmcdn.com/group5/M06/A8/1D/wKgDtlR1oKHSsFngAAFlxraThWc933.jpg","type":3,"trackId":4428642,"uid":1315711},{"id":1388,"shortTitle":"小清新女神11月榜","longTitle":"小清新女神11月榜","pic":"http://fdfs.xmcdn.com/group5/M04/A8/25/wKgDtlR1owzjFmE7AAF3pxnuNxg222.jpg","type":5},{"id":1383,"shortTitle":"王朔《你也不会年轻很久》 静波播讲","longTitle":"王朔《你也不会年轻很久》 静波播讲","pic":"http://fdfs.xmcdn.com/group5/M03/A8/1C/wKgDtlR1oE-xEoq6AAEfe5PJmt4656.jpg","type":3,"trackId":4417987,"uid":12512006},{"id":1382,"shortTitle":"楚老湿大课堂(长效图-娱乐)","longTitle":"楚老湿大课堂(长效图-娱乐)","pic":"http://fdfs.xmcdn.com/group5/M06/A8/19/wKgDtlR1n7ORluWTAAFuKujnTB0163.jpg","type":3,"trackId":4422955,"uid":8401915},{"id":1365,"shortTitle":"唱响喜玛拉雅(活动图)","longTitle":"唱响喜玛拉雅(活动图)","pic":"http://fdfs.xmcdn.com/group5/M06/A5/6C/wKgDtVR0VFXA3LWXAAMruRW5vnI973.png","type":8,"url":"http://activity.ximalaya.com/activity-web/activity/57?app=iting"},{"id":1363,"shortTitle":"欧莱雅广告图24、25、27、28","longTitle":"欧莱雅广告图24、25、27、28","pic":"http://fdfs.xmcdn.com/group5/M05/A0/32/wKgDtlRyla6AnGneAAF2kpKTc2I036.jpg","type":4,"url":"http://ma8.qq.com/wap/index.html?utm_source=xmly&utm_medium=113282464&utm_term=&utm_content=xmly01&utm_campaign=CPD_LRL_MEN_MA8%20Campaign_20141118_MO_other"}]},"categories":{"ret":0,"data":[]},"latest_special":{"title":"感恩的心 感谢有你","coverPathSmall":"http://fdfs.xmcdn.com/group5/M04/AA/9B/wKgDtlR2q_jxMbU-AATUrGYasdg092_mobile_small.jpg","coverPathBig":"http://fdfs.xmcdn.com/group5/M04/AA/9B/wKgDtlR2q_jxMbU-AATUrGYasdg092.jpg","coverPathBigPlus":null,"isHot":false},"latest_activity":{"title":"唱响喜马拉雅-每年四季,打造你的音乐梦想","coverPathSmall":"http://fdfs.xmcdn.com/group5/M06/A4/DA/wKgDtlR0UQLik8xMABBJsD5tCNU868_mobile_small.jpg","isHot":true},"recommendAlbums":{"ret":0,"maxPageId":250,"count":1000,"list":[{"id":232357,"title":"今晚80后脱口秀 2014","coverSmall":"http://fdfs.xmcdn.com/group4/M01/19/5A/wKgDtFMsAq3COyRPAAUQ_GUt96k211_mobile_small.jpg","playsCounts":29318050},{"id":287570,"title":"大漠谣(风中奇缘)","coverSmall":"http://fdfs.xmcdn.com/group4/M07/7D/90/wKgDtFRGQFPzpmIsAAQ3HgQ6JRU598_mobile_small.jpg","playsCounts":669091},{"id":214706,"title":"段子来了 采采","coverSmall":"http://fdfs.xmcdn.com/group3/M04/64/9D/wKgDsVJ6DnSy_6Q7AAEXoFUKDKE679_mobile_small.jpg","playsCounts":29},{"id":233577,"title":"财经郎眼 2014","coverSmall":"http://fdfs.xmcdn.com/group2/M02/4E/2F/wKgDsFLTVG7RU3ZQAAPtxcqJYug831_mobile_small.jpg","playsCounts":8877870}]}}

有了数据就需要解析数据。接口数据为JSON格式,这里使用了FluentJson这个开源项目,可以把类与JSON数据互相转换。官网上有相关的源码和实例,可以下载看一下。下面介绍使用方法。

就针对上面的那个发现也接口我定义了一个类。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using XIMALAYA.PCDesktop.Core.Models.Album;
using XIMALAYA.PCDesktop.Core.Models.Category;
using XIMALAYA.PCDesktop.Core.Models.FocusImage;
using XIMALAYA.PCDesktop.Core.Models.Subject;
using XIMALAYA.PCDesktop.Core.Models.User;

namespace XIMALAYA.PCDesktop.Core.Models.Discover
{
    public class SuperExploreIndexResult : BaseResult
    {
        /// <summary>
        /// 焦点图
        /// </summary>
        public FocusImageResult FocusImages { get; set; }
        /// <summary>
        /// 分类
        /// </summary>
        public CategoryResult Categories { get; set; }
        /// <summary>
        /// 最后一个专题
        /// </summary>
        public object LatestSpecial { get; set; }
        /// <summary>
        /// 最后一个活动
        /// </summary>
        public object LatestActivity { get; set; }
        /// <summary>
        /// 推荐专辑
        /// </summary>
        public AlbumInfoResult1 Albums { get; set; }

        public SuperExploreIndexResult()
            : base()
        {
            this.doAddMap(() => this.FocusImages, "focusImages");
            this.doAddMap(() => this.Categories, "categories");
            this.doAddMap(() => this.LatestActivity, "latest_activity");
            this.doAddMap(() => this.LatestSpecial, "latest_special");
            this.doAddMap(() => this.Albums, "recommendAlbums");
        }
    }
}

这个SuperExploreIndexResult类的构造函数对应了接口数据中的射影关系。

生成的映射类如下:

// <auto-generated>
//     此代码由工具生成。
//     对此文件的更改可能会导致不正确的行为,并且如果
//     重新生成代码,这些更改将会丢失。
//        如存在本生成代码外的新需求,请在相同命名空间下创建同名分部类实现 SuperExploreIndexResultConfigurationAppend 分部方法。
// </auto-generated>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using FluentJson.Configuration;
using FluentJson;
using XIMALAYA.PCDesktop.Core.Data.Decorator;
using XIMALAYA.PCDesktop.Core.Models.Discover;

namespace XIMALAYA.PCDesktop.Core.Data
{
    /// <summary>
    ///     SuperExploreIndexResult
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public partial class SuperExploreIndexResultDecorator<T> : Decorator<T>
    {
        partial void doAddOtherConfig();
        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="result"></typeparam>
        public SuperExploreIndexResultDecorator(Result<T> result)
            : base(result)
        {

        }
        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="result"></typeparam>
        public override void doAddConfig()
        {
            base.doAddConfig();
            this.Config.MapType<SuperExploreIndexResult>(map => map
                                    .Field<System.Int32>(field => field.Ret, type => type.To("ret"))
                    .Field<System.String>(field => field.Message, type => type.To("msg"))
                    .Field<XIMALAYA.PCDesktop.Core.Models.FocusImage.FocusImageResult>(field => field.FocusImages, type => type.To("focusImages"))
                    .Field<XIMALAYA.PCDesktop.Core.Models.Category.CategoryResult>(field => field.Categories, type => type.To("categories"))
                    .Field<System.Object>(field => field.LatestActivity, type => type.To("latest_activity"))
                    .Field<System.Object>(field => field.LatestSpecial, type => type.To("latest_special"))
                    .Field<XIMALAYA.PCDesktop.Core.Models.Album.AlbumInfoResult1>(field => field.Albums, type => type.To("recommendAlbums"))
            );
            this.doAddOtherConfig();
        }
    }
}

这里只列出了一个SuperExploreIndexResult类,还有CategoryResult,FocusImageResult,AlbumInfoResult1这三个类,也做了同样的映射。这样这个接口的数据最终就可以映射为SuperExploreIndexResult类了。总之,把接口中JSON数据中的对象是全部需要隐射的。

下面演示了如何调用上面的映射类。代码中所有带Decorator后缀的类都是映射类。采用了下装饰模式。

using System;
using System.ComponentModel.Composition;
using FluentJson;
using XIMALAYA.PCDesktop.Core.Data;
using XIMALAYA.PCDesktop.Core.Data.Decorator;
using XIMALAYA.PCDesktop.Core.Models.Discover;
using XIMALAYA.PCDesktop.Core.Models.Tags;
using XIMALAYA.PCDesktop.Untils;

namespace XIMALAYA.PCDesktop.Core.Services
{
    /// <summary>
    /// 发现页接口数据
    /// </summary>
    [Export(typeof(IExploreService))]
    class ExploreService : ServiceBase<SuperExploreIndexResult>, IExploreService
    {
        #region 属性

        private ServiceParams<SuperExploreIndexResult> SuperExploreIndexResult { get; set; }

        #endregion

        #region IExploreService 成员

        /// <summary>
        /// 获取发现首页数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="act"></param>
        /// <param name="param"></param>
        public void GetData<T>(Action<object> act, T param)
        {
            Result<SuperExploreIndexResult> result = new Result<SuperExploreIndexResult>();

            new SuperExploreIndexResultDecorator<SuperExploreIndexResult>(result);
            //分类
            new CategoryResultDecorator<SuperExploreIndexResult>(result);
            new CategoryDataDecorator<SuperExploreIndexResult>(result);
            //焦点图
            new FocusImageResultDecorator<SuperExploreIndexResult>(result);
            new FocusImageDataDecorator<SuperExploreIndexResult>(result);
            //推荐用户
            //new UserDataDecorator<SuperExploreIndexResult>(result);
            //推荐专辑
            new AlbumInfoResult1Decorator<SuperExploreIndexResult>(result);
            new AlbumData1Decorator<SuperExploreIndexResult>(result);
            //专题列表
            //new SubjectListResultDecorator<SuperExploreIndexResult>(result);
            //new SubjectDataDecorator<SuperExploreIndexResult>(result);

            this.SuperExploreIndexResult = new ServiceParams<SuperExploreIndexResult>(Json.DecoderFor<SuperExploreIndexResult>(config => config.DeriveFrom(result.Config)), act);
            //this.Act = act;
            //this.Decoder = Json.DecoderFor<SuperExploreIndexResult>(config => config.DeriveFrom(result.Config));

            try
            {
                this.Responsitory.Fetch(WellKnownUrl.SuperExploreIndex, param.ToString(), asyncResult =>
                {
                    this.GetDecodeData<SuperExploreIndexResult>(this.GetDataCallBack(asyncResult), this.SuperExploreIndexResult);
                });
            }
            catch (Exception ex)
            {
                this.SuperExploreIndexResult.Act.BeginInvoke(new SuperExploreIndexResult
                {
                    Ret = 500,
                    Message = ex.Message
                }, null, null);
            }
        }

        #endregion
    }
}

如上,只要配置好映射关系,通过T4模板我们可以生成对应的映射关系类。

附上源码

下篇,客户端使用了prism+mef这个框架,单独开发模块,最后组合的方式。未完待续。。。。

时间: 2024-11-09 04:45:39

喜马拉雅第三方客户端开发(接口和接口数据解析)。的相关文章

喜马拉雅第三方客户端开发(界面)。

上一篇说了如何提取接口数据.这里讲下界面用到的一些库. 今天给大家推荐metro扁平化样式UI控件库:MahApps.Metro . 官方地址:http://mahapps.com/guides/quick-start.html 支持控件: Buttons DataGrid Dialogs FlipView Flyouts MetroWindow NumericUpDown ProgressBar ProgressRing RangeSlider Slider SplitButton and D

iOS网络编程开发—JSON和XML数据解析

iOS网络编程开发—JSON解析 一.什么是JSON JSON是一种轻量级的数据格式,一般用于数据交互 服务器返回给客户端的数据,一般都是JSON格式或者XML格式(文件下载除外) JSON的格式很像OC中的字典和数组 {"name" : "jack", "age" : 10} {"names" : ["jack", "rose", "jim"]} 标准JSON格式的

【Qt编程】基于Qt的词典开发系列&lt;九&gt;--JSON数据解析

在上一篇文章<用户登录及API调用的实现>中,我通过程序实现了用户登录及API调用的实现,从而能够实现网络查词.添词的操作.但是,从上文中可以看到,调用API后返回的是JSON格式的数据,如下图所示(在QtCreator中的显示): 为了更好的观察JSON格式,我整理后显示如下: 显然,为了显示查词的结果,我们必须在上图中提取有用的信息,并正确的显示出来.上图中每一行内容的意思我已经在文章<调用网络API>中作了解释.我在词典中选择想要显示的内容有:单词本身.单词ID.中文解释.英

Ace教你一步一步做Android新闻客户端(三) JSON数据解析

对于服务器端来说,返回给客户端的数据格式一般分为html.xml和json这三种格式,现在给大家讲解一下json这个知识点, 1 如何通过json-lib和gson这两个json解析库来对解析我们的json数据, 2 以及如何在我们的Android客户端解析来自服务器端的json数据,并更新到UI当中.(下篇来讲) 一.什么是json json(Javascript Object Notation)是一种轻量级的数据交换格式,相比于xml这种数据交换格式来说,因为解析xml比较的复杂,而且需要编

用Jersey为Android客户端开发Restful Web Service

平时在做Android客户端的时候经常要与服务器之间通信,客户端通过服务端提供的接口获取数据,然后再展示在客户端的界面上,作为Android开发者,我们平时更多的是关注客户端的开发,而对服务端开发的关注相对较少,但是如果我们要自己一个人设计并开发一套完整的系统,我们就必须同时具备客户端和服务端的开发知识,而且我认为同时掌握客户端和服务端的开发技术还是很有用处的,不仅有助于提升我们的架构知识和能力,而且还……你懂得!身边一些做WEB开发的朋友很多都说为客户端开发接口和单纯地做WEB项目并没有很大的

iOS开发笔记3:XML/JSON数据解析

这篇主要总结在iOS开发中XML/JSON数据解析过程用到的方法.XML数据解析主要使用SAX方式的NSXMLParser以及DOM方式的GDataXML,JSON数据解析主要使用NSJSONSerialization. 1.XML解析之NSXMLParser NSXMLParser是iOS原生的XML解析方式,采用SAX方式进行解析,特点是解析的时候从根元素开始,按顺序依次对每一个元素进行解析. 以下图所示XML文档为例进行解析 (1)首先是实例化一个NSXMLParser,设置代理监听解析过

iOS GET、POST数据解析

在实际开发中,JSON数据解析更简单易行,一般均使用json数据解析,因此,程序猿们请务必和后台搞好关系,让他给你json数据.    XML解析: ios SDK提供了NSXMLParser和libxml2两个类库,另外还有很多第三方类库可选:TBXML.TouchXML.KissXML.TinyXML和GDataXML. 解析XML通常有两种方式:DOM和SAX DOM解析XML时,读入整个XML文档并构建一个驻留内存的树结构(节点树),遍历树结构可以检索任意XML节点,读取它的属性和值.

小蚂蚁学习APP接口开发(9)—— APP版本升级的接口方法——处理客户端发来的请求数据

今天的笔记主要来记录最后的一个知识要点,APP客户端版本升级的接口开发. APP的开发主要由伟大而神圣的用户来更新,更新的流程应该是这样的: 开启APP--请求初始化接口init.php--检测是否需要更新---否--首页 ---是--更新 在初始化接口的时候,需要客户端发送几个数据: app_id            客户端id,比如: 1,安卓手机 2,iphone手机 version_id        大版本号id version_mini     小版本号id did        

App开发:模拟服务器数据接口 - MockApi

App开发:模拟服务器数据接口 - MockApi 为了方便app开发过程中,不受服务器接口的限制,便于客户端功能的快速测试,可以在客户端实现一个模拟服务器数据接口的MockApi模块.本篇文章就尝试为使用gradle的android项目设计实现MockApi. 需求概述 在app开发过程中,在和服务器人员协作时,一般会第一时间确定数据接口的请求参数和返回数据格式,然后服务器人员会尽快提供给客户端可调试的假数据接口.不过有时候就算是假数据接口也来不及提供,或者是接口数据格式来回变动--很可能是客