配置
权限配置
要注意配置的位置
1 添加网络权限
<manifest >
<application> .......</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
2 配置明文网络流量
<application
...
android:usesCleartextTraffic="true"
...>
<activity>
...
...
</activity>
</application>
android:usesCleartextTraffic 指示应用程序是否打算使用明文网络流量,例如明文HTTP。目标API级别为27或更低的应用程序的默认值为“ true”。面向API级别28或更高级别的应用默认为“ false”。
封装函数
GET 请求
这个也是我自己封装的专门用来发送 http 请求的类(我的请求没用到 post)
public class HttpUtil {
/**
* 发起 get 请求
* @param api 请求的 api
* @param pc 是否需要 PC 端的请求头,为了应对有些 api 只有 pc 端才能请求成功,但这只是极少数,一般为 false
* @return 响应的 String 类型数据
*/
public static String getApi(String api, boolean pc){
Log.d("start","正在调用...");
// 初始化连接对象
HttpURLConnection connection=null;
try {
// 创建 URL
URL url = new URL(api);
// 开启对应 URL 的连接
connection = (HttpURLConnection) url.openConnection();
// 需要 PC 请求头
if(pc){
// 设置 PC 请求头
// 注意这里的 SystemConstant.PC_USER_AGENT 是我自己封装的系统常量,粘贴时需要改动
connection.setRequestProperty("user-agent",SystemConstant.PC_USER_AGENT);
}
// 设置连接超时时间
connection.setConnectTimeout(3000);
// 设置传递数据超时时间
connection.setReadTimeout(3000);
// 设置请求方式 GET / POST 一定要大写
connection.setRequestMethod("GET");
// 设置为写入模式(即:从连接中读取数据)
connection.setDoInput(true);
// 将写出模式设为 false
connection.setDoOutput(false);
// 建立连接
connection.connect();
// 获取响应码
int responseCode = connection.getResponseCode();
// 请求失败 抛出异常
if (responseCode != HttpURLConnection.HTTP_OK) {
throw new IOException("HTTP error code" + responseCode);
}
// 获取响应数据
String result = getStringByStream(connection.getInputStream());
// 响应数据为 null
if (result == null) {
Log.d("fail", "失败了");
return null;
}
// 响应数据不为 null
else{
Log.d("success", "成功了 ");
return result;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 抽取的读取响应数据的函数
* @param inputStream 输入流
* @return 输入流字符串
*/
private static String getStringByStream(InputStream inputStream){
Reader reader;
try {
reader=new InputStreamReader(inputStream, StandardCharsets.UTF_8);
char[] rawBuffer=new char[512];
StringBuilder buffer=new StringBuilder();
int length;
while ((length=reader.read(rawBuffer))!=-1){
buffer.append(rawBuffer,0,length);
}
return buffer.toString();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
POST 请求
相比于 GET 请求,POST 可能需要添加 请求体参数
添加请求体参数方法:
DataOutputStream dos=new DataOutputStream(connection.getOutputStream());
String param="My param";
dos.writeBytes(param);
service 调用
在安卓开发中,http 请求必须在子线程(非主线程)中发起
封装 http 请求函数
/**
* 获取天气信息的接口
*/
public void getWeather(){
Log.d(TAG,"开始查询天气");
new Thread(() -> {
String res = HttpUtil.getApi(SystemConstant.WEATHER_API, false);
if(res == null){
return;
}
try {
JSONObject resJson = new JSONObject(res);
weatherJsonObject = resJson.getJSONArray("forecasts").getJSONObject(0);
if(weatherResponseListener!=null){
weatherResponseListener.response(weatherJsonObject);
}
} catch (JSONException e) {
e.printStackTrace();
}
}).start();
}
这里有两个关键的变量:
- weatherJsonObject:JSONObject 类型,用于存储查询到的信息。
- weatherResponseListener:自定义的监听类型 ResponseListener,用于发起的请求接收到响应数据后发送给主线程进行相关操作。
ResponseListener 接口:
public interface ResponseListener {
/**
* http 请求响应数据回调函数
* @param jsonObject 响应数据
* @throws JSONException 抛出异常
*/
void response(JSONObject jsonObject) throws JSONException;
}
关于系统使用该回调函数实现 service 主动与 activity 通信的方法,这里先不展开讲。
完整的 HttpService 代码:
public class HttpService extends Service {
// 日志标签
private static final String TAG = "HttpService";
private JSONObject weatherJsonObject;
// IBinder 对象用于传递实例
private final IBinder binder = new MyBinder();
// 向 IBinder 添加获取实例的方法
public class MyBinder extends Binder {
public HttpService getService() {
return HttpService.this;
}
}
private ResponseListener weatherResponseListener;
public void setWeatherResponseListener(ResponseListener weatherResponseListener) {
this.weatherResponseListener = weatherResponseListener;
}
/**
* 获取天气信息的接口
*/
public void getWeather(){
Log.d(TAG,"开始查询天气");
new Thread(() -> {
String res = HttpUtil.getApi(SystemConstant.WEATHER_API, false);
if(res == null){
return;
}
try {
JSONObject resJson = new JSONObject(res);
weatherJsonObject = resJson.getJSONArray("forecasts").getJSONObject(0);
if(weatherResponseListener!=null){
weatherResponseListener.response(weatherJsonObject);
}
} catch (JSONException e) {
e.printStackTrace();
}
}).start();
}
}
这个 service 是我自己封装的,相当于在最底层的 http 请求的基础上又包了一层。
activity 调用接口
这里的原理是通过绑定 service 成功时的回调方法获取 service 实例,然后调用 service 中的 setWeatherResponseListener(ResponseListener weatherResponseListener) 回调方法来接收请求的响应数据。
绑定 service 的同时通过回调获取数据并通过 Handler 通知主线程更新组件:
这些代码是在嵌套在 activity 组件中 fragment 里的,所以使用 getActivity() 来获取 activity 中的 this
public Handler handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what){
case HandlerConstant.UPDATE_WEATHER:
updateWeather();
getActivity().unbindService(serviceConnection);
break;
default:
}
}
};
/**
* 查询天气
*/
private void queryWeather(){
Log.d(TAG, "开始查询天气信息...");
// 绑定 service
Intent httpIntent = new Intent(getActivity(), HttpService.class);
getActivity().bindService(httpIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection serviceConnection = new ServiceConnection() {
/**
* 与 HttpService 绑定成功后调用
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
httpService = ((HttpService.MyBinder) (service)).getService();
if(httpService != null){
// 调用获取天气的请求
httpService.getWeather();
// 天气接口回调
httpService.setWeatherResponseListener(jsonObject -> initWeather(jsonObject));
}
}
/**
* 解绑成功后调用
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
httpService = null;
}
};
/**
* 初始化 weather
* @param jsonObject 响应数据
* @throws JSONException 抛出 JSON 异常
*/
private void initWeather(JSONObject jsonObject) throws JSONException {
weather = new Weather();
JSONObject today = jsonObject.getJSONArray("casts").getJSONObject(0);
weather.setCity(jsonObject.getString("city"));
weather.setDate(jsonObject.getString("reporttime"));
weather.setDayWeather(today.getString("dayweather"));
weather.setNightWeather(today.getString("nightweather"));
weather.setDayTemp(today.getString("daytemp"));
weather.setNightTemp(today.getString("nighttemp"));
// 这里是通知主线程调用 updateWeather()
Message msg = new Message();
msg.what = HandlerConstant.UPDATE_WEATHER;
handler.sendMessage(msg);
}
@SuppressLint("SetTextI18n")
private void updateWeather(){
cityText.setText(weather.getCity());
dateText.setText(weather.getDate());
weatherText.setText(weather.getDayWeather()+" 转 "+ weather.getNightWeather());
tempText.setText(weather.getNightTemp()+" ~ "+weather.getDayTemp()+" 摄氏度");
}
以上代码在调用 http 请求接口方面的核心为:
/**
* 查询天气
*/
private void queryWeather(){
Log.d(TAG, "开始查询天气信息...");
// 绑定 service
Intent httpIntent = new Intent(getActivity(), HttpService.class);
getActivity().bindService(httpIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection serviceConnection = new ServiceConnection() {
/**
* 与 HttpService 绑定成功后调用
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
httpService = ((HttpService.MyBinder) (service)).getService();
if(httpService != null){
// 调用获取天气的请求
httpService.getWeather();
// 天气接口回调
httpService.setWeatherResponseListener(jsonObject -> initWeather(jsonObject));
}
}
/**
* 解绑成功后调用
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
httpService = null;
}
};
我的 fragment 完整代码:
public class HomeFragment extends Fragment {
// 日志标签
public final String TAG = "HomeFragment";
// 发送 http 请求的 service
private HttpService httpService;
// 天气信息
private Weather weather;
// 用于展示天气信息的组件
private TextView cityText;
private TextView dateText;
private TextView weatherText;
private TextView tempText;
// 用于展示笔记信息的组件
private ListView noteListView;
public Handler handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what){
case HandlerConstant.UPDATE_WEATHER:
updateWeather();
getActivity().unbindService(serviceConnection);
break;
default:
}
}
};
/**
* 构造函数
*/
public HomeFragment() {
// Required empty public constructor
}
/**
* 当 Activity 完成onCreate() 时调用。
* @param savedInstanceState
*/
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initView();
initDatabase();
initClickListener();
}
/**
* 初始化视图组件
*/
private void initView(){
/*
* 天气相关
*/
cityText = getView().findViewById(R.id.city);
dateText = getView().findViewById(R.id.datetime);
weatherText = getView().findViewById(R.id.weather);
tempText = getView().findViewById(R.id.temp);
}
/**
* 初始化点击事件
*/
private void initClickListener(){
}
/**
* 创建时的回调方法
* @param inflater
* @param container
* @param savedInstanceState
* @return
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// LayoutInflater lf = getActivity().getLayoutInflater();
// View view = lf.inflate(R.layout.fragment_home, container, false);
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_home, container, false);
}
/**
* fragment 可见时调用
*/
@Override
public void onStart() {
super.onStart();
}
/**
* 恢复 fragment 时调用
*/
@Override
public void onResume() {
super.onResume();
queryWeather();
}
/**
* 查询天气
*/
private void queryWeather(){
Log.d(TAG, "开始查询天气信息...");
Intent httpIntent = new Intent(getActivity(), HttpService.class);
getActivity().bindService(httpIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection serviceConnection = new ServiceConnection() {
/**
* 与 HttpService 绑定成功后调用
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
httpService = ((HttpService.MyBinder) (service)).getService();
if(httpService != null){
// 调用获取天气的请求
httpService.getWeather();
// 天气接口回调
httpService.setWeatherResponseListener(jsonObject -> initWeather(jsonObject));
}
}
/**
* 解绑成功后调用
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
httpService = null;
}
};
/**
* 初始化 weather
* @param jsonObject 响应数据
* @throws JSONException 抛出 JSON 异常
*/
private void initWeather(JSONObject jsonObject) throws JSONException {
weather = new Weather();
JSONObject today = jsonObject.getJSONArray("casts").getJSONObject(0);
weather.setCity(jsonObject.getString("city"));
weather.setDate(jsonObject.getString("reporttime"));
weather.setDayWeather(today.getString("dayweather"));
weather.setNightWeather(today.getString("nightweather"));
weather.setDayTemp(today.getString("daytemp"));
weather.setNightTemp(today.getString("nighttemp"));
Message msg = new Message();
msg.what = HandlerConstant.UPDATE_WEATHER;
handler.sendMessage(msg);
}
@SuppressLint("SetTextI18n")
private void updateWeather(){
cityText.setText(weather.getCity());
dateText.setText(weather.getDate());
weatherText.setText(weather.getDayWeather()+" 转 "+ weather.getNightWeather());
tempText.setText(weather.getNightTemp()+" ~ "+weather.getDayTemp()+" 摄氏度");
}
}
总结
我这里对于网络请求的代码结构采用分层的方法。
总共有三层。
第一层为最原始的发送 http 请求的代码。
第二层为 service 中子线程对第一层接口的调用。
第三层为 activity(fragment) 中通过绑定 service 时 ServiceConnection 中的回调方法获取 service 实例;然后通过 service 中的自己定义的 ResponseListener 类型的回调函数获取 service 接收到数据后主动发送来的响应信息;最后通过 Handler 的进程通信在主线程中对数据进行展示等只能在主线程的操作。
标签:http,service,安卓,private,发送,weather,void,new,public From: https://www.cnblogs.com/huang-guosheng/p/17456659.html