Android应用:实现网络加载商品数据【OKHttp、Glide、Gson】
实现网络加载商品数据的功能:
1、在AndroidManifest.xml中声明网络权限;
2、在app/build.gradle中添加okhttp, glide, gson等必需的第3方库;
3、在MainActivity中通过OkHttpClient连接给定的Web服务,获取商品数据;对应的json数据为本地的json文件,名字为goods_list_data.json;数据内容为:[
{“id”:1,“count”:“5.4万”,“goodsName”:“富士拍立得相机”,“goodsPic”:“/img/polaroid.png”},
{“id”:2,“count”:“5.3万”,“goodsName”:“格兰仕微波炉”,“goodsPic”:“/img/microwave_oven.png”},
{“id”:3,“count”:“1.4万”,“goodsName”:“新国标电动车”,“goodsPic”:“/img/electric_vehicle.png”},
{“id”:4,“count”:“1.6万”,“goodsName”:“官方订制投影仪”,“goodsPic”:“/img/projector.png”},
{“id”:5,“count”:“0.4万”,“goodsName”:“美的35L烤箱”,“goodsPic”:“/img/oven.png”},
{“id”:6,“count”:“3.3万”,“goodsName”:“儿童学习桌”,“goodsPic”:“/img/learning_table.png”}
]
对应的图片也存储在本地的img文件中
4、使用gson库解析JSON格式的商品数据,转成java bean商品数据对象(Goods类)的列表;
5、创建MsgHandler类,用于异步更新商品列表;
6、在GoodsAdapter中通过glide控件加载并显示网络图片。
1.部署网络图片资源
首先,我们需要将对应的文件部署在一个简易的服务器(Tomcat)中,服务器中存放数据的目录结构如下图所示
E:.
├─goods
│ └─img
│ └─goods_list_data.json
└─WEB-INF
其中,ROOT目录在"apache-tomcat-9.0.65-windows-x64\webapps\ROOT"
下,表示Tomcat服务器的根目录。
- goods文件夹存放的是商品列表所用到的数据
- 其中goods\img文件夹存放的是商品的图片资源
goods_list_data.json
文件存放的是商品列表的数据,具体如下所示
[
{"id":1,"count":"5.4万","goodsName":"富士拍立得相机","goodsPic":"/img/polaroid.png"},
{"id":2,"count":"5.3万","goodsName":"格兰仕微波炉","goodsPic":"/img/microwave_oven.png"},
{"id":3,"count":"1.4万","goodsName":"新国标电动车","goodsPic":"/img/electric_vehicle.png"},
{"id":4,"count":"1.6万","goodsName":"官方订制投影仪","goodsPic":"/img/projector.png"},
{"id":5,"count":"0.4万","goodsName":"美的35L烤箱","goodsPic":"/img/oven.png"},
{"id":6,"count":"3.3万","goodsName":"儿童学习桌","goodsPic":"/img/learning_table.png"}
]
启动tomcat后,可访问http://localhost:8080/goods/goods_list_data.json
展示信息
2.创建项目
- 打开Android Studio,并创建一个新的Android项目。
- 命名项目并选择适当的目标API级别和设备类型。
- 创建一个新的空白活动(Empty Activity)。
3.在AndroidManifest.xml中声明网络权限
在 AndroidManifest.xml 文件中添加以下权限声明,以便应用可以访问网络:
<uses-permission android:name="android.permission.INTERNET" />
由于网络安全策略导致的问题,即不允许在明文(非加密)的情况下与 localhost 进行通信。这通常涉及到网络安全配置,特别是在 Android 9.0(API级别28)及更高版本中引入了更严格的网络安全策略;因此我们还需要进行配置网络安全配置文件
解决此问题的方法之一是使用 HTTPS 协议而不是 HTTP,因为 HTTPS 是加密的。
如果在本地测试应用,可以使用 Android 的网络安全配置文件
来允许明文通信。
- 在
res/xml
文件夹中创建一个名为network_security_config.xml
的网络安全配置文件。如果该文件夹不存在,请手动创建。
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>
- 在
AndroidManifest.xml
文件中,将这个网络安全配置文件应用于你的应用。在<application>
元素内添加android:networkSecurityConfig
属性,如下所示:
<application
android:networkSecurityConfig="@xml/network_security_config"
<!-- 其他属性和元素 -->
>
<!-- 其他元素 -->
</application>
这将允许你的应用在本地开发和测试过程中与 localhost 进行明文通信。但请注意,在生产环境中,强烈建议使用 HTTPS 以确保数据的安全传输。
如果你使用的是模拟器或真机设备,请确保重新构建和部署应用,以使配置生效。此外,如果你的服务器正在本地运行,请确保服务器端口和地址正确。
4.添加依赖库
在app/build.gradle
文件中添加OkHttp、Glide、Gson依赖库。
implementation("com.squareup.okhttp3:okhttp:4.9.1")
implementation("com.squareup.okhttp3:logging-interceptor:4.9.1")
implementation("com.google.code.gson:gson:2.8.8")
implementation("com.github.bumptech.glide:glide:4.12.0")
annotationProcessor("com.github.bumptech.glide:compiler:4.12.0")
进行同步
等待安装依赖……
5.创建一个XML布局文件
在res/layout
文件夹中创建一个布局文件,例如activity_main.xml
,用于显示商品数据。
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"/>
创建一个XML布局文件用于显示商品项
在res/layout
文件夹中创建一个布局文件,例如item_goods.xml
,用于显示每个商品项。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<ImageView
android:id="@+id/goodsImageView"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/goodsNameTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp" />
</LinearLayout>
6.创建一个Java Bean类
创建一个Goods
类,用于表示商品数据。
public class Goods {
private int id;
private String count;
private String goodsName;
private String goodsPic;
// Getters and setters
}
7.创建一个适配器类
创建一个自定义适配器类GoodsAdapter
,用于将商品数据绑定到RecyclerView中。
public class GoodsAdapter extends RecyclerView.Adapter<GoodsAdapter.ViewHolder> {
private List<Goods> goodsList;
private Context context;
public GoodsAdapter(Context context, List<Goods> goodsList) {
this.context = context;
this.goodsList = goodsList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_goods, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Goods goods = goodsList.get(position);
holder.goodsNameTextView.setText(goods.getGoodsName());
// Load and display image using Glide
Glide.with(context)
.load(goods.getGoodsPic())
.into(holder.goodsImageView);
}
@Override
public int getItemCount() {
if (goodsList != null) {
return goodsList.size();
} else {
return 0; // 返回0表示没有数据
}
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView goodsNameTextView;
ImageView goodsImageView;
ViewHolder(View itemView) {
super(itemView);
goodsNameTextView = itemView.findViewById(R.id.goodsNameTextView);
goodsImageView = itemView.findViewById(R.id.goodsImageView);
}
}
}
8.实现网络加载数据
在MainActivity.java
中实现网络加载商品数据的功能。请确保你的goods_list_data.json
文件位于app/src/main/assets
文件夹下。
package com.leo.network;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.gson.Gson;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private GoodsAdapter adapter;
private List<Goods> goodsList;
private static final int MSG_UPDATE_DATA = 1;
private Handler msgHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == MSG_UPDATE_DATA) {
adapter.notifyDataSetChanged();
}
return true;
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new GoodsAdapter(this, goodsList);
recyclerView.setAdapter(adapter);
// Fetch data from the network
fetchGoodsData();
}
private void fetchGoodsData() {
new Thread(new Runnable() {
@Override
public void run() {
try {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://10.0.2.2:8080/goods/goods_list_data.json")
.build();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
String jsonData = response.body().string();
Log.d("Network", "Data fetched successfully: " + jsonData);
Gson gson = new Gson();
Goods[] goodsArray = gson.fromJson(jsonData, Goods[].class);
// 补全图片URL
for (Goods goods : goodsArray) {
goods.setGoodsPic("http://10.0.2.2:8080//goods" + goods.getGoodsPic());
}
goodsList = Arrays.asList(goodsArray);
msgHandler.sendEmptyMessage(MSG_UPDATE_DATA);
// 切换到主线程以更新UI
runOnUiThread(new Runnable() {
@Override
public void run() {
// 设置RecyclerView的适配器
adapter = new GoodsAdapter(MainActivity.this, goodsList);
recyclerView.setAdapter(adapter);
}
});
}
} catch (IOException e) {
e.printStackTrace();
Log.e("Network", "Error fetching data: " + e.getMessage());
}
}
}).start();
}
}
替换"URL_TO_YOUR_JSON_DATA"
为你的本地JSON文件的路径,例如:http://localhost:8080/goods/goods_list_data.json
。
注意,如果你是本地部署的tomcat,需要在Android默认的虚拟器中访问,需要改为“
10.0.2.2
”
以下是对代码的一些说明:
-
在
fetchGoodsData()
方法中创建了一个新的线程来执行网络请求。这是一个良好的实践,因为它确保网络请求不会阻塞主线程,以避免应用的响应性问题。 -
在网络请求成功后使用 Gson 库将 JSON 数据解析为
Goods
对象的数组,并补全了图片的URL。这确保了 Glide 能够正确加载图片。 -
使用
Handler
来更新UI,因为 UI更新必须在主线程中执行。 -
在网络请求失败时,通过
Log.e
打印了错误消息,这有助于调试和问题排查。
实现效果