第一行代码网络技术笔记
1.WebView用法
(1)在注册文件里声明网络权限
<uses-permission android:name="android.permission.INTERNET"/>
(2)在布局中定义WebView控件
<WebView
android:id="@+id/webview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></WebView>
(3)在MainActivity中
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView webView=(WebView)findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
//getSettings()可以设置一些浏览器属性
webView.setWebViewClient(new WebViewClient());
//调用setWebViewClient方法,里面传入实例
//希望当一个网页跳到另一个网页时,目标网页仍然在当前WebView中显示。而不是打开系统浏览器
webView.loadUrl("http://www.baidu.com");//传入网址
}
2.HTTP协议访问网络
I. GET&&POST方法
get是从服务器获取数据,几次get没问题。get方法会把请求字段显示在地址栏,不安全。
post向服务器传递数据,执行多次post会向服务器增加n条相同数据。post比较安全。
II.使用HttpUrlConnection
先来一个界面
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/getbutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/responseText"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</ScrollView>
</LinearLayout>
ScrollView控件可以让页面滚动形式出现,内容多的部分在下面。这里按钮用来发送请求,用TextView接收。
然后处理分二步:构建HttpURLConnection,传入一个URL的openConnection()打开连接,setRequestMethod选择请求方法,有POST和GET供选择。最后要记得disConnection()。
输入流进行处理显示
public class MainActivity extends AppCompatActivity {
TextView responseText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button getButton=(Button)findViewById(R.id.getbutton);
responseText=(TextView)findViewById(R.id.responseText);
getButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendRequestWithHttpURLConnection();
}
});
}
private void sendRequestWithHttpURLConnection(){
//开启线程发起网络请求
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection httpURLConnection=null;
BufferedReader reader=null;
try {
URL url=new URL("https://www.baidu.com");
httpURLConnection=(HttpURLConnection)url.openConnection();
httpURLConnection.setRequestMethod("GET");
httpURLConnection.setConnectTimeout(8000);
httpURLConnection.setReadTimeout(8000);
//上传数据这样写
//httpURLConnection.setRequestMethod("POST");
//DataOutputStream out=new DataOutputStream(httpURLConnection.getOutputStream());
//out.writeBytes("username=admin&password=123456");
InputStream in=httpURLConnection.getInputStream();
//对获取的输入流进行处理
reader=new BufferedReader(new InputStreamReader(in));
StringBuffer response=new StringBuffer();
String line;
while ((line=reader.readLine())!=null){
response.append(line);
}
showResponse(response.toString());
}catch (Exception e){
e.printStackTrace();
}finally {
if(reader!=null){
try{
reader.close();
}catch (IOException e){
e.printStackTrace();
}
}
if(httpURLConnection!=null){
httpURLConnection.disconnect();
}
}
}
}).start();
}
private void showResponse(final String response){
runOnUiThread(new Runnable() {
@Override
public void run() {
//在这里进行UI操作,显示到界面上,因为子线程不能进行UI操作,所以通过这个函数转移到主线程
responseText.setText(response);
}
});
}
}
然后在注册文件中添加网络权限
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.networktest">
<uses-permission android:name="android.permission.INTERNET"/>
III.OkHttp使用
主页地址 https://github.com/square/okhttp
使用步骤
(1)引入依赖
implementation 'com.squareup.okhttp3:okhttp:4.0.1'
(2)AndroidManifest加入网络许可权限
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
(3)使用GET方法
private void sendRequestWithOkHttp(){
new Thread(new Runnable() {
@Override
public void run() {
try{
OkHttpClient client = new OkHttpClient();
Request request=new Request.Builder().url("https://www.baidu.com").build();
Response response=client.newCall(request).execute();
String responseData=response.body().string();
showResponse(responseData);
}catch (Exception e){
e.printStackTrace();
}
}
}).start();
}
首先构建一个OkHttpClient实例
OkHttpClient client = new OkHttpClient();
然后如果想要发起HTTP请求,就创建一个Request对象,在build前加一系列东西
Request request=new Request.Builder().url(“https://www.baidu.com”).build();
之后调用 OkHttpClient的newCall()方法创建一个Call对象 ,并调用它的execute()方法发送请求和获取服务器返回的数据
Response response=client.newCall(request).execute();
Response对象就是服务器返回的数据了,然后显示出来
String responseData=response.body().string();
(4)使用POST方法
与GET大部分相同,不同的是要构建一个RequestBody对象来存放等待提交的参数
RequestBody requestBody=new FormBody.Builder().add("username","yuer").build();
然后Request里添加post()方法将构建好的传进去
// Request request=new Request.Builder().url("https://easy-mock.com/mock/5d2dcaef934181050dd4ae60/hhh/hhh").post(requestBody).build();
(5)还可以封装到一个类的方法里,调用方便
方法这样写
public static void sendOkHttpRequest(String address,okhttp3.Callback callback) {
OkHttpClient okHttpClient=new OkHttpClient();
Request request=new Request.Builder().url(address).build();
okHttpClient.newCall(request).enqueue(callback);
}
//回掉时候
HttpUtil.sendOkHttpRequest("http://www.baidu.com",new okhttp3.Callback(){
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
//得到服务器返回的内容
String responseData=response.body().string();
}
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
//对异常进行处理
}
});
}
3.网络解析
I.XML格式解析
(1)Pull解析方式
首先我本地模拟XML数据,不进行网络获取
String xmlData= "<person> \n" +
" <name>张三</name> \n" +
" <age>23</age> \n" +
"</person>";
parseXMLWithPull(xmlData);
接下来进行解析
public void parseXMLWithPull(String xmlData){
try{
XmlPullParserFactory xmlPullParserFactory=XmlPullParserFactory.newInstance();
XmlPullParser xmlPullParser=xmlPullParserFactory.newPullParser();
xmlPullParser.setInput(new StringReader(xmlData));
int eventType=xmlPullParser.getEventType();
String name="";
String age="";
while(eventType!=XmlPullParser.END_DOCUMENT){
String nodeName=xmlPullParser.getName();
switch (eventType){
//开始解析某个结点
case XmlPullParser.START_TAG:{
if("name".equals(nodeName)){
name=xmlPullParser.nextText();
}else if("age".equals(nodeName)){
age=xmlPullParser.nextText();
}
}break;
//完成解析某个结点
case XmlPullParser.END_TAG:{
if("person".equals(nodeName)){
Log.d("MainActivity","name is"+name);
Log.d("MainActivity","age is"+age);
}
break;
}
default:break;
}
eventType=xmlPullParser.next();
}
}catch (Exception e){
e.printStackTrace();
}
}
(1)XmlPullParserFactory.newInstance ()方法其实是返回一个XmlPullParserFactory形式的实例,每次调用构造方法把实例构造出来,那平常的构造法能不能使用呢 XmlPullParserFactory xmlPullParserFactory=new XmlPullParserFactory();我发现报错了,因为构造器是保护类型,protected的作用区域是包内,挎包不能用,这是一种安全措施,但我们还要用这个构造方法咋办呢,一般都像这样,定义一个公开的方法,在里面返回实例。
/**
* Creates a new instance of a PullParserFactory that can be used
* to create XML pull parsers. The factory will always return instances
* of Android's built-in {@link XmlPullParser} and {@link XmlSerializer}.
*/
public static XmlPullParserFactory newInstance () throws XmlPullParserException {
return new XmlPullParserFactory();
}
/**
* Protected constructor to be called by factory implementations.
*/
protected XmlPullParserFactory()
(2) XmlPullParser定义的是一个接口,不是具体实现,所以构建实例的时候采用xmlPullParserFactory.newPullParser();形式
(3)接下来调用.setInput进行数据装填,xmlPullParser.getEventType();获取当前解析事件,然后在while中不断进行解析,如果当前事件不等于XmlPullParser.END_DOCUMENT,说明解析工作还没完成,调用eventType=xmlPullParser.next();方法继续解析下一个结点。
(2)SAX解析方式
SAX解析对比Pull解析要复杂一点,但是代码可读性比较强
首先新定义一个类继承自DefaultHandler,并重写其中五个方法
ublic class MyHandler extends DefaultHandler {
//开始解析时候调用
@Override
public void startDocument() throws SAXException {
super.startDocument();
}
//开始解析某个结点的时候调用 被解析数据以参数形式到下面三个方法中
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
}
//获取结点内容时候调用,可能会被调用两次,换行符会调用一次,要做好处理
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
}
//结束解析某个结点时候调用
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
}
//结束解析时候调用
@Override
public void endDocument() throws SAXException {
super.endDocument();
}
}
按照这个思路,我们实现以下上面pull解析方式解析的内容,先重写这几个方法
public class MyHandler extends DefaultHandler {
String nodeName;
StringBuilder name;
StringBuilder age;
//开始解析时候调用
@Override
public void startDocument() throws SAXException {
//完成变量初始空间分配
name=new StringBuilder();
age=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 {
//根据当前结点名判断将内容添加到哪一个StringBuffer中
if("name".equals(nodeName)){
name.append(ch, start, length);
}
else if("age".equals(nodeName)){
age.append(ch, start, length);
}
}
//结束解析某个结点时候调用
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if("person".equals(nodeName)){
Log.d("MyHandler","name is "+name.toString().trim());
Log.d("MyHandler","age is "+age.toString().trim());
//最后要将StringBuilder清除掉
name.setLength(0);
age.setLength(0);
}
}
//结束解析时候调用
@Override
public void endDocument() throws SAXException {
super.endDocument();
}
}
接下来就在主程序中调用这个方法
public void parseXMLWithSAX(String xmlData){
try{
SAXParserFactory saxParserFactory=SAXParserFactory.newInstance();
XMLReader xmlReader=saxParserFactory.newSAXParser().getXMLReader();
MyHandler handler=new MyHandler();
//将MyHandler的实例传送到XMLReader中
xmlReader.setContentHandler(handler);
//开始执行解析
xmlReader.parse(new InputSource(new StringReader(xmlData)));
}catch(Exception e){
e.printStackTrace();
}
}
(3)DOM解析方式
II.JSON格式解析
json格式数据对比SAX简洁,但语义不够多,现在的主流。
(1)用JSONObject 解析json
public void parseJSONWithOkHttp(String jsonData){
try{
JSONArray jsonArray=new JSONArray(jsonData);
for(int i=0;i<jsonArray.length();i++){
JSONObject jsonObject=jsonArray.getJSONObject(i);
String name=jsonObject.getString("name");
String age=jsonObject.getString("age");
Log.d("MainActivity","name is "+name);
Log.d("MainActivity","age is "+age);
}
}catch (Exception e){
e.printStackTrace();
}
}
(2)GSON使用
谷歌开发,原理就是将json格式的字符串自动映射为对象,不用手动编写代码解析
它在这里 https://github.com/google/gson
使用方法:
(1)可以查找对应版本引进依赖,在app那个gradle里加上
implementation ‘com.google.code.gson:gson:2.8.5’
(2)预定义一个要解析数据的类,加入解析字段
public class Person {
private String name;
private int age;
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age=age;
}
}
(3)如果要解析的json数据是一段数组要麻烦一些,不是的话可以简单调用下面字段就可以之间变成对象
Gson gson=new Gson();
Person person=gson.fromJson(jsonData,Person.class);
对于数据要借助TypeToken将期望解析的数据类型传入到fromJson中
List
public void parseJsonWithGson(String jsonData){
Gson gson=new Gson();
List<Person> peopleList=gson.fromJson(jsonData,new TypeToken<List<Person>>(){}.getType());
for(Person person:peopleList){
Log.d("MainActivity","name is "+person.getName());
Log.d("MainActivity","age is "+person.getAge());
}
}
此外还有toJson,可以把数据转换成json格式。
相似功能的还有Fastjson,阿里巴巴的,速度比较快,但对比GSON有一些漏洞。