在实验的安卓界面完成后,下一步就是对接后台了。在实际的应用中,通常需要从后台接口中获取或修改数据库数据,展示在安卓界面中。为了完成这样的功能,学习了安卓的网络编程实例。
使用网络获取数据
使用网络技术也很简单,无非是发送http
请求并解析返回数据,在这里使用了android
的网络通信库OkHttp
,它能更加简化网络请求的步骤。
首先在app/build.gradle
文件中声明OkHttp
依赖:
implementation("com.squareup.okhttp3:okhttp:4.2.1")
在AndroidMainfest.xml
中声明网络权限:
创建一个OkHttpClient对象,再创建一个request请求发起一个http请求:
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder().url("http://192.168.2.234:8080/city").build();
Response response = okHttpClient.newCall(request).execute();
其中返回来的json数据,可以从response.string()
中获取到返回流的json字符串,再用相应的json解析工具,类似gson解析json就可以获取到我们想要的数据了。
// 使用gson解析json数据
City[] cities = gson.fromJson(response.body().string(), City[].class);
子线程请求数据
当请求服务器获取数据时,考虑到网络原因,服务器往往不能立刻响应我们的网络请求,如果在主线程中使用网络请求,会造成主线程堵塞,给用户带来不好的体验,甚至会因为长时间未响应被关闭。因此,网络请求应该在子线程中进行。
new Thread(new Runnable() {
@Override
public void run() {
// 使用http请求
....
}
}).start();
获取到数据以后,应该把获取到的数据更新到界面上。但是,安卓并不允许在子线程中更新ui,因为多线程更新ui会产生许多的线程同步和线程安全问题。要在子线程中解决ui更新问题,还得使用安卓的异步消息处理机制。
异步消息处理
因为之前一直在写web,说到异步处理就想起了JavaScript。与安卓一样,js也是频繁的修改dom,为此,js设计为单线程的语言。js处理异步的方式就是采用事件循环,当主线程跑完代码后,就会不断循环从消息队列中读取消息执行。安卓的异步消息处理与js的类似,主要由四个部分组成:Message、Handler、MessageQueue、Looper。
Message
Message表示在线程之间传递的消息,它可以携带少量的信息在不同的线程中交换数据。通过Message的what、arg1、arg2、obj字段来携带数据。
Handler
处理者,用于发送和处理消息。通过Handler的sendMessage()方法发送消息,被looper回调后通过handleMessage处理消息。
MessageQueue
消息队列,存放所有通过handler发送的消息。每个线程只有一个消息队列。
Looper
Lopper相当于每个线程中MessageQueue的管家,它会处于无限循环中,每当发现MessageQueue存在一条消息,就会将它取出,并传递到Handler的handlerMessage()方法中,每个线程也只有一个looper。
他们之间的协作关系如下图:
使用异步消息发送http请求:
public class MainActivity extends AppCompatActivity {
private Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
Object message = msg.obj; // 获取携带的数据
// 这里可以更新ui
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
new Thread(new Runnable() {
@Override
public void run() {
// 发送http请求
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder().url("http://192.168.2.234:8080/city").build();
Response response = okHttpClient.newCall(request).execute();
City[] cities = gson.fromJson(response.body().string(), City[].class);
Message message = new Message();
message.obj = cities; // 这里可以携带数据
handler.sendMessage(message);
}
}).start();
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
来梳理以下具体的流程:我们创建了一个Handler对象并覆写了它的handlerMessage方法,然后在子线程中发送http请求,等请求返回数据后,创建一个Message对象,将请求回来的数据放入Message对象的obj字段中,然后通过handler.sendMessage()方法将消息放入消息队列中。因为handler对象是在主线程中创建的,所以消息被放在了主线程的消息队列中。在主线程的looper对象发现消息队列中存在消息时,就会把消息取出,执行Message的Hanler的handlerMessage方法。因为handlerMessage方法是在主线程中运行的,所以可以对ui进行操作。
基于安卓的异步消息的处理机制,我们就可以在子线程发送http请求数据,然后在主线程中更新ui了。