0
点赞
收藏
分享

微信扫一扫

Android开发(7):使用网络技术

迎月兮 2022-01-20 阅读 40

个人笔记整理

使用网络技术

使用Http访问网络

使用HttpURLConnection

// new一个Url对象
URL url = new URL("http://www.baidu.com");
// new HttpURLConnection 新建连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 可设置请求方式 GET/POST
connection.setRequestMethod("GET");
// 设置连接超时毫秒数
connection.setConnectTimeout(8000);
// 设置读取超时毫秒数
connection.setReadTimeout(8000);

// 如果为POST,获取输出流
OutputStream os = connection.getOutputStream();
// 传入post参数
os.write("username=张三&age=20".getBytes());

// 获得输入流
InputStream in = connection.getInputStream();

// 进行字节流读取相关操作

// 关闭流
in.close();
os.close();
// 关闭连接
connection.disconnect();

使用OkHttp

导入依赖

编辑app/build.gradle,添加依赖

dependencies {
    compile 'com.squareup.okhttp3:okhttp:3.10.0'
}
使用
// 创建一个OkHttpClient的实例
OkHttpClient client = new OkHttpClient();
// 创建一个(空)Request 对象:
Request request = new Request.Builder().build();
// 或者在Builder中设置url
Request request = new Request.Builder()
        .url("http://www.baidu.com")
        .build();

// 之后调用OkHttpClient的newCall() 方法来创建一个Call 对象,并调用它的execute() 方法来发送请求并获取服务器返回的数据
Response response = client.newCall(request).execute();

// 得到Response返回的具体内容
String responseData = response.body().string();

// 如果为POST 我们需要先构建出一个Request Body对象来存放待提交的参数
RequestBody requestBody = new FormBody.Builder()
        .add("username", "admin")
        .add("password", "123456")
        .build();
// Request.Builder中调用一下post() 方法,并将RequestBody 对象传入
Request request = new Request.Builder()
        .url("http://www.baidu.com")
        .post(requestBody)
        .build();
// 调用execute() 方法来发送请求并获取服务器返回的数据即可。
get同步请求
// 创建OkHttpClient对象
OkHttpClient client = new OkHttpClient.Builder()
    .cache() // 设置缓存目录(有默认)
    .connectTimeout()	// 设置连接超时(有默认)
    .cookieJar()	// cookie的读取,保存(需自己实现)
    .build();
// 创建Request对象
Request request = new Request.Builder()
    .url(address)
    .build();
// 发送请求,获取响应
Call call = client.newCall(request);
// 发送同步请求,需手动创建子线程
new Thread(){
    public void run(){
        Response response = call.execute();//等待服务端进行响应(等待过程中会阻塞)
        // 判断响应状态是否正常
        if(response.isSuccessful()){
            // 获取服务端响应的内容(文本)
            String str = response.body().string();
        }
    }
}
get异步请求
//发送异步方式的get请求
private void getAsync(){
    //2.创建请求对象
    Request request = new Request.Builder()
            .url(Constant.BASE_URL +
                    "LoginServlet?username=张三&password=123")
            .build();
    //3.发送请求,获取响应
    Call call = okHttpClient.newCall(request);
    //异步请求,不需要手动创建子线程
    call.enqueue(new Callback(){
        @Override
        public void onFailure(Call call, IOException e) {
            //请求失败时执行
            e.printStackTrace();
        }
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            //请求成功时执行
            Log.e("onResponse",Thread.currentThread().getName());
            Log.e("异步get请求的结果",response.body().string());
            //执行在子线程中,不能直接修改用户界面
            //可以借助Handler实现

            //在实现文件下载功能时,如何获取网络文件的数据
//                InputStream is = response.body().byteStream();
//                FileOutputStream fos = new FileOutputStream(
//                        getExternalFilesDir(null).getAbsolutePath() + "/a.jpg");
        }
    });
    Log.e("okhttp","异步请求已发送");
}

异步post:发送文本

MIME 参考手册 (w3school.com.cn)


//异步方式的post请求,发送文本数据
private void postAsync(){
    //2.创建请求体对象
    RequestBody requestBody = RequestBody.create(
            MediaType.parse("text/plain;charset=utf-8"),	// 根据上传类型选择相应MIME
            "username=张三&password=123");
    //3.创建请求对象
    Request request = new Request.Builder()
            .url(Constant.BASE_URL + "LoginServlet")
            .post(requestBody)//设置请求方式为POST
            .build();
    //4.发送请求,获取响应
    Call call = okHttpClient.newCall(request);
    //异步请求,不需要手动创建子线程
    call.enqueue(new Callback(){
        @Override
        public void onFailure(Call call, IOException e) {
            //请求失败时执行
            e.printStackTrace();
        }
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            //请求成功时执行
            Log.e("onResponse",Thread.currentThread().getName());
            Log.e("异步get请求的结果",response.body().string());
            //执行在子线程中,不能直接修改用户界面
            //可以借助Handler实现
        }
    });
}
异步post:发送表单
//2.创建请求体对象
FormBody formBody = new FormBody.Builder()
        .add("username","张三")
        .add("password","123")
        .build();

//3.创建请求对象
Request request = new Request.Builder()
        .url(Constant.BASE_URL + "LoginServlet")
        .post(formBody)//设置请求方式为POST
        .build();
post本地选择文件上传
//文件上传
private void uploadFile(){
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_PICK);
    intent.setType("image/*");
    startActivityForResult(intent, 1);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == 1 && resultCode == RESULT_OK){
        ContentResolver contentResolver = getContentResolver();
        Cursor cursor = contentResolver.query(data.getData(),
                null,null,null,null);
        if (cursor.moveToFirst()){
            String imagePath = cursor.getString(cursor.getColumnIndex("_data"));
            //创建请求体对象
            File file = new File(imagePath);
            RequestBody requestBody = RequestBody.create(
                    MediaType.parse("application/octet-stream"),
                    file);
            //创建请求对象
            Request request = new Request.Builder()
                    .url(Constant.BASE_URL + "UploadServlet")
                    .post(requestBody)
                    .build();
            Call call = okHttpClient.newCall(request);
            call.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    e.printStackTrace();
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    Log.e("上传文件的结果",response.body().string());
                }
            });
        }
    }
}
post下载远端
@Override
public void onResponse(Call call, Response r) throws IOException{
    InputStream is = response.body().byteStream();
    File file = new File(Environment.getExternalStorageDirectory(), 
                        "test.txt");
    FileOutputStream fos = new FileOutputStream(file);
    int len = 0;
    byte[] buf = new byte[128];
    while ((len = is.read(buf)) != -1){
        fos.write(buf, 0, len);
    }
}

解析XML格式数据

Pull解析方式

解析的xml

<apps>
    <app>
        <id>1</id>
        <name>Google Maps</name>
        <version>1.0</version>
    </app>
    <app>

        <id>2</id>
        <name>Chrome</name>
        <version>2.1</version>
    </app>
    <app>
        <id>3</id>
        <name>Google Play</name>
        <version>2.3</version>
    </app>
</apps>

java代码

private void parseXMLWithPull(String xmlData) {
    try {
        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
        // 创建xmlPull解析器
        // XmlPullParser parser = Xml.newPullParser();
        XmlPullParser xmlPullParser = factory.newPullParser();
        // 初始化解析器
        xmlPullParser.setInput(new StringReader(xmlData));
        // 当前读取的事件类型
        int eventType = xmlPullParser.getEventType();
        String id = "";
        String name = "";
        String version = "";
        // 如果没有读取到文档的末尾
        while (eventType != XmlPullParser.END_DOCUMENT) {
            // 获得当前节点的名称
            String nodeName = xmlPullParser.getName();
            switch (eventType) {
                // 开始标签
                case XmlPullParser.START_TAG: {
                    if ("id".equals(nodeName)) {
                        id = xmlPullParser.nextText();
                    } else if ("name".equals(nodeName)) {
                        name = xmlPullParser.nextText();
                    } else if ("version".equals(nodeName)) {
                        version = xmlPullParser.nextText();
                    }
                    break;
                }
                // 结束标签
                case XmlPullParser.END_TAG: {
                    if ("app".equals(nodeName)) {
                        Log.d("MainActivity", "id is " + id);
                        Log.d("MainActivity", "name is " + name);
                        Log.d("MainActivity", "version is " + version);
                    }
                    break;
                }
                default:
                    break;
            }
            eventType = xmlPullParser.next();

        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
  1. 首先要获取到一个XmlPullParserFactory 的实例
  2. 借助这个实例得到XmlPullParser 对象
  3. 然后调用XmlPullParser 的setInput() 方法将服务器返回的XML数据设置进去就可以开始解析了。
  4. 解析的过程也非常简单,通过getEventType() 可以得到当前的解析事件
  5. 然后在一个while循环中不断地进行解析如果当前的解析事件不等于XmlPullParser.END_DOCUMENT,说明解析工作还没完成,调用next() 方法后可以获取下一个解析事件。
  6. 在while 循环中,我们通过getName() 方法得到当前节点的名字,如果发现节点名等于id、name或version,就调用nextText() 方法来获取节点内具体的内容,每当解析完一个app节点后就将获取到的内容打印出来。

SAX解析方式

模板

public class MyHandler extends DefaultHandler {
    @Override
    public void startDocument() throws SAXException {
        // startDocument() 方法会在开始XML解析的时候调用
    }
    @Override
    public void startElement(String uri, String localName, String qName, Attributes
        attributes) throws SAXException {
        // startElement() 方法会在开始解析某个节点的时候调用
    }
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        // characters() 方法会在获取节点中内容的时候调用
    }
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        // endElement() 方法会在完成解析某个节点的时候调用
    }

    @Override
    public void endDocument() throws SAXException {
        // endDocument() 方法会在完成整个XML解析的时候调用
    }
}

解析上述XML

public class ContentHandler extends DefaultHandler {
    private String nodeName;
    private StringBuilder id;
    private StringBuilder name;
    private StringBuilder version;
    @Override
    public void startDocument() throws SAXException {
        id = new StringBuilder();
        name = new StringBuilder();

        version = new StringBuilder();
    }
    @Override
    public void startElement(String uri, String localName, String qName, Attributes
        attributes) throws SAXException {
        // 记录当前节点名
        nodeName = localName;
    }
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        // 根据当前的节点名判断将内容添加到哪一个StringBuilder对象中
        if ("id".equals(nodeName)) {
            id.append(ch, start, length);
        } else if ("name".equals(nodeName)) {
            name.append(ch, start, length);
        } else if ("version".equals(nodeName)) {
            version.append(ch, start, length);
        }
    }
    @Override

    public void endElement(String uri, String localName, String qName) throws
        SAXException {
        if ("app".equals(localName)) {
            Log.d("ContentHandler", "id is " + id.toString().trim());
            Log.d("ContentHandler", "name is " + name.toString().trim());
            Log.d("ContentHandler", "version is " + version.toString().trim());
            // 最后要将StringBuilder清空掉
            id.setLength(0);
            name.setLength(0);
            version.setLength(0);
        }
    }
    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
    }
}

调用

private void parseXMLWithSAX(String xmlData) {
    try {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        XMLReader xmlReader = factory.newSAXParser().getXMLReader();
        ContentHandler handler = new ContentHandler();
        // 将ContentHandler的实例设置到XMLReader中
        xmlReader.setContentHandler(handler);
        // 开始执行解析
        xmlReader.parse(new InputSource(new StringReader(xmlData)));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

解析JSON格式数据

测试Json数据

[{"id":"5","version":"5.5","name":"Clash of Clans"},
{"id":"6","version":"7.0","name":"Boom Beach"},
{"id":"7","version":"3.5","name":"Clash Royale"}]

使用JSONObject

转成Json字符串
// JSONObject转json字符串
JSONObject jsonObject = new JSONObject();
jsonObject.put(String name,Object value);
jsonObject.put(String name,Object value);

String jsonStr = jsonObject.toString();

// 若是数组,写个循环生成JSONObject,然后用JSONArray对象的put方法
读取JSON字符串
private void parseJSONWithJSONObject(String jsonData) {
    try {
        // 读入JSON数组字符串 新建JSONArray对象
        JSONArray jsonArray = new JSONArray(jsonData);
        for (int i = 0; i < jsonArray.length(); i++) {
            // 得到json对象
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            // 获得对应数据
            String id = jsonObject.getString("id");
            String name = jsonObject.getString("name");
            String version = jsonObject.getString("version");
            Log.d("MainActivity", "id is " + id);
            Log.d("MainActivity", "name is " + name);
            Log.d("MainActivity", "version is " + version);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

使用Gson

添加依赖

编辑app/build.gradle文件,在dependencies闭包中添加

compile 'com.google.code.gson:gson:2.8.5'
使用
Builder设置Gson属性
Gson gson = new GsonBuilder()
    .serializeNulls()	// 允许导出null
    .setPrettyPrinting()	// 格式化s
    .setDateFormat("yyyy-MM-dd")	// 设置日期输出格式
    .create();	// 生成配置好的Gson对象
对象转json字符串
Gson gson = new Gson();
String jsonStr = gson.toJson(Object);
字符串直接解析对象
jsonData = """{"name":"Tom","age":20}"""

Gson gson = new Gson();
Person person = gson.fromJson(jsonData, Person.class);
Json字符串为对象数组
List<Person> people = gson.fromJson(jsonData, new TypeToken<List<Person>>() {}.getType());

网络编程实践

定义一个Http工具类

public class HttpUtil {
    // 发送Http GET请求
    public static String sendHttpRequest(String address) {
        HttpURLConnection connection = null;
        try {
            URL url = new URL(address);

            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(8000);
            connection.setReadTimeout(8000);
            connection.setDoInput(true);
            connection.setDoOutput(true);
            InputStream in = connection.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            return response.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }
    

    /**
     * 封装好的post请求
     *
     * @param uri        url
     * @param parameters 请求参数
     * @return post请求
     */
    public static String sendPost(String uri, Map<String, String> parameters) {
        StringBuffer postUrlSB = new StringBuffer(serverUrl + uri);
        if (parameters != null) {
            postUrlSB.append("?");
            Set<Map.Entry<String, String>> entries = parameters.entrySet();
            int count = 1;
            int size = parameters.size();
            for (Map.Entry<String, String> entry : entries) {
                String key = entry.getKey();
                String value = entry.getValue();
                if (count++ < size) {
                    postUrlSB.append(key).append("=").append(value).append("&");
                } else {
                    postUrlSB.append(key).append("=").append(value);
                }
            }
        }
        String postUrl = postUrlSB.toString();
        URL url = null;
        HttpURLConnection connection = null;
        String meg = null;
        try {
            url = new URL(postUrl);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-type", "text/plain;charset=UTF-8");
            InputStream is = connection.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            meg = br.readLine();
            br.close();
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return meg;
    }

}

开启子线程来接收返回值

Java回调机制

1. 定义一个HttpCallbackListener接口
public interface HttpCallbackListener {
    void onFinish(String response);

    void onError(Exception e);
}
  • onFinish() 方法表示当服务器成功响应我们请求的时候调用:onFinish() 方法中的参数代表着服务器返回的数据。
  • onError() 表示当进行网络操作出现错误的时候调用:onError() 方法中的参数记录着错误的详细信息。
2.修改原HttpUtil类
public class HttpUtil {
    public static void sendHttpRequest(final String address, final
        HttpCallbackListener listener) {
        new Thread(new Runnable() {
            @Override

            public void run() {
                HttpURLConnection connection = null;
                try {
                    URL url = new URL(address);
                    connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("GET");
                    connection.setConnectTimeout(8000);
                    connection.setReadTimeout(8000);
                    connection.setDoInput(true);
                    connection.setDoOutput(true);
                    InputStream in = connection.getInputStream();
                    BufferedReader reader = new BufferedReader(new InputStreamReader
                       (in));
                    StringBuilder response = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        response.append(line);
                    }
                    if (listener != null) {
                        // 回调onFinish()方法
                        listener.onFinish(response.toString());
                    }
                } catch (Exception e) {

                    if (listener != null) {
                        // 回调onError()方法
                        listener.onError(e);
                    }
                } finally {
                    if (connection != null) {
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }
}

我们首先给sendHttpRequest() 方法添加了一个HttpCallbackListener 参数

并在方法的内部开启了一个子线程,然后在子线程里去执行具体的网络操作。

子线程中是无法通过return语句来返回数据的,因此这里我们将服务器响应的数据传入了HttpCallbackListener的onFinish() 方法中,如果出现了异常就将异常原因传入到onError() 方法中。
现在sendHttpRequest() 方法接收两个参数了,因此我们在调用它的时候还需要将HttpCallbackListener的实例传入

HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {
    @Override
    public void onFinish(String response) {
        // 在这里根据返回内容执行具体的逻辑
    }
    @Override
    public void onError(Exception e) {
        // 在这里对异常情况进行处理
    }
});

使用OkHttp

使用

public class HttpUtil {
    ...
    public static void sendOkHttpRequest(String address, okhttp3.Callback callback) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(address)
                .build();
        client.newCall(request).enqueue(callback);
    }
}

okhttp3.Callback :是OkHttp库中自带的一个回调接口

HttpUtil.sendOkHttpRequest("http://www.baidu.com", new okhttp3.Callback() {
@Override
    public void onResponse(Call call, Response response) throws IOException {
        // 得到服务器返回的具体内容
        String responseData = response.body().string();
    }
    @Override
    public void onFailure(Call call, IOException e) {
        // 在这里对异常情况进行处理
    }
});
举报

相关推荐

0 条评论