同步操作将从 Troy/okhttps 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
OkHttps 是近期开源的对 OkHttp3 轻量封装的框架,它独创的异步预处理器,特色的标签,灵活的上传下载进度监听与过程控制功能,在轻松解决很多原本另人头疼问题的同时,设计上也力求纯粹与优雅。
<dependency>
<groupId>com.ejlchina</groupId>
<artifactId>okhttps</artifactId>
<version>1.0.0</version>
</dependency>
compile 'com.ejlchina:okhttps:1.0.0'
HTTP http = HTTP.builder().build();
以上代码构建了一个最简单的HTTP
实例,它拥有以下三个方法:
async(String url)
开始一个异步HTTP任务sync(String url)
开始一个同步HTTP任务cancel(String tag)
根据标签批量取消HTTP任务 为了使用方便,在构建的时候,我们更愿意指定一个BaseUrl
(请参见5.1 设置 BaseUrl):
HTTP http = HTTP.builder()
.baseUrl("http://api.demo.com")
.build();
为了简化文档,下文中出现的http
均是在构建时设置了BaseUrl
的HTTP
实例。
使用方法sync(String url)
开始一个同步请求:
List<User> users = http.sync("/users") // http://api.demo.com/users
.get() // GET请求
.getBody() // 获取响应报文体
.toList(User.class); // 得到目标数据
方法sync
返回一个同步HttpTask
,可链式使用。
使用方法async(String url)
开始一个异步请求:
http.async("/users/1") // http://api.demo.com/users/1
.setOnResponse((HttpResult result) -> {
// 得到目标数据
User user = result.getBody().toBean(User.class);
})
.get(); // GET请求
方法async
返回一个异步HttpTask
,可链式使用。
同步与异步的HttpTask
都拥有get
、post
、put
与delete
方法。不同的是:同步HttpTask
的这些方法返回一个HttpResult
,而异步HttpTask
的这些方法返回一个HttpCall
。
HttpResult res1 = http.sync("/users").get(); // 同步 GET
HttpResult res2 = http.sync("/users").post(); // 同步 POST
HttpResult res3 = http.sync("/users/1").put(); // 同步 PUT
HttpResult res4 = http.sync("/users/1").delete();// 同步 DELETE
HttpCall call1 = http.async("/users").get(); // 异步 GET
HttpCall call2 = http.async("/users").post(); // 异步 POST
HttpCall call3 = http.async("/users/1").put(); // 异步 PUT
HttpCall call4 = http.async("/users/1").delete();// 异步 DELETE
只有异步请求才可以设置回调函数:
http.async("/users/{id}") // http://api.demo.com/users/1
.addPathParam("id", 1)
.setOnResponse((HttpResult result) -> {
// 响应回调
})
.setOnException((IOException e) -> {
// 异常回调
})
.setOnComplete((State state) -> {
// 完成回调,无论成功失败都会执行
})
.get();
HttpResult
是HTTP请求执行完后的结果,它是同步请求方法( get
、post
、put
、delete
)的返回值,也是异步请求响应回调(OnResponse
)的参数,它定义了如下方法:
getState()
得到请求执行状态枚举,它有以下取值:
State.CANCELED
请求被取消State.RESPONSED
已收到响应State.TIMEOUT
请求超时State.NETWORK_ERROR
网络错误State.EXCEPTION
其它请求异常getStatus()
得到HTTP状态码isSuccessful()
是否响应成功,状态码在 [200..300) 之间getHeaders()
得到HTTP响应头getHeaders(String name)
得到HTTP响应头getHeader(String name)
得到HTTP响应头getBody()
得到响应报文体Body
实例,它定义了如下方法(对同一个Body
实例,以下的toXXX()
类方法只能使用一个且仅能调用一次,除非先使用 cache 方法):
toBytes()
返回字节数组toByteStream()
返回字节输入流toCharStream()
返回字符输入流toString()
返回字符串toJsonObject()
返回Json对象toJsonArray()
返回Json数组toBean(Class<T> type)
返回根据type自动json解析后的JavaBeantoList(Class<T> type)
返回根据type自动json解析后的JavaBean列表toFile(String filePath)
下载到指定路径toFile(File file)
下载到指定文件toFolder(String dirPath)
下载到指定目录toFolder(File dir)
下载到指定目录getContentType()
返回报文体的媒体类型getContentLength()
返回报文体的字节长度cache()
缓存报文体,开启缓存后可重复使用toXXX()
类方法close()
关闭报文体,未对报文体做任何消费时使用,比如只读取报文头getError()
执行中发生的异常,自动捕获执行请求是发生的 网络超时、网络错误 和 其它请求异常close()
关闭报文,未对报文体做任何消费时使用,比如只读取长度示例,请求结果自动转Bean和List:
// 自动转Bean
Order order = http.sync("/orders/1")
.get().getBody().toBean(Order.class);
// 自动转List
List<Order> orders = http.sync("/orders")
.get().getBody().toList(Order.class);
示例,使用 cache 方法:
Body body = http.sync("/orders").get().getBody().cache();
// 使用 cache 后,可以多次使用 toXXX() 方法
System.out.println(body.toString());
System.out.println(body.toJsonArray());
System.out.println(body.toList(Order.class));
示例,获取下载文件的大小:
long size = http.sync("http://api.demo.com/download/test.zip")
.get().getBody()
.close() // 只是想获得文件大小,不消费报文体,所以直接关闭
.getContentLength(); // 获得待下载文件的大小
// 由于未消费报文体,所以本次请求不会消耗下载报文体的时间和网络流量)
System.out.println("size = " + size);
HttpCall
对象是异步请求方法(get
、post
、put
、delete
)的返回值,与java
的Future
接口很像,它有如下方法:
cancel()
取消本次请求,返回取消结果isCanceled()
返回请求是否被取消isDone()
返回是否执行完成,包含取消和失败getResult()
返回执行结果HttpResult
对象,若请求未执行完,则挂起当前线程直到执行完成再返回取消一个异步请求示例:
HttpCall call = http.async("/users/1").get();
System.out.println(call.isCanceled()); // false
boolean success = call.cancel(); // 取消请求
System.out.println(success); // true
System.out.println(call.isCanceled()); // true
HTTP
对象的sync
与async
方法返回一个HttpTask
对象,该对象提供了可链式调用的addXXX
与setXXX
系列方法用于构建任务本身。
addHeader(String name, String value)
添加请求头
addHeader(Map<String, String> headers)
添加请求头
addPathParam(String name, Object value)
添加路径参数:替换URL里的{name}占位符
addPathParam(Map<String, ?> params)
添加路径参数:替换URL里的{name}占位符
addUrlParam(String name, Object value)
添加URL参数:拼接在URL的?之后(查询参数)
addUrlParam(Map<String, ?> params)
添加URL参数:拼接在URL的?之后(查询参数)
addBodyParam(String name, Object value)
添加Body参数:以表单key=value&的形式放在报文体内(表单参数)
addBodyParam(Map<String, ?> params)
添加Body参数:以表单key=value&的形式放在报文体内(表单参数)
addJsonParam(String name, Object value)
添加Json参数:请求体为Json(支持多层结构)
addJsonParam(Map<String, ?> params)
添加Json参数:请求体为Json(支持多层结构)
setRequestJson(Object json)
设置请求体的Json字符串 或待转换为 Json的 JavaBean
setRequestJson(Object bean, String dateFormat)
设置请求体的Json字符串 或待转换为 Json的 JavaBean
addFileParam(String name, String filePath)
上传文件
addFileParam(String name, File file)
上传文件
addFileParam(String name, String type, InputStream inputStream)
上传文件
addFileParam(String name, String type, String fileName, InputStream input)
上传文件
addFileParam(String name, String type, byte[] content)
上传文件
addFileParam(String name, String type, String fileName, byte[] content)
上传文件
setTag(String tag)
为HTTP任务添加标签
setRange(long rangeStart)
设置Range头信息,用于断点续传
setRange(long rangeStart, long rangeEnd)
设置Range头信息,用于分块下载
setRange(long rangeStart, long rangeEnd)
设置Range头信息,用于分块下载
bind(Object object)
绑定一个对象,可用于实现Android里的生命周期绑定
有时候我们想对HTTP任务加以分类,这时候可以使用标签功能:
http.async("/users") //(1)
.setTag("A").get();
http.async("/users") //(2)
.setTag("A.B").get();
http.async("/users") //(3)
.setTag("B").get();
http.async("/users") //(4)
.setTag("B.C").get();
http.async("/users") //(5)
.setTag("C").get();
当使用标签后,就可以按标签批量的对HTTP任务进行取消:
int count = http.cancel("B"); //(2)(3)(4)被取消(取消标签包含"B"的任务)
System.out.println(count); // 输出 3
同样的,只有异步HTTP任务才可以被取消。标签除了可以用来取消任务,在预处理器中它也可以发挥作用,请参见并行预处理器与串行预处理器。
HTTP http = HTTP.builder()
.baseUrl("http://api.demo.com") // 设置 BaseUrl
.build();
该配置全局生效,在配置了BaseUrl
之后,具体的请求便可以省略BaseUrl
部分,使得代码更加简洁,例如:
http.sync("/users").get() // http://api.demo.com/users
http.sync("/auth/signin") // http://api.demo.com/auth/signin
.addBodyParam("username", "Jackson")
.addBodyParam("password", "xxxxxx")
.post() // POST请求
在配置了BaseUrl
之后,如有特殊请求任务,仍然可以使用全路径的方式,一点都不妨碍:
http.sync("https://www.baidu.com").get()
如何想改变执行回调函数的线程时,可以配置回调执行器。例如在Android里,让所有的回调函数都在UI线程执行,则可以在构建HTTP
时配置如下:
HTTP http = HTTP.builder()
.callbackExecutor((Runnable run) -> {
runOnUiThread(run); // 在UI线程执行
})
.build();
该配置影响所有回调。
与其他封装 OkHttp3 的框架不同,OkHttps 并不会遮蔽 OkHttp3 本身就很好用的功能,如下:
HTTP http = HTTP.builder()
.config((Builder builder) -> {
// 配置连接池 最小10个连接(不配置默认为 5)
builder.connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES));
// 配置连接超时时间(默认10秒)
builder.connectTimeout(20, TimeUnit.SECONDS);
// 配置拦截器
builder.addInterceptor((Chain chain) -> {
Request request = chain.request();
// 必须同步返回,拦截器内无法执行异步操作
return chain.proceed(request);
});
// 其它配置: SSL、缓存、代理、事件监听...
})
.build();
预处理器(Preprocessor
)可以让我们在请求发出之前对请求本身做一些改变,但与OkHttp
的拦截器(Interceptor
)不同:预处理器可以让我们异步处理这些问题。
例如,当我们想为请求任务自动添加Token
头信息,而Token
只能通过异步方法requestToken
获取时,这时使用Interceptor
就很难处理了,但可以使用预处理器轻松解决:
HTTP http = HTTP.builder()
.addPreprocessor((PreChain chain) -> {
HttpTask<?> task = chain.getTask();// 获得当前的HTTP任务
if (!task.isTagged("Auth")) { // 根据标签判断该任务是否需要Token
return;
}
requestToken((String token) -> { // 异步获取 Token
task.addHeader("Token", token);// 为任务添加头信息
chain.proceed(); // 继续当前的任务
});
})
.build();
和Interceptor
一样,Preprocessor
也可以添加多个。他们之前的区别如下:
- 拦截器只能处理同步操作,预处理器支持处理异步操作
- 拦截器都是并行处理请求,预处理器支持串行处理(详见6.5章节)
- 拦截器处理时机在请求前和响应后,预处理器只在请求前,并且预处理器先于拦截器执行。关于响应后,OkHttps还提供了全局回调机制(详见6.6章节)
普通预处理器都是可并行处理的,然而有时我们希望某个预处理器同时只处理一个任务。比如 当Token
过期时我们需要去刷新获取新Token
,而刷新Token
这个操作只能有一个任务去执行,因为如果n
个任务同时执行的话,那么必有n-1
个任务刚刷新得到的Token
可能就立马失效了,而这是我们所不希望的。
为了解决这个问题,OkHttps 提供了串行预处理器,它可以让 HTTP 任务排好队,一个一个地进入预处理器:
HTTP http = HTTP.builder()
.addSerialPreprocessor((PreChain chain) -> {
HttpTask<?> task = chain.getTask();
if (!task.isTagged("Auth")) {
return;
}
// 检查过期,若需要则刷新Token
requestTokenAndRefreshIfExpired((String token) -> {
task.addHeader("Token", token);
chain.proceed(); // 调用此方法前,不会有其它任务进入该处理器
});
})
.build();
串行预处理器实现了让HTTP任务排队串行处理的功能,但值得一提的是:它并没有因此而阻塞任何线程!
HTTP http = HTTP.builder()
.responseListener((HttpTask<?> task, HttpResult result) -> {
// 所有请求响应后都会走这里
return true; // 返回 true 表示继续执行 task 的 OnResponse 回调,false 表示不再执行
})
.completeListener((HttpTask<?> task, State state) -> {
// 所有请求执行完都会走这里
return true; // 返回 true 表示继续执行 task 的 OnComplete 回调,false 表示不再执行
})
.exceptionListener((HttpTask<?> task, IOException error) -> {
// 所有请求发生异常都会走这里
return true; // 返回 true 表示继续执行 task 的 OnException 回调,false 表示不再执行
})
.build();
全局回调与拦截器的异同:
- 拦截器处理时机在请求前和响应后,全局回调只在响应后
- 拦截器可以添加多个,全局回调分三种,每种最多添加一个
- 全局回调可以阻断某个请求的具体回调(在全局回调里返回 false),而拦截器不能
HTTP http = HTTP.builder()
.downloadListener((HttpTask<?> task, Download download) -> {
// 所有下载在开始之前都会先走这里
Ctrl ctrl = download.getCtrl(); // 下载控制器
})
.build();
类HttpUtils
本是 1.x 版本里的最重要的核心类,由于在 2.x 版本里抽象出了HTTP
接口,使得它的重要性已不如往昔。但合理的使用它,仍然可以带来不少便利,特别是在没有IOC容器的环境里,比如在Android开发和一些工具项目的开发中。
类HttpUtils
共定义了四个静态方法:
async(String url)
开始一个异步请求 (内容通过一个HTTP
单例实现)sync(String url)
开始一个同步请求 (内容通过一个HTTP
单例实现)cancel(String tag)
按标签取消请求(内容通过一个HTTP
单例实现)of(HTTP http)
配置HttpUtils
持有的HTTP
实例(不调用此方法前默认使用一个没有没有经过任何配置的HTTP
懒实例) 也就是说,能使用http
实例的地方,都可以使用HttpUtils
类,例如:
// 在配置HTTP实例之前,只能使用全路径方式
List<Role> roles = HttpUtils.sync("http://api.demo.com/roles")
.get().getBody().toList(Role.class);
// 配置HTTP实例,全局生效
HttpUtils.of(HTTP.builder()
.baseUrl("http://api.demo.com")
.build());
// 内部使用新的HTTP实例
List<User> users = HttpUtils.sync("/users")
.get().getBody().toList(User.class);
OkHttps 并没有把文件的下载排除在常规的请求之外,同一套API,它优雅的设计使得下载与常规请求融合的毫无违和感,一个最简单的示例:
http.sync("/download/test.zip")
.get() // 使用 GET 方法(其它方法也可以,看服务器支持)
.getBody() // 得到报文体
.toFile("D:/download/test.zip") // 指定下载的目录,文件名将根据下载信息自动生成
.start(); // 启动下载
或使用异步连接方式:
http.async("/download/test.zip")
.setOnResponse((HttpResult result) -> {
result.getBody().toFile("D:/download/test.zip").start();
})
.get();
这里要说明一下:sync
与async
的区别在于连接服务器并得到响应这个过程的同步与异步(这个过程的耗时在大文件下载中占比极小),而start
方法启动的下载过程则都是异步的。
就直接上代码啦,诸君一看便懂:
http.sync("/download/test.zip")
.get()
.getBody()
.setStepBytes(1024) // 设置每接收 1024 个字节执行一次进度回调(不设置默认为 8192)
// .setStepRate(0.01) // 设置每接收 1% 执行一次进度回调(不设置以 StepBytes 为准)
.setOnProcess((Process process) -> { // 下载进度回调
long doneBytes = process.getDoneBytes(); // 已下载字节数
long totalBytes = process.getTotalBytes(); // 总共的字节数
double rate = process.getRate(); // 已下载的比例
boolean isDone = process.isDone(); // 是否下载完成
})
.toFolder("D:/download/") // 指定下载的目录,文件名将根据下载信息自动生成
// .toFile("D:/download/test.zip") // 指定下载的路径,若文件已存在则覆盖
.setOnSuccess((File file) -> { // 下载成功回调
})
.start();
值得一提的是:由于 OkHttps 并没有把下载做的很特别,这里设置的进度回调不只对下载文件起用作,即使对响应JSON的常规请求,只要设置了进度回调,它也会告诉你报文接收的进度(提前是服务器响应的报文有Content-Length
头),例如:
List<User> users = http.sync("/users")
.get()
.getBody()
.setStepBytes(2)
.setOnProcess((Process process) -> {
System.out.println(process.getRate());
})
.toList(User.class);
过于简单:还是直接上代码:
Ctrl ctrl = http.sync("/download/test.zip")
.get()
.getBody()
.setOnProcess((Process process) -> {
System.out.println(process.getRate());
})
.toFolder("D:/download/")
.start(); // 该方法返回一个下载过程控制器
ctrl.status(); // 下载状态
ctrl.pause(); // 暂停下载
ctrl.resume(); // 恢复下载
ctrl.cancel(); // 取消下载(同时会删除文件,不可恢复)
无论是同步还是异步发起的下载请求,都可以做以上的控制:
http.async("/download/test.zip")
.setOnResponse((HttpResult result) -> {
// 拿到下载控制器
Ctrl ctrl = result.getBody().toFolder("D:/download/").start();
})
.get();
OkHttps 对断点续传并没有再做更高层次的封装,因为这是app该去做的事情,它在设计上使各种网络问题的处理变简单的同时力求纯粹。下面的例子可以看到,OkHttps 通过一个失败回调拿到断点,便将复杂的问题变得简单:
http.sync("/download/test.zip")
.get()
.getBody()
.toFolder("D:/download/")
.setOnFailure((Failure failure) -> { // 下载失败回调,以便接收诸如网络错误等失败信息
IOException e = failure.getException(); // 具体的异常信息
long doneBytes = failure.getDoneBytes(); // 已下载的字节数(断点),需要保存,用于断点续传
File file = failure.getFile(); // 下载生成的文件,需要保存 ,用于断点续传(只保存路径也可以)
})
.start();
下面代码实现续传:
long doneBytes = ... // 拿到保存的断点
File file = ... // 待续传的文件
http.sync("/download/test.zip")
.setRange(doneBytes) // 设置断点(已下载的字节数)
.get()
.getBody()
.toFile(file) // 下载到同一个文件里
.setAppended() // 开启文件追加模式
.setOnSuccess((File file) -> {
})
.setOnFailure((Failure failure) -> {
})
.start();
当文件很大时,有时候我们会考虑分块下载,与断点续传的思路是一样的,示例代码:
static String url = "http://api.demo.com/download/test.zip"
public static void main(String[] args) {
long totalSize = HttpUtils.sync(url).get().getBody()
.close() // 因为这次请求只是为了获得文件大小,不消费报文体,所以直接关闭
.getContentLength(); // 获得待下载文件的大小(由于未消费报文体,所以该请求不会消耗下载报文体的时间和网络流量)
download(totalSize, 0); // 从第 0 块开始下载
sleep(50000); // 等待下载完成(不然本例的主线程就结束啦)
}
static void download(long totalSize, int index) {
long size = 3 * 1024 * 1024; // 每块下载 3M
long start = index * size;
long end = Math.min(start + size, totalSize);
HttpUtils.sync(url)
.setRange(start, end) // 设置本次下载的范围
.get().getBody()
.toFile("D:/download/test.zip") // 下载到同一个文件里
.setAppended() // 开启文件追加模式
.setOnSuccess((File file) -> {
if (end < totalSize) { // 若未下载完,则继续下载下一块
download(totalSize, index + 1);
} else {
System.out.println("下载完成");
}
})
.start();
}
一个简单文件上传的示例:
http.sync("/upload")
.addFileParam("test", "D:/download/test.zip")
.post() // 上传发法一般使用 POST 或 PUT,看服务器支持
异步上传也是完全一样:
http.async("/upload")
.addFileParam("test", "D:/download/test.zip")
.post()
OkHttps 的上传进度监听,监听的是所有请求报文体的发送进度,示例代码:
http.sync("/upload")
.addBodyParam("name", "Jack")
.addBodyParam("age", 20)
.addFileParam("avatar", "D:/image/avatar.jpg")
.setStepBytes(1024) // 设置每发送 1024 个字节执行一次进度回调(不设置默认为 8192)
// .setStepRate(0.01) // 设置每发送 1% 执行一次进度回调(不设置以 StepBytes 为准)
.setOnProcess((Process process) -> { // 上传进度回调
long doneBytes = process.getDoneBytes(); // 已发送字节数
long totalBytes = process.getTotalBytes(); // 总共的字节数
double rate = process.getRate(); // 已发送的比例
boolean isDone = process.isDone(); // 是否发送完成
})
.post()
咦!怎么感觉和下载的进度回调的一样?没错!OkHttps 还是使用同一套API处理上传和下载的进度回调,区别只在于上传是在get/post
方法之前使用这些API,下载是在getBody
方法之后使用。很好理解:get/post
之前是准备发送请求时段,有上传的含义,而getBody
之后,已是报文响应的时段,当然是下载。
上传文件的过程控制就很简单,和常规请求一样,只有异步发起的上传可以取消:
HttpCall call = http.async("/upload")
.addFileParam("test", "D:/download/test.zip")
.setOnProcess((Process process) -> {
System.out.println(process.getRate());
})
.post()
call.cancel(); // 取消上传
上传就没有暂停和继续这个功能啦,应该没人有这个需求吧?
在 Android 开发中,经常会把某些代码放到特点的线程去执行,比如网络请求响应后的页面更新在主线程(UI线程)执行,而保存文件则在IO线程操作。OkHttps 为这类问题提供了良好的方案。
在 默认 情况下,所有回调 函数都会 在 IO 线程 执行。为什么会设计如此呢?这是因为 OkHttps 只是纯粹的 Java 领域 Http工具包,本身对 Android 不会有任何依赖,因此也不知 Android 的 UI 线程为何物。这么设计也让它在 Android 之外有更多的可能性。
但是在 Android 里使用 OkHttps 的话,UI线程的问题能否优雅的解决呢?当然可以!简单粗暴的方法就是配置一个 回调执行器:
HTTP http = HTTP.builder()
.callbackExecutor((Runnable run) -> {
// 实际编码中可以吧 Handler 提出来,不需要每次执行回调都重新创建
new Handler(Looper.getMainLooper()).post(run); // 在主线程执行
})
.build();
上述代码便实现了让 所有 的 回调函数 都在 主线程 执行的目的,如:
http.async("/users")
.addBodyParam("name", "Jack")
.setOnProcess((Process process) -> {
// 在主线程执行
})
.setOnResponse((HttpResult result) -> {
// 在主线程执行
})
.setOnException((Exception e) -> {
// 在主线程执行
})
.setOnComplete((State state) -> {
// 在主线程执行
})
.post();
但是,如果同时还想让某些回调放在IO线程,实现 自由切换,怎么办呢?OkHttps 给出了非常灵活的方法,如下:
http.async("/users")
.addBodyParam("name", "Jack")
.setOnProcess((Process process) -> {
// 在主线程执行
})
.nextOnIO() // 指定下一个回调在 IO 线程执行
.setOnResponse((HttpResult result) -> {
// 在 IO 线程执行
})
.setOnException((Exception e) -> {
// 在主线程执行(没有指明 nextOnIO 则在回调执行器里执行)
})
.nextOnIO() // 指定下一个回调在 IO 线程执行
.setOnComplete((State state) -> {
// 在 IO 线程执行
})
.post();
无论是哪一个回调,都可以使用nextOnIO()
方法自由切换。同样,对于文件下载也是一样:
http.sync("/download/test.zip")
.get()
.getBody()
.setOnProcess((Process process) -> {
// 在主线程执行
})
.toFolder("D:/download/")
.nextOnIO() // 指定下一个回调在 IO 线程执行
.setOnSuccess((File file) -> {
// 在 IO 线程执行
})
.setOnFailure((Failure failure) -> {
// 在主线程执行
})
.start();
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。