关于Android网络通信编程
Android对HTTP通信提供了支持 通过标准的JAVA类HttpURLConnection便可以实现基于URL的请求及响应功能
关于URL和URI还分不清吗
然后还有就是GET和POST方式提交数据
注意 使用GET或者POST方式提交参数时 为了防止中文乱码 要对参数进行编码
使用WebView控件进行网络开发
 //Android内置浏览器 使用开源的webkit引擎
来看两个例子:
使用WebView控件浏览网页
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <WebView
        android:id="@+id/webView_page"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>
package com.example.four_content;
import android.os.Bundle;
import android.webkit.WebView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
public class WebViewPage extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview_page);
        //Android内置浏览器 使用开源的webkit引擎
        WebView webView= findViewById(R.id.webView_page);
        //指定要加载的网页(小红书)
        webView.loadUrl("https://www.xiaohongshu.com/explore");
        //WebView控件具备放大和缩小网页的功能
        webView.getSettings().setSupportZoom(true);
        webView.getSettings().setBuiltInZoomControls(true);
    }
}
注意注意 要在清单文件中添加允许访问网络资源的权限
<uses-permission android:name="android.permission.INTERNET"/>
效果如图:

使用WebView控件执行HTML和JS代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <WebView
        android:id="@+id/webView_html"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <Button
        android:id="@+id/btn_dialog"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="执行JAVASCRIPT代码并弹出提示框"
        android:layout_margin="8dp"
        android:textColor="@android:color/white"
        android:background="@drawable/btn_dialog_selector"/>
    <WebView
        android:id="@+id/webView_js"
        android:layout_width="300dp"
        android:layout_height="500dp" />
</LinearLayout>
需要注意的点就是:
js代码使用时默认不支持 需要使用方法来设置
package com.example.four_content;
import android.os.Bundle;
import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.widget.Button;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
public class WebView_html_js extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview_html_js);
        //获取布局控件
        WebView webView_html = findViewById(R.id.webView_html);
        WebView webView_js = findViewById(R.id.webView_js);
        //WebView控件执行html
        // 创建字符串构建器
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("<h2>高地远近</h2>");
        stringBuilder.append("<ul>");
        stringBuilder.append("<li>啊啊</li>");
        stringBuilder.append("<li>哈哈</li>");
        stringBuilder.append("<li>哇哇</li>");
        stringBuilder.append("</ul>");
        //loadDataWithBaseURL方法对比loadData()方法 加载中文的html代码不会乱码
        // 五个参数
        // 1.指定页面使用的url null默认空白页 2.显示的字符串数据 3.显示内容类型 4.数据编码方式 5.进入页面显示的url null默认空白页
        webView_html.loadDataWithBaseURL(null,stringBuilder.toString(),"text/html","utf-8",null);
        //WebView控件执行js代码
        Button button = findViewById(R.id.btn_dialog);
        //指定要加载的网页
        webView_js.loadUrl("file:///android_asset/alert.html");
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //获取WebView控件的WebSettings对象 调用setJavaScriptEnabled()方法使支持js代码
                webView_js.getSettings().setJavaScriptEnabled(true);
                //对于js代码的alert()提示框不加载 需要使用setWebChromeClient()方法实现
                //setWebChromeClient主要处理解析,渲染网页等浏览器做的事情。
                //WebChromeClient是辅助WebView处理JavaScript的对话框,网页图标,图标title,加载进度等
                webView_js.setWebChromeClient(new WebChromeClient());
                webView_js.loadUrl("file:///android_asset/alert.html");
            }
        });
    }
}
JSON数据解析
JSON数据主要有两种结构:
- 对象结构
- 数组结构
 例如:在进行java开发时候 用postman接口调试 传递JSON数据
传的值就是key:value形式
顺便介绍个工具:有很对好用的功能 譬如 JSON数据转JavaBean等等
https://he3.app/zh/
言归正传:
JSON解析
Android SDK提供了两种方式解析
- JSONObject类和JSONArray类解析
 具体不写了 就是JSONObject类将数据获取 然后JSONArray类输出
 类似之前的隐式显式传递 Bundle方法
- 重点使用Gson库解析JSON数据
 可通过fromJson()方法解析JSON数据
 注意: 使用前必须创建JSON数据对应的实体类
 方法对应两种:
1. 对象结构:
        Gson gson = new Gson();
        对象 对象名= gson.fromJson(JSON数据,class);
2. 数组结构:
         Gson gson = new Gson();
        //解析数组结构的JSON数据
        Type listType = new TypeToken<List<实体类>>() {}.getType();
        List<实体类> 名字 = gson.fromJson(JSON数据, listType);
当然使用Gson库需要添加依赖 在此先不说 后面看一个综合案例来说
刨析一个案例之前先看一下Handle机制
详细介绍:参考 https://blog.csdn.net/ly0724ok/article/details/117324053
大致就是:
handler其实就是主线程在起了一个子线程,子线程运行并生成Message,Looper获取message并传递给Handler,Handler逐个获取子线程中的Message
来看本篇文章的一个最重要的案例
就是要实现一个仿pdd砍价界面
但是图片来自于网络传输的 也需要用到JSON数据 还有服务器
所以先得准备:
首先tomcat服务器:
将图片和JSON文件放在里面
为的就是通过网络访问到他们
关于tomcat运行啊 失败啊 接口占用之类的问题 javaweb的时候都学过了 不过多赘述了
只要你访问8080能够加载出来tomcat官网就行
注意文件放到webapps目录下
然后就是添加需要的依赖:
去这里搜索获取 注意版本问题即可
https://mvnrepository.com/artifact/com.google.code.gson/gson/2.8.5
(这里有坑)
我的使用kts语法
    implementation("com.google.code.gson:gson:2.8.5")
    implementation("com.squareup.okhttp3:okhttp:3.12.0")
    implementation("com.github.bumptech.glide:glide:4.16.0")
书中给的glide是3.7.0的 不支持了 因为我们的是AndroidX 所以用最新的才会支持
https://www.jianshu.com/p/d051c02a485c
但是呢我之前不知道将其添加到library里面 要去删除
准备完了前提 来看代码部分
Gson库对应必须的实体类(对应JSON数据)
package com.example.four_content;
public class Goods {
    private int id;
    private String count;
    private String goodsName;
    private String goodsPic;
    public void setId(int id) {
        this.id = id;
    }
    public int getId() {
        return id;
    }
    public void setCount(String count) {
        this.count = count;
    }
    public String getCount() {
        return count;
    }
    public void setGoodsName(String goodsName) {
        this.goodsName = goodsName;
    }
    public String getGoodsName() {
        return goodsName;
    }
    public void setGoodsPic(String goodsPic) {
        this.goodsPic = goodsPic;
    }
    public String getGoodsPic() {
        return goodsPic;
    }
}
数据适配器
package com.example.four_content;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import java.util.ArrayList;
import java.util.List;
public class PddAdapter extends RecyclerView.Adapter<PddAdapter.MyViewHolder> {
    private Context mcontext;
    private List<Goods> goods = new ArrayList<>();
    public PddAdapter(Context context) {
        this.mcontext = context;
    }
    //获取数据更新界面
    public void setData(List<Goods> goods) {
        this.goods = goods;
        //notifyDataSetChanged方法通过一个外部的方法控制如果适配器的内容改变时需要强制调用getView来刷新每个Item的内容
        // 可以实现动态的刷新列表的功能
        notifyDataSetChanged();
    }
    //      * onCreateViewHolder 加载界面的布局文件 inflate()方法
    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        /**
         *LayoutInflater 是 Android 开发中一个非常重要的类,它的主要作用是将 XML 布局文件转换成相应的视图(View)对象,以便在应用程序的 UI 中使用。
         * inflate 方法是 LayoutInflater 类中的一个核心方法,用于将 XML 布局文件转换为相应的视图对象。
         *  public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)三个参数:
         *  加载的布局文件的资源ID
         * 这是新创建的视图将要被附加到的父视图
         * 用于指定是否立即将新创建的视图附加到父视图上
         * */
        MyViewHolder holder = new MyViewHolder(LayoutInflater.from(mcontext).inflate(R.layout.goods_item, parent, false));
        return holder;
    }
    //      * onBindViewHolder 将获取的数据绑定到对应的控件上
    /**
     * @NonNull MyViewHolder holder: 这是一个非空的视图持有者对象
     * MyViewHolder 通常是开发者自定义的一个类,用于持有列表项中各个视图的引用,从而可以方便地对它们进行操作(如设置文本、图片等)
     * int position: 这是一个整数,表示当前绑定数据的列表项在 RecyclerView 中的位置
     */
    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        Goods been = goods.get(position);
        holder.tv_count.setText("已砍" + been.getCount() + "件");
        holder.tv_goods_name.setText(been.getGoodsName());
        //Glide 是一个快速高效的 Android 图片加载库,可以自动加载网络、本地文件,app 资源中的图片,注重于平滑的滚动
        /**
         * Glide.with(mcontext):
         * 这是 Glide 的一个静态方法,用于创建一个新的图片请求
         *.load(been.getGoodsPic()):
         * 这是指定要加载的图片的 URL、资源 ID 或文件路径的地方
         * error(R.mipmap.ic_launcher):
         * 如果图片加载失败(例如,因为网络错误或无效的 URL),Glide 会显示一个占位符或错误图片。
         * 在这里,如果图片加载失败,Glide 将显示 R.mipmap.ic_launcher,这通常是应用的默认图标
         * .into(holder.iv_img):
         * 这指定了 Glide 应该将图片加载并显示在哪个ImageView上
         * */
        Glide.with(mcontext)
                .load(been.getGoodsPic())
                .error(R.mipmap.ic_launcher)
                .into((holder).iv_img);
    }
    //      * getItemCount 获取列表条目的总数
    @Override
    public int getItemCount() {
        return goods.size();
    }
    //强制使用ViewHolder 使代码编写规范化 想当与listview中的优化
    class MyViewHolder extends RecyclerView.ViewHolder {
        TextView tv_count, tv_goods_name;
        ImageView iv_img;
        Button btn_free;
        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            tv_count = itemView.findViewById(R.id.tv_count);
            tv_goods_name = itemView.findViewById(R.id.tv_goods_name);
            iv_img = itemView.findViewById(R.id.iv_img);
            btn_free = itemView.findViewById(R.id.btn_free);
        }
    }
}
package com.example.four_content;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.List;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class PDDActivity extends AppCompatActivity {
    private static final int MSG_GOODS_OK = 1;
    //内网接口
    public static final String Web_SITE = "http://127.16.43.20:8080/goods";
    //商品列表接口
    public static final String REQUEST_GOODS_URL = "/goods_list_data.json";
    private MHandle mHandle;
    private PddAdapter pddAdapter;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pdd);
        /**
         * 1.获取列表框架
         * 2.创建一个适配器 可以用已有的 亦可以创建继承的自己的
         * 3.将适配器添加到列表框架中
         * */
        RecyclerView rv_list = findViewById(R.id.rv_list);
        pddAdapter = new PddAdapter(PDDActivity.this);
        rv_list.setAdapter(pddAdapter);
        //GridLayoutManager,这是一个RecyclerView的布局管理器,它可以将项目以网格的形式展示
        GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
        rv_list.setLayoutManager(gridLayoutManager);
        //Handler消息机制
        mHandle = new MHandle();
        initData();
    }
    //开启异步线程访问网络
    private void initData() {
        OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder().url(Web_SITE + REQUEST_GOODS_URL).build();
        Call call = okHttpClient.newCall(request);
        //消息队列 存储消息
        call.enqueue(new Callback() {
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String string = response.body().string();
                Message message = new Message();
                message.obj = string;
                message.what = MSG_GOODS_OK;
                //发送消息
                mHandle.sendMessage(message);
            }
            @Override
            public void onFailure(Call call, IOException e) {
            }
        });
    }
    //处理消息
    class MHandle extends Handler {
        @Override
        public void dispatchMessage(@NonNull Message msg) {
            super.dispatchMessage(msg);
            if (msg.what == MSG_GOODS_OK) {
                if (msg.obj != null) {
                    String result = (String) msg.obj;
                    List<Goods> goodsList = getGoodsList(result);
                    pddAdapter.setData(goodsList);
                }
            }
        }
    }
    //Gson库解析JSON数据
    public List<Goods> getGoodsList(String json) {
        Gson gson = new Gson();
        //解析数组结构的JSON数据
        Type listType = new TypeToken<List<Goods>>() {}.getType();
        List<Goods> goods = gson.fromJson(json, listType);
        return goods;
    }
}
上述跳过了layout布局文件
运行后:

为何不显示呢 因为是网络访问 需要将程序运行到第三方模拟器上
这里以夜神模拟器为例:
参考 https://blog.csdn.net/My_Deng_Peng/article/details/82153940
启动连接

内部找到

补充 我上面说错了 不是因为模拟器的原因 而是网络访问的问题 还有就是设置IP的问题:
当然上面就当学习第三方模拟器了:
https://zhuanlan.zhihu.com/p/112957829
参考 你会发现夜神模拟器不兼容 可以使用上述很详细介绍
咱主要来看看错误的原因 就是:IP地址需要是你本机的地址
参考 https://blog.csdn.net/weixin_44734310/article/details/108550307
这里使用Mumu模拟器
我们会发现还没加载出来
坑坑坑:
需要配置此项:
网络资源加载失败 http访问问题
参考:https://www.cnblogs.com/zhangwenju/p/14240765.html
终于终于:

别忘记开启tomcat服务
 
                     
                    
                 
                    
                 










 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号