OkHttp框架从入门到放弃,解析图片使用Picasso裁剪,二次封装OkHttpUtils,Post提交表单数据

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: OkHttp框架从入门到放弃,解析图片使用Picasso裁剪,二次封装OkHttpUtils,Post提交表单数据 我们这片博文就来聊聊这个反响很不错的OkHttp了,标题是我恶搞的,本篇将着重详细的分析,探索OkHttp这个框架的使用和封装 一.

OkHttp框架从入门到放弃,解析图片使用Picasso裁剪,二次封装OkHttpUtils,Post提交表单数据

我们这片博文就来聊聊这个反响很不错的OkHttp了,标题是我恶搞的,本篇将着重详细的分析,探索OkHttp这个框架的使用和封装

一.追其原理

Android系统提供了两种HTTP通信类

  • HttpURLConnection
  • HttpClient

Google推荐使用HttpURLConnection,这个没必要多说,事实上,我这篇写的应该算是比较晚了,很多优秀的博文都已经提出了这些观点了,那我也就不好意思重复的说废话了,不过我还是得吐槽一下,HttpURLConnection是在是太难用了,而且功能也实在是太少了,虽然Github上封装的框架还真是不少,不过依然不是特别好用,而Google自己也在寻求解决的办法,如果你看过android4.4的源码,你就应该知道,Google把HttpURLConnection替换成了OkHttp,而OkHttp走到现在,已经是相对来说,比较成熟的框架了,那我们为何不去使用它呢?而且现在学习OkHttp的资料和文章实在是太多了,根本不需要什么学习成本的,搜索一下,马上就有一大堆,既然如此,我们今儿个就来看看这个花姑凉长什么样吧!各位小司机,跟着老司机一起上车吧!

注意,我们使用的IDE是Android Studio

二.使用准备

肯定要配置一下啦,我们首先新建一个工程——OkHttpGo,这名字好听,我就不加demo或者test了,这样显得有点low,项目我们基于5.0 Lollipop来开发

 

而关于OkHttp的官方介绍,大家可以移步这里

  • http://square.github.io/okhttp/

 

如果想看源码,可以去Github上

  • https://github.com/square/okhttp

我们既然要使用,就根据github上来吧,加入依赖,把依赖添加到build.gradle中,当然,他是提供jar的,你如果用Eclipse获取喜欢用jar,你也可以直接下载jar

compile 'com.squareup.okhttp3:okhttp:3.3.1'

因为我们会用到图片解析,所以可以加上Picasso的依赖,关于它的介绍,可以移步

  • http://square.github.io/picasso/

 

这个库,我下篇博文会介绍到,这里你只要知道是这么添加依赖和使用就可以了

compile 'com.squareup.picasso:picasso:2.5.2'

记住,网络的使用,是需要添加权限的哦!

  <!--网络权限-->
  <uses-permission android:name="android.permission.INTERNET"/>

行,大致的配置就到这里OK了,如果你还有什么不清楚的,可以去他们官网或者github上瞧一瞧,看一看,这里再送上一下下载jar的地址吧!

  • OkHttp 2.7.5 Jar下载

OkHttp内部依赖了Okio,这里也提供了jar下载地址

  • Okio 1.8.0 jar下载

三.图片加载

我们先从图片加载说起,最起码先定义一下布局呀

<ImageView
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher" />

 <Button
        android:id="@+id/btn_iv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="图片加载" />

非常简陋的一个布局,就一个button和一个imageview,现在我们就是点击按钮,然后解析显示在控件上,这对于OkHttp来说,应该是怎么使用的呢?注意,现在演示的,都还只是没有封装的前提下,我们首先做一些准备工作

    //成功状态
    private static final int SUCCESS_STATUS = 1;
    //失败状态
    private static final int FAIL_STATUS = 2;

    //图片链接
    private String url = "http://d.3987.com/qiz_141118/004.jpg";

这里我定义了两个常量,分别是解析成功失败的状态,又定义了一张图片的链接,图片是网上的以上美女图片,好的,这些都准备好了,现在我们就可以来书写OkHttp相关的类了

 //图片解析
        btn_iv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //初始化OkHttp
                OkHttpClient client = new OkHttpClient();
                //构建Request,解析链接,这里可选get/post方法
                final Request request = new Request.Builder().get().url(url).build();
                //添加到请求队列
                client.newCall(request).enqueue(new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {
                        //失败
                        Log.i(TAG, "解析失败");
                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        //成功
                        Message message = handler.obtainMessage();
                        //判断,成功就传值
                        if (response.isSuccessful()) {
                            message.what = SUCCESS_STATUS;
                            message.obj = response.body().bytes();
                            handler.sendMessage(message);
                        } else {
                            handler.sendEmptyMessage(FAIL_STATUS);
                        }
                    }
                });
            }
        });

可以看到,它使用和Volley有点类似,个人感觉,其实也就是那么几个步骤,首先初始化,然后设置一下乱七八糟的属性,最后添加到队列中,返回两个回调,成功和失败,是吧,我们这个时候就直接用handler去发消息了

 //子线程
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case SUCCESS_STATUS:
                    //拿值
                    byte[] result = (byte[]) msg.obj;
                    //图片加载
                    Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0, result.length);
                    //设置图片
                    iv.setImageBitmap(bitmap);
                    break;
                case FAIL_STATUS:
                    //失败
                    Log.i(TAG, "解析失败");
                    break;
            }
        }
    };

得出来的效果,用一张图来表示就绰绰有余了

OK,图片解析的就已经实现了

四.图片裁剪

现在呢,我们可以看到是一个美女的图片,但是如果我们图片比较大,而我们不需要这么大,比如长这样?

如果我们不想要这么大的图片,又或者说,我们想要一张正方形一样整齐的图片,我们该怎么去做?还记得我们添加的picasso图片框架吗?他就可以做到,我们可以用它的功能写一个工具类来帮助我们裁剪,这个工具类写起来也没有多麻烦

package com.lgl.okhttpgo;

import android.graphics.Bitmap;

import com.squareup.picasso.Transformation;

/**
 * 裁剪图片
 * Created by LGL on 2016/6/19.
 */
public class TailorImageView implements Transformation {

    @Override
    public Bitmap transform(Bitmap source) {

        //得到原图片的大小,取最小值
        int size = Math.min(source.getWidth(), source.getHeight());
        //长大于宽,还是宽大于长
        int x = (source.getWidth() - size) / 2;
        int y = (source.getHeight() - size) / 2;
        //创建新的bitmap
        Bitmap bitmap = Bitmap.createBitmap(source, x, y, size, size);

        if (bitmap != source) {
            //回收
            source.recycle();
        }
        return bitmap;
    }

    @Override
    public String key() {

        return "lgl";
    }
}

我们使用的话,就在我们解析成功的时候调用就可以了,

case SUCCESS_STATUS:
                    //拿值
                    byte[] result = (byte[]) msg.obj;
                    //图片加载
                    Bitmap bitmap = new TailorImageView().transform(BitmapFactory.decodeByteArray(result, 0, result.length));
                    //设置图片
                    iv.setImageBitmap(bitmap);
                    break;

我们可以是这样的

正方形就搞定了,欧耶!

五.网络框架封装

事实上,我们上面所说的,都还是有些许繁杂了,毕竟我们不应该重复的去写这么多麻烦的代码,对吧,现在我们来对他进行一个封装,而对于OkHttp,他有以下的几个功能

  • 一般的get请求
  • 一般的post请求
  • 基于Http的文件上传
  • 文件下载
  • 加载图片
  • 支持请求回调,直接返回对象,对象集合
  • 支持session的保持

我们要封装一个OkHttp的话,也就是围绕着他的这几个功能来二次开发了,好的,小司机们,我们继续开车吧!污污污污污…..

我们写一个OkHttpUtils,其实还算是比较简单的,因为我们实际上没写多少内容

package com.lgl.okhttpgo;

import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 * OkHttp的封装工具类
 * Created by LGL on 2016/6/19.
 */
public class OkHttpUtils {

    //TAG
    private static final String TAG = OkHttpUtils.class.getSimpleName();

    //声明客户端
    private OkHttpClient client;
    //防止多个线程同时访问所造成的安全隐患
    private volatile static OkHttpUtils okHttpUtils;
    //定义提交类型Json
    private static final MediaType JSON = MediaType.parse("application/json;charset=utf-8");
    //定义提交类型String
    private static final MediaType STRING = MediaType.parse("text/x-markdown;charset=utf-8");
    //子线程
    private Handler handler;

    //构造方法
    private OkHttpUtils() {
        //初始化
        client = new OkHttpClient();
        handler = new Handler(Looper.getMainLooper());
    }


    //单例模式
    public static OkHttpUtils getInstance() {
        OkHttpUtils okUtils = null;
        if (okHttpUtils == null) {
            //线程同步
            synchronized (OkHttpUtils.class) {
                if (okUtils == null) {
                    okUtils = new OkHttpUtils();
                    okHttpUtils = okUtils;
                }
            }
        }
        return okUtils;
    }

    /**
     * 请求的返回结果是json字符串
     *
     * @param jsonValue
     * @param callBack
     */
    private void onsuccessJsonStringMethod(final String jsonValue, final FuncJsonString callBack) {
        handler.post(new Runnable() {
            @Override
            public void run() {
                if (callBack != null) {
                    try {
                        //解析json
                        callBack.onResponse(jsonValue);
                    } catch (Exception e) {

                    }
                }
            }
        });
    }

    /**
     * 求的返回结果是json对象
     *
     * @param jsonValue
     * @param callBack
     */
    private void onsuccessJsonObjectMethod(final String jsonValue, final FuncJsonObject callBack) {
        handler.post(new Runnable() {
            @Override
            public void run() {
                if (callBack != null) {
                    try {
                        callBack.onResponse(new JSONObject(jsonValue));
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }


    /**
     * 求的返回结果是json数组
     *
     * @param data
     * @param callBack
     */
    private void onsuccessJsonByteMethod(final byte[] data, final FuncJsonObjectByte callBack) {
        handler.post(new Runnable() {
            @Override
            public void run() {
                if (callBack != null) {
                    callBack.onResponse(data);
                }
            }
        });
    }

    /**
     * 同步请求,不是很常用,因为会阻塞线程
     *
     * @param url
     * @return
     */
    public String syncGetByURL(String url) {
        //构建一个Request请求
        Request request = new Request.Builder().url(url).build();
        Response response = null;

        try {
            //同步请求数据
            response = client.newCall(request).execute();

            if (response.isSuccessful()) {
                return response.body().string();
            }
        } catch (Exception e) {

        }

        return null;
    }


    /**
     * 请求指定的url,返回的结果是json字符串
     *
     * @param url
     * @param callback
     */
    public void syncJsonStringByURL(String url, final FuncJsonString callback) {
        final Request request = new Request.Builder().url(url).build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.i(TAG, "解析失败");
            }

            //解析成功
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response != null && response.isSuccessful()) {
                    onsuccessJsonStringMethod(response.body().string(), callback);
                }
            }
        });
    }


    /**
     * 返回字符串json的接口
     */
    interface FuncJsonString {
        //处理我们返回的结果
        void onResponse(String result);
    }

    /**
     * 返回json对象的接口
     */
    interface FuncJsonObject {
        //处理我们返回的结果
        void onResponse(JSONObject jsonObject);
    }

    /**
     * 返回json对象的接口
     */
    interface FuncJsonObjectByte {
        //处理我们返回的结果
        void onResponse(byte[] result);
    }

    /**
     * 返回json对象的接口
     */
    interface FuncJsonObjectBitmap {
        //处理我们返回的结果
        void onResponse(Bitmap bitmap);
    }

}

这里可以看到,我们基本上没做什么东西,对吧,只是把常用的方法都复写作了一些简单的操作而已,而且我们的注释也写的十分详细,你需要扩展的话,直接扩展就好了,这样,我们去验证一下,xml中加上

<TextView
        android:id="@+id/tv_json"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="json数据" />
<Button
        android:id="@+id/btn_json"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="解析json数据" />

好的,什么初始化的我就不写出来了,直接看点击事件

 //解析json
        btn_json.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //单例初始化
                OkHttpUtils okHttpUtils = OkHttpUtils.getInstance();
                /**
                 * 地址
                 * 成功回调
                 */
                okHttpUtils.syncJsonStringByURL(json_url, new OkHttpUtils.FuncJsonString() {
                    @Override
                    public void onResponse(String result) {
                        tv_json.setText(result);
                        Log.i(TAG,""+result);
                    }
                });
            }
        });

可以看到,是不是非常的简单就OK了。我们只要定义传url进入就可以了,而接口,我们使用的是豆瓣的接口

//json地址
private String json_url = "https://api.douban.com/v2/book/1220562";

这样我们可以看下运行结果

 

当然,我也是只提供一种思路罢了,这个封装类并不完善,还需要你自己根据需求来实施,好的,我这里也就继续来优化一下了

六.封装优化

前面可以看到,我们已经封装好了一个工具类,但是并不完善,现在呢,我们就完善的封装一下,当然,也只是针对功能点去优化,比如刚才我们只封装了一个解析返回json字符串,现在我们来一个解析直接返回一个json对象,嘿嘿,怎么做呢?

     /**
     * 请求指定的url,返回的结果是json对象
     *
     * @param url
     * @param callback
     */
    public void syscJsonObjectByURL(String url, final FuncJsonObject callback) {
        final Request request = new Request.Builder().url(url).build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.i(TAG, "解析失败");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response != null && response.isSuccessful()) {
                    onsuccessJsonObjectMethod(response.body().string(), callback);
                }
            }
        });
    }

跟之前的其实很类似,同样的,我们可以返回byte字节数组

    /**
     * 请求指定的url,返回的结果是byte字节数组
     *
     * @param url
     * @param callback
     */
    public void syscGetByteByURL(String url, final FuncJsonObjectByte callback) {
        final Request request = new Request.Builder().url(url).build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.i(TAG, "解析失败");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response != null && response.isSuccessful()) {
                    onsuccessJsonByteMethod(response.body().bytes(), callback);
                }
            }
        });
    }

我们也看到了,我们还剩下Bitmap,我们还自带裁剪功能哦,哈哈


    /**
     * 请求指定的url,返回的结果是Bitmap
     * @param url
     * @param callback
     */
    public void syscDownloadImageByURL(String url, final FuncJsonObjectBitmap callback){
        final Request request = new Request.Builder().url(url).build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.i(TAG, "解析失败");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response != null && response.isSuccessful()) {
                    byte [] data = response.body().bytes();
                    Bitmap bitmap = new TailorImageView().transform(BitmapFactory.decodeByteArray(data,0,data.length));
                    callback.onResponse(bitmap);
                }
            }
        });
    }

到这里,基本的get封装就应该差不多写完了,但是别忘了,论post的重要性,既然如此,那我们就继续封装,首先实现的一个功能

post提交表单数据

开发中,也是有诸多需要post的地方的,毕竟安全性,传输都是个很不错的选择,我们继续写方法

 /**
     * 向服务器提交表单
     *
     * @param url      提交地址
     * @param params   提交数据
     * @param callback 提交回调
     */
    public void sendDatafForClicent(String url, Map<String, String> params, final FuncJsonObject callback) {
        //表单对象,包含input开始的操作
        FormBody.Builder from = new FormBody.Builder();
        //键值对不为空,他的值也不为空
        if (params != null && !params.isEmpty()) {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                //装载表单值
                from.add(entry.getKey(), entry.getValue());
            }
        }
        RequestBody body = from.build();
        //post提交
        Request request = new Request.Builder().url(url).post(body).build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.i(TAG, "解析失败");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response != null && response.isSuccessful()) {
                    onsuccessJsonObjectMethod(response.body().string(), callback);
                }
            }
        });
    }

这里没有服务端,所以就不能测试了,我这里也就教大家怎么使用就好了,首先xml中定义一个按钮

 <Button
        android:id="@+id/btn_post"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="POST表单提交" />

我们可以直接看他的点击事件

//post表单提交
        btn_post.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                OkHttpUtils okHttpUtils = OkHttpUtils.getInstance();

                //服务端的地址
                String post_url = "";
                //map集合
                HashMap<String, String> map = new HashMap<String, String>();
                map.put("username", "LGL");
                map.put("password", "12345678");

                okHttpUtils.sendDatafForClicent(post_url, map, new OkHttpUtils.FuncJsonObject() {
                    @Override
                    public void onResponse(JSONObject jsonObject) {
                        //输出结果
                        Log.i(TAG, jsonObject.toString());
                    }
                });
            }
        });

OK,这样就提交了表单,这就是一个完整的封装过程了,如果你问,那我们普通的请求怎么办呢?额,你看了这么久还不熟悉他的套路?我嘴角微微一笑,你的司机之路还很长啊,咳咳,跑题了,如果大家还有什么疑问的话,可以去鸿洋那里看看,我相信现在很多的文章都将了很多的基本使用的,所以我也不是想怎么去讲解析json什么的

  • Android OkHttp完全解析 是时候来了解OkHttp了

2016/6/20补充:

Post提交数据,应该是这样判断的

 if (params != null && !params.isEmpty()) 

有个!的判断,在Demo里面是没有的,现在及时改正了

原文地址http://www.bieryun.com/2777.html

相关文章
|
13天前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
30 3
|
20天前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
80 3
|
22天前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
118 2
|
8天前
|
自然语言处理 数据可视化 前端开发
从数据提取到管理:合合信息的智能文档处理全方位解析【合合信息智能文档处理百宝箱】
合合信息的智能文档处理“百宝箱”涵盖文档解析、向量化模型、测评工具等,解决了复杂文档解析、大模型问答幻觉、文档解析效果评估、知识库搭建、多语言文档翻译等问题。通过可视化解析工具 TextIn ParseX、向量化模型 acge-embedding 和文档解析测评工具 markdown_tester,百宝箱提升了文档处理的效率和精确度,适用于多种文档格式和语言环境,助力企业实现高效的信息管理和业务支持。
3916 2
从数据提取到管理:合合信息的智能文档处理全方位解析【合合信息智能文档处理百宝箱】
|
4天前
|
JavaScript API 开发工具
<大厂实战场景> ~ Flutter&鸿蒙next 解析后端返回的 HTML 数据详解
本文介绍了如何在 Flutter 中解析后端返回的 HTML 数据。首先解释了 HTML 解析的概念,然后详细介绍了使用 `http` 和 `html` 库的步骤,包括添加依赖、获取 HTML 数据、解析 HTML 内容和在 Flutter UI 中显示解析结果。通过具体的代码示例,展示了如何从 URL 获取 HTML 并提取特定信息,如链接列表。希望本文能帮助你在 Flutter 应用中更好地处理 HTML 数据。
91 1
|
6天前
|
存储 弹性计算 NoSQL
"从入门到实践,全方位解析云服务器ECS的秘密——手把手教你轻松驾驭阿里云的强大计算力!"
【10月更文挑战第23天】云服务器ECS(Elastic Compute Service)是阿里云提供的基础云计算服务,允许用户在云端租用和管理虚拟服务器。ECS具有弹性伸缩、按需付费、简单易用等特点,适用于网站托管、数据库部署、大数据分析等多种场景。本文介绍ECS的基本概念、使用场景及快速上手指南。
31 3
|
17天前
|
机器学习/深度学习 人工智能 自然语言处理
前端大模型入门(三):编码(Tokenizer)和嵌入(Embedding)解析 - llm的输入
本文介绍了大规模语言模型(LLM)中的两个核心概念:Tokenizer和Embedding。Tokenizer将文本转换为模型可处理的数字ID,而Embedding则将这些ID转化为能捕捉语义关系的稠密向量。文章通过具体示例和代码展示了两者的实现方法,帮助读者理解其基本原理和应用场景。
|
21天前
|
数据采集 XML 前端开发
Jsoup在Java中:解析京东网站数据
Jsoup在Java中:解析京东网站数据
|
26天前
|
人工智能 缓存 Java
深入解析Spring AI框架:在Java应用中实现智能化交互的关键
【10月更文挑战第12天】Spring AI 是 Spring 框架家族的新成员,旨在满足 Java 应用程序对人工智能集成的需求。它支持自然语言处理、图像识别等多种 AI 技术,并提供与云服务(如 OpenAI、Azure Cognitive Services)及本地模型的无缝集成。通过简单的配置和编码,开发者可轻松实现 AI 功能,同时应对模型切换、数据安全及性能优化等挑战。
|
4天前
|
JSON 前端开发 JavaScript
API接口商品详情接口数据解析
商品详情接口通常用于提供特定商品的详细信息,这些信息比商品列表接口中的信息更加详细和全面。以下是一个示例的JSON数据格式,用于表示一个商品详情API接口的响应。这个示例假定API返回一个包含商品详细信息的对象。

推荐镜像

更多