安卓基础(持续更新的笔记)
为什么要这样:
// 创建请求体
RequestBody body = RequestBody.create(
MediaType.parse("application/json; charset=utf-8"),
jsonObject.toString()
);
jsonObject
就包含了一个 JSON 数据,它其实就是:
{
"name": "张三",
"age": 10
}
所以,jsonObject.toString()
会把我们的 JSON 数据转换成:
{"name":"张三","age":10}
什么是 client.newCall(request).enqueue()
?
client
是什么?
- 你可以把
client
想象成一个邮递员。 - 它负责接收信件(就是请求)并把信件送到目的地(比如服务器)。
request
是什么?
request
就是你要寄出的信,它包含了你想要发送的信息(比如:你想要从网站获取的数据)。- 你可以把 request 看作是装在信封里的信,信里写着你要做什么(比如:查天气,获取新闻等)。
newCall(request)
是什么意思?
- 当你说
client.newCall(request)
时,你是在告诉邮递员:“嘿!这是我的信(请求),帮我寄出去!”
enqueue(new Callback() {...})
是什么意思?
-
这部分就是你告诉邮递员,“当信送到之后,告诉我一声!”就像你期待收信一样,你也期待邮递员处理完请求之后给你一个答复。
-
enqueue
是一个 "异步" 操作,意味着邮递员把信送出去后,他不会立刻等待回信,而是继续去做其他事情。等他收到回复时,他会 通知你,就像“嘿!你的信已经回来了,我有了答案!” -
Callback
就是你给邮递员的一个回信地址,你告诉邮递员,“当有回复的时候,联系我”。当你收到答复时,邮递员就会通过Callback
告诉你 答复内容。
public void onResponse(Call call, Response response) throws IOException
为什么要加一个 Call call
参数?
-
表示当前的请求:
onResponse
是一个回调方法,它在网络请求成功后被调用。Call call
这个参数是告诉你当前的请求是什么,它是你发起的某个网络请求对象。这样,在回调中你可以通过call
来获取有关请求的信息。 -
支持多次请求: 有时候,程序可能会发起多个请求。通过在
onResponse
中传递call
,你就能知道是哪个请求返回的结果。如果没有call
,你就不知道是哪个请求触发了回调。
String responseBody = response.body().string();
response.body().string();
这行代码的意思是什么?
response
: 这是你从网络请求得到的回应,它包含了很多信息,比如服务器返回的数据、状态码、头信息等。body()
: 这个方法可以用来获取服务器返回的数据内容。可以理解为,"body" 就是回应的内容。string()
: 这个方法把回应的内容转换成字符串。因为服务器返回的数据通常是以二进制的方式存储的,我们需要把它转换成可读的文本(字符串)才能理解里面的内容。
举个简单的例子:
想象你给朋友发了一个包裹,这个包裹里有一些写着字的纸条,代表服务器给你的回应。
response.body()
就像是你打开包裹,拿到里面的纸条。string()
就是你把纸条上的内容读出来,变成可以理解的文字。
轮询获取消息是什么?
轮询获取消息,简单来说,就是定时检查是否有新消息。就像你每天都会去查看邮箱,看看是否有新的邮件。你定期检查一次,如果有新邮件,你就打开看,没有就过一会儿再看一次。
代码怎么实现?
- Looper 是顾客排队的地方(等候队列),
- Handler 就是把任务分配给助手的工具(执行任务)。
new Handler(Looper.getMainLooper())
就是告诉你:“嘿,我们要让这个任务在应用的大脑(主线程)里执行!”-
handler.post(new Runnable()):
handler.post(new Runnable())
就是把一个新的任务(Runnable)交给管家(Handler)来执行。- 它的作用是告诉管家:“嘿,我有个任务交给你,你去做吧!”
-
Looper.loop()
会启动一个无限的循环,持续检查并执行交给它的任务。
在安卓中,轮询可以通过定时任务来实现。最常见的方法是使用Handler
和Runnable
,每隔一段时间就去请求服务器看看有没有新消息。这里是一个简单的例子:
// 导入 Android 中的 Handler 类和 Looper 类
import android.os.Handler;
import android.os.Looper;
public class PollingExample {
// 创建一个 Handler 对象,Handler 会在主线程中处理任务
private Handler handler = new Handler(Looper.getMainLooper());
// 定义一个变量 interval,每隔5秒检查一次
private int interval = 5000; // 每5秒钟检查一次
private boolean isPolling = false; // 用来标记是否在进行轮询
// 开始轮询,设置 isPolling 为 true,表示开始检查新消息
public void startPolling() {
isPolling = true; // 开启轮询
pollMessages(); // 调用 pollMessages 方法开始检查
}
// 停止轮询,设置 isPolling 为 false,表示不再检查新消息
public void stopPolling() {
isPolling = false; // 停止轮询
}
// 检查是否有新消息的方法
private void pollMessages() {
if (isPolling) { // 如果正在进行轮询
// 模拟发起网络请求,检查是否有新消息
System.out.println("正在检查是否有新消息..."); // 输出提示信息,表示正在检查消息
// 模拟检查到新消息
boolean newMessageReceived = checkForNewMessages();
// 如果检查到新消息,就输出提示信息
if (newMessageReceived) {
System.out.println("有新消息!"); // 输出“有新消息!”
}
// 每隔 interval(5秒)再去检查一次
handler.postDelayed(new Runnable() {
@Override
public void run() {
pollMessages(); // 再次调用 pollMessages 方法,继续检查
}
}, interval); // 设置延迟时间为 interval(5秒)
}
}
// 模拟一个方法,用来检查是否有新消息
private boolean checkForNewMessages() {
// 假设这里是通过网络请求来检查新消息,我们简单返回 true,表示有新消息
return true;
}
}
Retrofit 是一个用来简化网络请求的 Android 库。它帮助我们在 Android 应用程序中发送 HTTP 请求(比如获取网络上的数据),并把这些请求的响应数据转换成我们能理解和使用的格式。换句话说,它是一个让你与网络进行“对话”的工具。
如何使用 Retrofit?
- 创建一个接口:定义你想要请求的 API,告诉 Retrofit 你需要什么数据。
- 配置 Retrofit 实例:设置 Retrofit,告诉它请求的 URL 和如何处理数据。
- 发起请求:通过 Retrofit 请求网络数据,并处理返回的结果。
步骤 1: 安装 Retrofit
首先,我们需要在我们的项目中添加 Retrofit 这个工具。就像是我们需要下载一个游戏的安装包一样。
- 打开 Android Studio。
- 找到你项目中的
build.gradle
文件(就是一个列出我们需要用到的工具和库的清单)。 - 在这个文件里添加 Retrofit 的依赖(就像是给我们的游戏添加功能):
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0' // 这是 Retrofit 这个工具
implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // 用来转换数据的工具
}
然后点击 Sync Now,就像是安装游戏一样,安装完成后你就可以使用 Retrofit 了。
步骤 2: 创建 Retrofit 接口
接下来我们要告诉 Retrofit,我要去哪里获取数据(就像告诉游戏你要去哪里玩)。
接口 是一个用来定义我们需要请求哪些数据的地方。我们可以把它想象成是告诉 Retrofit 我们要去哪里拿数据。
比如我们想去一个网站,看看 GitHub 上某个人的仓库(就像是我们想要知道某个游戏的排名)。我们可以定义一个接口来告诉 Retrofit 我们需要这些信息。
public interface ApiService {
@GET("users/{user}/repos") // 这里是 GitHub 的地址
Call<List<Repo>> listRepos(@Path("user") String user); // 这个方法去获取某个用户的所有仓库
}
这里我们用了一个 GET 请求,意思是去获取数据。
步骤 3: 创建 Retrofit 实例
接下来,我们需要让 Retrofit 开始工作,就像启动游戏一样。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/") // 告诉 Retrofit 去哪里获取数据
.addConverterFactory(GsonConverterFactory.create()) // 使用 Gson 来帮助我们理解数据
.build();
步骤 4: 发起请求
现在我们需要告诉 Retrofit 去获取数据。我们只需要告诉它一个用户的名字,它就会帮我们去获取这个用户的所有仓库。
ApiService apiService = retrofit.create(ApiService.class); // 创建接口
Call<List<Repo>> call = apiService.listRepos("octocat"); // 发起请求,获取 octocat 的仓库
步骤 5: 处理结果
当我们请求数据时,Retrofit 会帮我们处理所有的事情。我们只需要准备好两件事情:
- 如果请求成功了,我们怎么处理结果(就像完成任务后获得奖励)。
- 如果请求失败了,我们怎么处理失败的情况(就像遇到障碍要重新尝试)。
call.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
if (response.isSuccessful()) { // 请求成功
List<Repo> repos = response.body(); // 获取到仓库数据
for (Repo repo : repos) {
System.out.println(repo.getName()); // 打印出每个仓库的名字
}
}
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
t.printStackTrace(); // 如果请求失败了,打印错误信息
}
});
addConverterFactory(GsonConverterFactory.create())
这个代码行是 Retrofit 的一部分,它告诉 Retrofit 如何将从网络上获取到的数据(通常是 JSON 格式)转换成 Java 对象。简单来说,它就像是一个“翻译员”,帮你把网络上收到的“外文”数据(比如 JSON)翻译成你能理解的“中文”数据(比如 Java 对象)。
具体解释:
-
Gson:它是一个工具库,可以将 JSON 数据转换成 Java 对象,也可以将 Java 对象转换成 JSON 数据。Retrofit 使用 Gson 来帮你完成这种转换。
-
ConverterFactory:这就是一个工具箱,里面有很多种转换数据的“翻译员”。
GsonConverterFactory.create()
就是告诉 Retrofit 用 Gson 这个“翻译员”来做这个工作。 -
addConverterFactory():这个方法是用来告诉 Retrofit 你希望使用哪种“翻译员”,并且让它使用这个“翻译员”来处理数据。
举个例子:
假设你从网络上请求到一个用户的信息,服务器返回的结果是这样的一段 JSON:
{
"name": "Tom",
"age": 12
}
你可能想把这段 JSON 转换成一个 Java 对象,可能是这样的:
class User {
String name;
int age;
}
然后,使用 addConverterFactory(GsonConverterFactory.create())
,Retrofit 就会帮你把这段 JSON 数据自动转换成 User
类的对象,这样你就能在程序里像使用普通 Java 对象一样使用这段数据。
总结:
addConverterFactory(GsonConverterFactory.create())
就是告诉 Retrofit 用 Gson 来帮你自动翻译 JSON 数据,让你可以直接使用 Java 对象,免去了你自己手动解析 JSON 的麻烦。
Gson 是怎么工作的?
Gson 可以帮助你把这段 JSON 数据“翻译”成一个 Java 对象,像这样:
class Person {
String name;
int age;
}
使用 Gson,你可以通过以下代码把 JSON 转换成 Java 对象:
Gson gson = new Gson();
String json = "{\"name\":\"Tom\", \"age\":12}";
Person person = gson.fromJson(json, Person.class);
这里,fromJson()
方法会把 JSON 数据转换成 Person
类型的对象。现在你就可以像使用普通 Java 对象一样,访问 person.name
或 person.age
了。
Gson 能做什么?
-
将 Java 对象转换成 JSON(序列化):
Person person = new Person(); person.name = "Tom"; person.age = 12; Gson gson = new Gson(); String json = gson.toJson(person); // 将 Java 对象转换为 JSON
-
这会得到一个 JSON 字符串:
{"name":"Tom", "age":12}
。 -
将 JSON 转换成 Java 对象(反序列化):
String json = "{\"name\":\"Tom\", \"age\":12}";
Person person = gson.fromJson(json, Person.class); // 从 JSON 创建 Java 对象
为什么要用 Gson?
- 简单易用:你只需要很少的代码就可以处理 JSON。
- 自动转换:Gson 会自动把 JSON 数据和 Java 对象的字段进行匹配。
- 高效:它处理 JSON 数据非常高效,尤其适合网络请求和响应。
总结:
Gson 就是一个用来处理 JSON 数据的工具,它让你在 Java 程序中可以轻松地和 JSON 数据打交道,把它们相互转换成 Java 对象。
使用 OkHttp3:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://api.example.com/data")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
// 请求失败
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// 请求成功,获取响应数据
}
});
使用 Retrofit:
// 1. 定义接口
public interface ApiService {
@GET("data")
Call<Data> getData(); // 自动把响应转换成 Data 对象
}
// 2. 创建 Retrofit 实例并发起请求
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create()) // Gson 解析响应
.build();
ApiService apiService = retrofit.create(ApiService.class);
Call<Data> call = apiService.getData(); // 通过接口方法发起请求
call.enqueue(new Callback<Data>() {
@Override
public void onResponse(Call<Data> call, Response<Data> response) {
// 处理响应数据,response.body() 会自动转成 Data 对象
}
@Override
public void onFailure(Call<Data> call, Throwable t) {
// 请求失败
}
});
总结:
- OkHttp3 是底层的工具,用来发起 HTTP 请求并处理响应。
- Retrofit 是基于 OkHttp3 的更高级工具,它让我们通过定义接口、注解和一些简化的代码来轻松发起 HTTP 请求,并且能自动解析服务器响应的数据。
HashMap 和 Gson 是两个完全不同的东西,它们有不同的用途和功能。下面我会简单地说明它们各自是什么,以及它们之间的区别。
1. HashMap:
HashMap 是一种数据结构,用于存储键值对(key-value pairs)。它将一个 键(key) 映射到一个 值(value)。你可以通过给定的 键 来快速查找、添加或删除 值。
- 用途:用于存储和查找数据。
- 操作:可以存储、查找、删除、更新键值对。
- 例子:比如你有一个字典,你可以通过查找词语(键)来获得它的解释(值)。
示例:
HashMap<String, String> map = new HashMap<>();
map.put("apple", "fruit");
map.put("dog", "animal");
String value = map.get("apple"); // 获取 "apple" 对应的值 "fruit"
2. Gson:
Gson 是一个用于将 Java 对象转换为 JSON 格式(序列化)和将 JSON 格式转换回 Java 对象(反序列化)的库。JSON 是一种轻量级的数据交换格式,广泛用于网络请求和响应中。
- 用途:用于在 Java 对象和 JSON 数据之间进行转换。
- 操作:将 Java 对象转换为 JSON 格式,或者将 JSON 格式的数据转换为 Java 对象。
- 例子:当你从服务器获取 JSON 数据时,可以使用 Gson 将 JSON 转换为 Java 对象,或者将 Java 对象转换为 JSON 发送到服务器。
示例:
// 将 Java 对象转换为 JSON 字符串
Person person = new Person("John", 30);
Gson gson = new Gson();
String json = gson.toJson(person); // 转换成 JSON 字符串
// 将 JSON 字符串转换为 Java 对象
String jsonString = "{\"name\":\"John\",\"age\":30}";
Person personFromJson = gson.fromJson(jsonString, Person.class);
3. HashMap 和 Gson 的区别:
-
类型和功能:
- HashMap 是一个数据结构,用来存储键值对(key-value pairs),用于高效查找、添加和删除数据。
- Gson 是一个库,用来处理 JSON 和 Java 对象之间的转换。它不直接存储数据,只是帮助将数据转换成 JSON 格式,或者从 JSON 转换回 Java 对象。
-
用途:
- HashMap 用于存储和管理键值对数据,像是字典一样。
- Gson 用于将对象和 JSON 格式之间进行转换,通常用于网络请求和响应。
-
使用场景:
- HashMap 用于程序内部存储临时数据,并根据键快速查找对应的值。
- Gson 用于与外部系统(例如服务器)交换数据,或者存储/传输数据时使用 JSON 格式。
4. 总结:
- HashMap 是一个数据结构,用于存储键值对(key-value pairs),类似字典的功能。
- Gson 是一个用于 对象和 JSON 数据 相互转换的工具库。
Interceptor
是 OKHttp 中的一个强大工具,它允许你拦截和修改 HTTP 请求和响应。你可以使用 Interceptor
来实现一些常见功能,比如记录日志、修改请求头、重试机制、缓存控制等。
使用 Interceptor
的步骤
1. 创建一个 Interceptor
Interceptor
是一个接口,有两个主要的方法:
intercept(Chain chain)
:这个方法用于拦截请求,可以在这里修改请求和响应
// 导入 OkHttp 的相关类,Interceptor 用于拦截和修改请求和响应
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
// 创建一个 LoggingInterceptor 类,它实现了 OkHttp 的 Interceptor 接口
public class LoggingInterceptor implements Interceptor {
// 重写 intercept 方法,这个方法会在请求发出之前和响应返回之后执行
@Override
public Response intercept(Chain chain) throws IOException {
// 获取原始的 HTTP 请求对象
Request request = chain.request();
// 打印请求的 URL 地址
System.out.println("Request URL: " + request.url());
// 打印请求的所有头部信息,例如 User-Agent,Content-Type 等
System.out.println("Request Headers: " + request.headers());
// 继续发送这个请求,获取响应。这里的 `chain.proceed(request)` 就是实际执行请求的地方
Response response = chain.proceed(request);
// 打印响应的状态码(例如 200 表示成功)
System.out.println("Response Code: " + response.code());
// 打印响应的头部信息
System.out.println("Response Headers: " + response.headers());
// 返回响应对象,这一步很重要,必须返回响应以继续正常流程
return response;
}
}
流程图:
- 请求 → 拦截器 1 → 拦截器 2 → 网络请求 → 网络拦截器 → 拦截器 2 → 拦截器 1 → 响应返回应用。
拦截器的执行顺序(请求):
假设我们有以下两个拦截器:
LoggingInterceptor
(打印请求信息)HeaderInterceptor
(添加额外的 header)
假设添加顺序如下:
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new LoggingInterceptor()) // 第一个拦截器
.addInterceptor(new HeaderInterceptor()) // 第二个拦截器
.build();
LoggingInterceptor
会先执行,打印请求的 URL、头部等信息。- 然后,
HeaderInterceptor
会执行,向请求中添加额外的 header(比如授权信息)。
public class LoggingInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request(); // 获取原始请求
System.out.println("Request URL: " + request.url());
// 继续执行请求
Response response = chain.proceed(request);
System.out.println("Response Code: " + response.code());
return response;
}
}
public class HeaderInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Request modifiedRequest = originalRequest.newBuilder()
.addHeader("Authorization", "Bearer some_token") // 添加一个header
.build();
// 继续请求
return chain.proceed(modifiedRequest);
}
}
import okhttp3.*;
import java.io.IOException;
public class ComplexInterceptorExample {
public static void main(String[] args) throws Exception {
// 创建 OkHttpClient 并添加多个拦截器
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new LoggingInterceptor()) // 第一个拦截器 - 日志
.addInterceptor(new HeaderInterceptor()) // 第二个拦截器 - 添加header
.addInterceptor(new CustomInterceptor()) // 第三个拦截器 - 自定义处理
.build();
// 创建请求
Request request = new Request.Builder()
.url("https://jsonplaceholder.typicode.com/posts")
.build();
// 发送请求
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
System.out.println("请求失败: " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println("最终响应: " + response.body().string());
}
});
}
// 第一个拦截器:日志拦截器
static class LoggingInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request(); // 获取原始请求
System.out.println("日志拦截器: 请求 URL: " + request.url());
// 打印请求的头信息
System.out.println("日志拦截器: 请求头: " + request.headers());
// 继续请求
Response response = chain.proceed(request);
// 打印响应的状态码
System.out.println("日志拦截器: 响应码: " + response.code());
// 返回响应
return response;
}
}
// 第二个拦截器:添加 Header
static class HeaderInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
// 在请求头添加自定义的 Authorization token
Request modifiedRequest = originalRequest.newBuilder()
.addHeader("Authorization", "Bearer some_token")
.build();
// 打印修改后的请求头
System.out.println("Header拦截器: 添加了 Authorization header");
// 继续请求
return chain.proceed(modifiedRequest);
}
}
// 第三个拦截器:模拟延迟处理
static class CustomInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
System.out.println("Custom拦截器: 在请求前做一些处理");
// 模拟延迟处理
try {
Thread.sleep(1000); // 假设在此做了一些长时间处理
} catch (InterruptedException e) {
e.printStackTrace();
}
// 继续发送请求
Response response = chain.proceed(request);
// 模拟响应后处理
System.out.println("Custom拦截器: 在响应后做一些处理");
// 返回响应
return response;
}
}
}
Retrofit 拦截器的工作原理
- 拦截器的作用:拦截器可以在请求被发送到服务器之前和响应被返回给应用之前进行拦截。这使得它能够修改请求和响应,比如添加自定义的请求头、打印日志、处理缓存等。
- 拦截器的类型:OkHttp 拦截器主要分为两种:
- 应用拦截器:它可以访问整个请求和响应,并可以修改它们。应用拦截器是一次性拦截请求或响应的。
- 网络拦截器:它与应用拦截器不同,网络拦截器允许你访问底层网络响应(例如,查看重定向和缓存的响应)。
如何在 Retrofit 中使用拦截器
-
创建拦截器: 你可以创建一个自定义的拦截器类,来控制请求和响应的行为。
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class LoggingInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
// 获取请求
Request request = chain.request();
// 打印请求信息
System.out.println("Request URL: " + request.url());
System.out.println("Request Headers: " + request.headers());
// 执行请求并获取响应
Response response = chain.proceed(request);
// 打印响应信息
System.out.println("Response Code: " + response.code());
System.out.println("Response Headers: " + response.headers());
// 返回响应
return response;
}
}
将拦截器添加到 Retrofit: 使用 OkHttp 的拦截器机制,我们可以将这个拦截器添加到 Retrofit 实例中。拦截器会在每个请求的生命周期中起作用。
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class RetrofitInstance {
private static Retrofit retrofit = null;
public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
// 创建 OkHttpClient 实例并添加拦截器
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new LoggingInterceptor()) // 添加自定义拦截器
.build();
// 创建 Retrofit 实例并返回
retrofit = new Retrofit.Builder()
.baseUrl("https://jsonplaceholder.typicode.com/")
.client(client) // 将 OkHttpClient 添加到 Retrofit 中
.addConverterFactory(GsonConverterFactory.create()) // 添加转换器
.build();
}
return retrofit;
}
}
使用 Retrofit 发起请求: 一旦你将拦截器添加到 Retrofit 中,它将自动应用于所有通过 Retrofit 发起的请求。
public interface ApiService {
@GET("posts")
Call<List<Post>> getPosts();
}
// 使用 Retrofit 实例发送请求
ApiService apiService = RetrofitInstance.getRetrofitInstance().create(ApiService.class);
Call<List<Post>> call = apiService.getPosts();
call.enqueue(new Callback<List<Post>>() {
@Override
public void onResponse(Call<List<Post>> call, Response<List<Post>> response) {
// 请求成功
}
@Override
public void onFailure(Call<List<Post>> call, Throwable t) {
// 请求失败
}
});
1. HttpURLConnection
HttpURLConnection
是 Android 中最基础的 HTTP 请求方式,用于从服务器获取数据或发送数据。它是 Java 自带的类,适用于大多数简单的 HTTP 请求。
使用示例:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class HttpURLConnectionExample {
public static void main(String[] args) throws Exception {
// 目标网址
URL url = new URL("https://api.example.com/data");
// 打开连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置请求方式
connection.setRequestMethod("GET");
// 设置连接超时和读取超时
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
// 添加请求头(headers)
connection.setRequestProperty("User-Agent", "Mozilla/5.0");
connection.setRequestProperty("Accept", "application/json");
connection.setRequestProperty("Authorization", "Bearer YOUR_ACCESS_TOKEN");
// 获取响应码
int responseCode = connection.getResponseCode();
System.out.println("Response Code: " + responseCode);
// 如果响应码是200(表示成功),读取响应内容
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
System.out.println("Response Content: " + response.toString());
} else {
System.out.println("Request failed");
}
// 关闭连接
connection.disconnect();
}
}
解释:
- URL: 请求的地址。
- HttpURLConnection: 用来创建 HTTP 连接。
- setRequestMethod("GET"): 设置请求方式(GET、POST 等)。
- getResponseCode(): 获取服务器响应的 HTTP 状态码(例如 200 表示成功,404 表示未找到页面)。
- getInputStream(): 获取服务器响应的内容。
- disconnect(): 关闭连接。
优缺点:
- 优点:比较简单、轻量,适合一般的网络请求。
- 缺点:API 较为底层,处理起来不如一些第三方库方便,且不支持很多现代化功能(例如请求体处理、文件上传等)。
2. HttpClient
HttpClient
是 Apache 提供的一个 HTTP 客户端库,可以用来发送 HTTP 请求。它比 HttpURLConnection
功能更强大,支持更丰富的功能(如请求重试、认证、文件上传等)。
在 Android 中,HttpClient
已经被废弃,但很多老版本的 Android 应用还是使用它。如果你要使用 HttpClient,需要将其作为依赖库导入。
使用示例:
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
public class HttpClientExample {
public static void main(String[] args) throws Exception {
// 创建 HttpClient 实例
CloseableHttpClient client = HttpClients.createDefault();
// 创建 GET 请求
HttpGet request = new HttpGet("https://api.example.com/data");
// 设置请求头参数
request.setHeader("User-Agent", "Mozilla/5.0");
request.setHeader("Accept", "application/json");
request.setHeader("Authorization", "Bearer YOUR_ACCESS_TOKEN");
// 执行请求
CloseableHttpResponse response = client.execute(request);
// 获取响应内容
HttpEntity entity = response.getEntity();
String content = EntityUtils.toString(entity);
// 打印响应内容
System.out.println("Response Content: " + content);
// 关闭连接
response.close();
client.close();
}
}
解释:
- HttpClients.createDefault(): 创建一个默认的
HttpClient
实例。 - HttpGet: 创建一个 GET 请求。
- execute(request): 执行 HTTP 请求并获取响应。
- EntityUtils.toString(entity): 从响应中提取字符串数据。
优缺点:
- 优点:功能强大、灵活,适合复杂的请求(如带有认证的请求、文件上传等)。
- 缺点:不再推荐使用,因为 Android 已经移除了
HttpClient
,并且它的性能较HttpURLConnection
更差。
1. BufferedReader 是什么?
想象一下,你在用吸管喝水。吸管就像是 BufferedReader
,它帮助你顺畅地喝水,而不是每次喝一点点。BufferedReader
就是一个帮你快速从大杯子里喝水的吸管,它能一次性把很多水(文本)都吸到嘴里,不需要你每次都喝一点点。
2. getInputStream 是什么?
假设你打开了一瓶饮料,这瓶饮料代表了一个连接,比如网络连接。getInputStream()
就像是打开瓶子后,你从瓶子里倒饮料(数据)出来喝。这时,你得到的饮料是字节流,它是一些看不懂的“数字”,就像是瓶子里的水看不出来是什么口味一样。
3. InputStreamReader 是什么?
现在你有了“饮料”(字节流),但是你还是不清楚它是什么味道,因为它是“数字”。为了知道它是什么味道,你需要一个工具,这个工具叫做 InputStreamReader
。它可以把这些“数字”转换成你能理解的“文字”或者“味道”,让你知道你喝的到底是什么。
4. 怎么一起使用这些东西?
假设你在喝饮料,整个过程是这样:
- 打开瓶子(通过
getInputStream()
获取字节流)。 - 倒出来(通过
InputStreamReader
把字节流转化为我们能理解的文字)。 - 吸管(
BufferedReader
)帮你一口气喝更多的水(高效读取文字)。
例子:
假设你想从互联网上读取数据(就像喝一瓶饮料),我们如何做呢?
getInputStream()
就像打开瓶子,获取网络上的数据。InputStreamReader
就是一个工具,可以把原本难懂的字节“数字”转换成你能读懂的“文字”。BufferedReader
就是帮你高效、快速读取每一口“文字”的吸管。
代码例子:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
public class Example {
public static void main(String[] args) {
try {
// 就像打开瓶子,准备读取数据
URL url = new URL("https://api.example.com/data");
// 让连接到网络
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 获取网络数据(字节流)
InputStreamReader inputStreamReader = new InputStreamReader(connection.getInputStream());
// 使用BufferedReader(吸管)来读取数据(每次读取一口)
BufferedReader reader = new BufferedReader(inputStreamReader);
String line;
StringBuilder response = new StringBuilder();
// 一口一口地读取数据,直到全部读取完
while ((line = reader.readLine()) != null) {
response.append(line);
}
// 打印读取到的内容
System.out.println(response.toString());
// 关闭连接
reader.close();
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
总结:
getInputStream
: 就像是打开瓶子,获取饮料(数据)。InputStreamReader
: 就像是一个工具,可以把“数字”变成“文字”。BufferedReader
: 就像是吸管,帮你一次性高效地喝掉更多的饮料(读取更多的文字)。
什么是 SOAP?
SOAP(全名是 Simple Object Access Protocol)是一种用来在不同的系统之间交换信息的 协议,特别是在网络上交换数据时非常有用。它通常用于 Web 服务,Web 服务就是不同应用程序之间通过网络进行交流的一种方式。
举个例子:
想象一下,你有一个应用程序,它需要向另一个应用程序请求信息。你可能想要知道天气、股市数据,或者查询数据库里的数据。为了让两个应用程序能够互相交流,它们就需要一种 “通用的语言” 来进行数据交换,这就是 SOAP。
SOAP 的特点:
-
XML格式: SOAP 使用 XML(可扩展标记语言)作为消息的格式。XML 是一种类似于 HTML 的标记语言,可以用来表示结构化的数据。
-
通过 HTTP 传输: SOAP 消息通常是通过 HTTP 发送的,这意味着它可以在 Internet 或 Intranet 上使用。
-
协议无关: SOAP 是一个协议,意味着它独立于操作系统、编程语言和其他技术。只要你的系统支持 HTTP,你就能使用 SOAP。
SOAP 的结构:
SOAP 消息包含几个重要的部分,主要是:
-
Envelope(信封):这是 SOAP 消息的外层包装,用来包含消息的所有内容。
-
Header(头部):可选部分,包含一些消息的元信息(比如认证信息等)。
-
Body(主体):消息的主要内容,就是你要传输的数据。
SOAP 消息的例子:
下面是一个简单的 SOAP 请求消息的例子,使用 XML 格式表示:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:web="http://www.example.com/">
<soapenv:Header/>
<soapenv:Body>
<web:GetWeather>
<web:CityName>Shanghai</web:CityName>
</web:GetWeather>
</soapenv:Body>
</soapenv:Envelope>
<soapenv:Envelope>
:是消息的信封,告诉接收方这个消息是一个 SOAP 消息。<soapenv:Body>
:是消息的主体部分,包含了你要传递的数据。在这里,我们请求获取天气信息。<web:GetWeather>
和<web:CityName>
:是你请求的 Web 服务的方法和参数。在这个例子中,你请求的是获取上海的天气。
是的,xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
是固定的,通常不需要更改。
为什么是固定的?
http://schemas.xmlsoap.org/soap/envelope/
这个 URL 指定了 SOAP 协议的标准格式和结构,它是 SOAP 消息的 命名空间。这个命名空间定义了 SOAP 消息的信封(Envelope),其中包括了消息的头(Header)和主体(Body)部分,任何符合 SOAP 标准的 Web 服务都会使用这个命名空间。
-
区分不同服务的元素:如果你的 Web 服务是
http://www.example.com/
,那么在 XML 请求中,所有的与http://www.example.com/
相关的元素都应该使用web
命名空间。这样就能确保请求中不会与其他 Web 服务产生冲突。 -
中间的接口:可以认为
web
命名空间就是连接客户端和服务器之间的“桥梁”,它帮助客户端明确指向某个特定的 Web 服务,并告诉服务器客户端请求的是什么内容。
为什么使用 SOAP?
SOAP 主要用在系统之间进行跨平台通信时,因为它可以使用 HTTP 来发送消息,而 HTTP 几乎可以在所有计算机系统中使用。
SOAP 的缺点:
- 相比一些现代的技术,SOAP 可能比较复杂。
- 它需要使用 XML 来进行消息的包装,比较繁琐,且数据量大。
总结:
- SOAP 是一种标准的协议,用来让不同的计算机系统通过网络交换数据。
- 它使用 XML 格式来表示消息,发送和接收数据。
- SOAP 通常用于 Web 服务,帮助不同的平台和编程语言的应用程序进行通信。
WebService 就像是不同计算机或手机之间的一种"语言"。它让你可以用电脑和其他设备进行沟通,互相交换信息。就像你跟朋友打电话问问题,朋友把答案告诉你,但你和朋友之间得有一个共同的语言或规则来沟通。
举个例子:
假设你想知道你家附近的天气,你可以通过手机向天气公司发出一个请求:“告诉我今天的天气”。然后,天气公司会用它们的计算机把天气信息返回给你。
这个过程的中介就是 WebService。它就像一个邮递员,把你发的问题(请求)带给天气公司(计算机),然后把答案带回给你。
用通俗的话来说:
- WebService 就是一个系统,让两个不一样的设备(比如手机和电脑)通过互联网相互交流和获取数据。
- 你向 WebService 提出一个问题,WebService 会把这个问题发送到一个服务端(比如一个天气系统),然后把回答返回给你。
所以,WebService 本质上就是一种帮助不同计算机之间互相“沟通”的工具,它让你可以从远程的计算机上获取数据或服务,就像从别人那里获取信息一样。
要使用 WebService 在 Android 上进行通信,通常我们会使用 SOAP
或 REST
这两种方式。下面是用 Android 调用 WebService 的一个简单例子,使用 SOAP 协议:
1. 添加依赖项
在 build.gradle
文件中,添加 SOAP 支持的库:
implementation 'com.koushikdutta.async:http:2.0.1'
2. SOAP WebService 调用代码
// 导入必要的库
import android.os.AsyncTask; // 用于异步执行任务
import android.os.Bundle; // 用于处理活动生命周期
import android.widget.TextView; // 用于显示文本信息
import androidx.appcompat.app.AppCompatActivity; // 用于支持现代 Android 活动
import org.ksoap2.SoapEnvelope; // 用于处理 SOAP 协议消息
import org.ksoap2.serialization.SoapObject; // 用于构建 SOAP 请求对象
import org.ksoap2.transport.HttpTransportSE; // 用于发送 SOAP 请求
// 创建 MainActivity 类,继承自 AppCompatActivity
public class MainActivity extends AppCompatActivity {
// 定义 WebService 的相关常量
private static final String NAMESPACE = "http://www.example.com/"; // WebService 的命名空间
private static final String URL = "http://www.example.com/webservice"; // WebService 的 URL
private static final String SOAP_ACTION = "http://www.example.com/GetWeather"; // SOAP 动作
private static final String METHOD_NAME = "GetWeather"; // WebService 的方法名称
private TextView textView; // 创建一个 TextView 变量用于显示结果
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // 调用父类的 onCreate 方法
setContentView(R.layout.activity_main); // 设置布局文件
textView = findViewById(R.id.textView); // 获取布局中的 TextView
// 执行异步任务来调用 WebService
new WebServiceTask().execute(); // 执行 WebServiceTask 异步任务
}
// 定义一个异步任务类,用于调用 WebService
private class WebServiceTask extends AsyncTask<Void, Void, String> {
// doInBackground 方法在后台线程执行
@Override
protected String doInBackground(Void... params) {
try {
// 创建 SOAP 请求对象,指定 WebService 的命名空间和方法名
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
// 添加请求参数(城市名称)
request.addProperty("CityName", "Shanghai");
// 创建 SOAP 包装对象,用于封装请求
SoapEnvelope envelope = new SoapEnvelope(SoapEnvelope.VER11); // 使用 SOAP 1.1 版本
envelope.setOutputSoapObject(request); // 设置请求对象
// 创建 HTTP 请求传输对象,用于向 WebService 发送请求
HttpTransportSE transport = new HttpTransportSE(URL); // 使用指定的 URL
transport.call(SOAP_ACTION, envelope); // 发送请求,调用 SOAP 方法
// 获取 WebService 返回的响应
SoapObject response = (SoapObject) envelope.getResponse(); // 获取响应对象
return response.getProperty(0).toString(); // 获取响应的第一个属性值(即返回的天气信息)
} catch (Exception e) {
e.printStackTrace(); // 如果发生错误,打印异常信息
return "Error"; // 返回错误信息
}
}
// onPostExecute 方法在主线程执行,用于更新 UI
@Override
protected void onPostExecute(String result) {
// 在 UI 上显示 WebService 返回的结果
textView.setText(result); // 将结果显示在 TextView 中
}
}
}
代码的解释:
private static final String NAMESPACE = "http://www.example.com/"; // WebService 的命名空间
private static final String URL = "http://www.example.com/webservice"; // WebService 的 URL
private static final String SOAP_ACTION = "http://www.example.com/GetWeather"; // SOAP 动作
private static final String METHOD_NAME = "GetWeather"; // WebService 的方法名称
- NAMESPACE:是 WebService 的命名空间,用来区分不同 WebService 的方法。它类似于一个标签,帮助服务器识别你请求的是哪个服务。
- URL:是 WebService 服务器的地址,告诉你请求的服务在哪儿。
- SOAP_ACTION:是 WebService 对应的动作,类似于方法,告诉服务器你要做的具体操作。
- METHOD_NAME:表示你请求的具体方法名,告诉服务器你想做什么(例如查询天气、获取数据等)。
总结:
- URL:指向服务器的地址,就像你去某个地方的门牌号。
- METHOD_NAME:表示你想做什么(比如查询天气,叫做
GetWeather
)。 - 请求参数:告诉 WebService 你需要哪些数据(例如城市名
Shanghai
)。
1. WebService 端 (模拟):
为了理解 WebService 调用,我们可以先创建一个简单的 WebService 端。假设 WebService 提供一个天气查询接口,客户端通过 SOAP 请求发送城市名称,WebService 返回该城市的天气。
示例 WebService (假设已发布在服务器上)
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://www.example.com/">
<soapenv:Header/>
<soapenv:Body>
<web:GetWeather>
<web:CityName>Shanghai</web:CityName>
</web:GetWeather>
</soapenv:Body>
</soapenv:Envelope>
这个示例包含一个 GetWeather
方法,它接受一个 CityName
参数,返回该城市的天气。
服务器端实现代码(示例):
为了在服务器端返回 Shanghai
的天气信息,你需要在 WebService 服务端编写代码来接收 SOAP 请求并返回响应。这里我们假设使用 Java 和 JAX-WS
来实现一个简单的 WebService。
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
public class WeatherService {
// 定义一个方法,模拟获取城市天气
@WebMethod
public String GetWeather(String CityName) {
// 根据城市名称返回天气信息(这里只是一个简单的示例)
if ("Shanghai".equalsIgnoreCase(CityName)) {
return "Temperature in Shanghai: 25°C"; // 返回天气信息
} else {
return "City not found"; // 返回未找到城市的信息
}
}
}
2. 客户端 (Android 中调用 WebService):
这个 Android 客户端代码通过 ksoap2
库发送 SOAP 请求,调用 WebService,并展示返回结果。
WebService 调用代码:
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.transport.HttpTransportSE;
public class MainActivity extends AppCompatActivity {
// WebService 的相关常量
private static final String NAMESPACE = "http://www.example.com/"; // WebService 的命名空间
private static final String URL = "http://www.example.com/webservice"; // WebService 的 URL
private static final String SOAP_ACTION = "http://www.example.com/GetWeather"; // SOAP 动作
private static final String METHOD_NAME = "GetWeather"; // WebService 的方法名称
private TextView textView; // 显示结果的 TextView
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // 设置布局文件
textView = findViewById(R.id.textView); // 获取 TextView 实例
// 执行异步任务来调用 WebService
new WebServiceTask().execute(); // 执行任务
}
// 定义一个异步任务,用于调用 WebService
private class WebServiceTask extends AsyncTask<Void, Void, String> {
// 在后台线程执行 WebService 调用
@Override
protected String doInBackground(Void... params) {
try {
// 创建 SOAP 请求对象,指定命名空间和方法名
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
// 添加请求参数(城市名称)
request.addProperty("CityName", "Shanghai");
// 创建 SOAP 包装对象
SoapEnvelope envelope = new SoapEnvelope(SoapEnvelope.VER11); // 使用 SOAP 1.1 版本
envelope.setOutputSoapObject(request); // 设置请求对象
// 创建 HTTP 请求传输对象
HttpTransportSE transport = new HttpTransportSE(URL);
transport.call(SOAP_ACTION, envelope); // 发送请求并获取响应
// 获取响应结果
SoapObject response = (SoapObject) envelope.getResponse();
return response.getProperty(0).toString(); // 返回天气信息
} catch (Exception e) {
e.printStackTrace(); // 出现错误时打印异常
return "Error"; // 返回错误信息
}
}
// 在主线程更新 UI
@Override
protected void onPostExecute(String result) {
textView.setText(result); // 显示 WebService 返回的结果
}
}
}