关于Retrofit网络请求方式和自己的一点理解

Retrofit简介:

Retrofit是Square公司开发的一款针对Android网络请求的框架,Retrofit2底层基于OkHttp实现的,Okhttp现在已经得到Google官方认可,大量的app都采用OkHttp做网络请求


1.要使用Retrofit首先得添加gradle依赖

compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'


2.新建一个Retrofit对象

mRetrofit = new Retrofit.Builder()
.baseUrl("http://192.168.0.102/")
.client(configClient())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();

需要注意的是baseUrl必须以”/”结尾


3.新建一个Interface,需要请求的接口服务类

public interface Api {
@GET("index.php?c=User&m=getUser")
Call<List<User>> getUser();
}

在我们定义请求的接口时,会传入具体的接口的地址(Endpoint):

 @GET("index.php?c=User&m=getUser")
    Call<List<User>> getUser();

Retrofit会帮我们完成拼接,最后的URL是:

http://192.168.0.102/index.php?c=User&m=getUser

Endpoint可以为完整的URL:

  Base URL: http://example.com/
  Endpoint: https://github.com/square/retrofit/
  Result: https://github.com/square/retrofit/

当我们需要请求不同服务器上的API时,这就非常有用了。


@GET
Call<List<User>> getUser2(@Url String url);
Call<List<User>> call=userClient.getUser2("http://www.jiangxu.online/index.php?c=User&m=getUser");


Retrofit中一些参数注解:
@Path:所有在网址中的参数(URL的问号前面),如:
http://102.10.10.132/api/Accounts/{accountId}
@Query:URL问号后面的参数,如:
http://102.10.10.132/api/Comments?access_token={access_token}
@QueryMap:相当于多个@Query
@Field:用于POST请求,提交单个数据
@Body:相当于多个@Field,以对象的形式提交

@Path和@Query的区别;

example:www.app.net/api/searchtypes/862189/filters?Type=6&SearchText=School

@GET("/api/searchtypes/{Id}/filters")
void getFilterList(@Path("Id") long customerId,
@Query("Type") String responseType,
@Query("SearchText") String searchText,
Callback<FilterResponse> filterResponse);

url拼接:www.app.net/api/searchtypes/{Path}/filters?Type={Query}&SearchText={Query}

 

Get查询参数

查询参数是一种很常见的客户端往服务端传递数据的方式,比如我们需要传一个id给服务端,那么URL可能是这样的:

https://api.example.com/tasks?id=123

Retrofit 定义实现查询参数:

public interface TaskService {  
    @GET("/tasks")
    Call<Task> getTask(@Query("id") long taskId);
}

方法getTask需要传入taskId,这个参数会被映射到@Query(“id”)中的id中,最后的URL会变成这样:

/tasks?id=<taskId>

有时我们需要对于一个id参数传入多个值,比如这样:

https://api.example.com/tasks?id=123&id=124&id=125  

对于这样的需求,Retrofit 通过传入一个List来实现:

public interface TaskService {  
    @GET("/tasks")
    Call<List<Task>> getTask(@Query("id") List<Long> taskIds);
}

这样,拼接后的URL就是我们需要的那样。

有时,我们在给服务端传递数据时,有些参数是可选的,比如下面的URL:

https://your.api.com/tasks?sort=value-of-order-parameter

sort参数是可选的,有些情况下可能不需要传入。

接口的定义:

public interface TaskService {  
    @GET("/tasks")
    Call<List<Task>> getTasks(@Query("sort") String order);
}

那么,在我们不想添加排序控制的时候,我们可以传入null,Retrofit 会忽略值为null的参数。

service.getTasks(null);  

需要注意的是,可忽略参数的参数类型不能是int, float, long这些基本类型,应该用Integer, Float, Long来代替。


Post 提交参数

Retrofit Post方式提交表单形式的参数需要添加标记@FormUrlEncoded,通过@Field注释添加键值对。 
接口定义:

public interface UserClient {
    @FormUrlEncoded
    @POST("/index.php?c=User&m=login")
    Call<List<User>> login(@Field("phone") String phone, @Field("password") String password);
}

客户端调用:

 private void login() {
        UserClient userClient = ServiceGenerator.createService(UserClient.class);
        Call<List<User>> call = userClient.login("13695378745","123456");
        call.enqueue(new Callback<List<User>>() {

        });
    }

Post 提交JSON数据

有时提交的数据量比较大时,用键值对的方式提交参数不太方便,Retrofit可以通过@Body注释,直接传递一个对象给请求主体,Retrofit通过JSON转化器,把对象映射成JSON数据。

接口定义:

public interface TaskService {  
    @POST("/tasks")
    Call<Task> createTask(@Body Task task);
}

传递实体需要有Model:

public class Task {  
    private long id;
    private String text;

    public Task() {}
    public Task(long id, String text) {
        this.id = id;
        this.text = text;
    }
}

客户端调用:

Task task = new Task(1, "my task title");  
Call<Task> call = taskService.createTask(task);  
call.enqueue(new Callback<Task>() {}); 

这样,服务端得到的是JOSN数据:

{
    "id": 1,
    "text": "my task title"
}

文件下载

接口定义:

 @Streaming
    @GET
    Call<ResponseBody> downloadFile(@Url String fileUrl);

在下载的时候,是在子线程中工作运行的,有时需要显示下载的进度,同时支持多个下载并发进行,这样需要封装一下,使用起来方便:

public class DownFileManager {

    private final static String TAG = "DownFileManager";
    private static DownFileManager sDownFileManager;
    private CallBack mCallBack;
    private final Handler mHandler = new Handler(Looper.getMainLooper());
    private ExecutorService executorService;

    public interface CallBack {

        void onSuccess(String path);

        void inProgress(int progress);

        void onFail();
    }

    public static DownFileManager getInstance() {
        if (sDownFileManager == null) {
            sDownFileManager = new DownFileManager();
        }
        return sDownFileManager;
    }

    public synchronized ExecutorService executorService() {
        if (executorService == null) {
            executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), Util.threadFactory("DownFileManager", false));
        }
        return executorService;
    }

    public void download(final String path, final String filePath, CallBack callBack) {
        mCallBack = callBack;
        executorService();
        executorService.submit(new Runnable() {
            boolean isSuccess;

            @Override
            public void run() {
                DownloadService service = ServiceGenerator.createService(DownloadService.class);
                Call<ResponseBody> call = service.downloadFile(path);
                try {
                    isSuccess = writeResponseBodyToDisk(filePath, call.execute().body());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (isSuccess) {
                            mCallBack.onSuccess(filePath);
                        } else {
                            mCallBack.onFail();
                        }
                    }
                });
            }
        });
    }

    private boolean writeResponseBodyToDisk(String path, ResponseBody body) {
        try {
            File futureStudioIconFile = new File(path);
            InputStream inputStream = null;
            OutputStream outputStream = null;
            try {
                byte[] fileReader = new byte[4096];

                long fileSize = body.contentLength();
                long fileSizeDownloaded = 0;

                inputStream = body.byteStream();
                outputStream = new FileOutputStream(futureStudioIconFile);
                int oldProgress = 0;
                while (true) {
                    int read = inputStream.read(fileReader);

                    if (read == -1) {
                        break;
                    }
                    outputStream.write(fileReader, 0, read);
                    fileSizeDownloaded += read;
                    final int progress = (int) ((fileSizeDownloaded * 1.0 / fileSize) * 100);
                    if (oldProgress != progress) {
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                mCallBack.inProgress(progress);
                            }
                        });
                    }
                    oldProgress = progress;
                }
                outputStream.flush();
                return true;
            } catch (IOException e) {
                return false;
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
            }
        } catch (IOException e) {
            return false;
        }
    }

}

使用:

 DownFileManager.getInstance().download(downloadUrl, filePath, new DownFileManager.CallBack() {
            @Override
            public void onSuccess(String path) {
                //下载文件的路径
            }

            @Override
            public void inProgress(int progress) {
                //下载的进度
                Trace.i("DownFileManager","progress  ="+progress);
            }

            @Override
            public void onFail() {
            }
        });

文件上传

Retrofit 支持 Multipart 请求,所以我们可以用它来实现文件上传,对于要实现文件上传的接口,需要添加@Multipart注释。 
接口定义:

public interface FileUploadService {
    @Multipart
    @POST("index.php?c=Upload&m=doUpload")
    Call<Msg> upload(
            @Part("file\"; filename=\"image.png\" ") RequestBody file,
            @Part("description") RequestBody description);
}

对于要上传的文件,我们需要把它包装成RequestBody 对象:

 private void uploadFile( File file) {
        FileUploadService service =
                ServiceGenerator.createService(FileUploadService.class);

        String descriptionString = "hello, this is description speaking";
        RequestBody description =
                RequestBody.create(MediaType.parse("multipart/form-data"), descriptionString);
        RequestBody requestBody =
                RequestBody.create(MediaType.parse("multipart/form-data"), file);

        Call<Msg> call = service.upload(requestBody, description);
        call.enqueue(new Callback<Msg>() {
            @Override
            public void onResponse(Call<Msg> call, Response<Msg> response) {
                Log.v("Upload", "success");
            }

            @Override
            public void onFailure(Call<Msg> call, Throwable t) {
                Log.e("Upload", t.getMessage());
            }
        });
    }

同步请求和异步请求

同步的请求会在同一个线程中执行,在Android中必须另开线程,否则在主线程中执行会抛出异常。 
同步和异步请求的接口都是一样的,只是调用的时候会不一样:

public interface TaskService {  
    @GET("/tasks")
    Call<List<Task>> getTasks();
}

同步请求调用:

TaskService taskService = ServiceGenerator.createService(TaskService.class);  
Call<List<Task>> call = taskService.getTasks();  
List<Task>> tasks = call.execute().body(); 

异步请求调用:

TaskService taskService = ServiceGenerator.createService(TaskService.class);  
Call<List<Task>> call = taskService.getTasks();  
call.enqueue(new Callback<List<Task>>() {  
    @Override
    public void onResponse(Call<List<Task>> call, Response<List<Task>> response) {
        if (response.isSuccess()) {
            // tasks available
        } else {
            // error response, no access to resource?
        }
    }

    @Override
    public void onFailure(Call<List<Task>> call, Throwable t) {
        // something went completely south (like no internet connection)
        Log.d("Error", t.getMessage());
    }
}

异步请求实现了一个CallBack,包含了两个回调方法:onResponse和 onFailure,在onResponse中我们可以拿到我们需要的实体数据,在onFailure中,可以拿到错误的Log。

原文地址:http://blog.csdn.net/jiangxuqaz/article/details/50759239

部分内容,也根据自己的理解添加或者修改

Demo地址:

http://download.csdn.net/detail/jiangxuqaz/9452369

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容