文章目錄
Android 網絡技術
1. WebView的用法
在應用程序中加載和顯示網頁
xml文件,在佈局中添加一個WebView
<?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="match_parent">
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
MainActivity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView webView = (WebView)findViewById(R.id.web_view);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient());
webView.loadUrl("http://www.baidu.com");
}
}
1.首先用findViewById()方法獲取到WebView實例
2. 調用WebView的getSettings()方法可以設置一些瀏覽器屬性,這裏只是調用了setJavaScriptEnabled()方法來讓WebView支持JavaScript腳本
3. 調用setWebViewClient()方法,並傳入一個WebViewClient實例。這段代碼的作用是,當需要從一個網頁跳到另外一個網頁時,我們希望目標網頁仍在當前WebView顯示,而不是打開瀏覽器。
4. 接着調用loadUrl()方法,將網址傳入,即可展示相應的網頁內容
需要注意的是,要加入權限聲明。
<uses-permission android:name="android.permission.INTERNET"/>
2. 使用http協議訪問網絡
Android 9 http及https的網絡連接需要附加一些東西:參考How to allow all Network connection types HTTP and HTTPS in Android (9) Pie?
1. 使用HttpURLConnection
需要聲明網絡權限
1. 首先需要獲取到HttpURLConnection()對象,一般只需要new一個URL對象,並傳入目標網絡地址,然後調用一下openConnection()方法即可
URL url = new URL("https://www.baidu.com");
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
2. 得到了HttpURLConnection對象之後,我們可以設置一下請求方法,GET和POST。GET希望從服務器那裏獲取數據,而POST表示希望提交數據給服務器。
connection.setRequestMethod("GET");
3. 可以進行一些自由定製,連接超時,讀取超時的毫秒數等等
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
4. 獲取服務器返回的輸入流
InputStream in = connection.getInputStream();
5. 最後可以關閉HTTP連接
connection.disconnect();
示例
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="match_parent"
android:orientation="vertical">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/send_request"
android:text="Send Request"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/response_content"/>
</ScrollView>
</LinearLayout>
ScrollView:由於手機屏幕的空間一般較小,有些時候過多的內容一屏是顯示不下的,藉助ScrollView可以以滾動的形式查看屏幕外的內容。
MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
TextView responseText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button sendRequest = (Button)findViewById(R.id.send_request);
responseText = (TextView)findViewById(R.id.response_content);
sendRequest.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if(view.getId()==R.id.send_request){
sendRequestWithHttpURLConnection();
}
}
private void sendRequestWithHttpURLConnection(){
//開啓線程來發起網絡請求
new Thread(new Runnable(){
public void run(){
HttpURLConnection connection = null;
BufferedReaader reader = null;
try{
URL url = new URL("https://www.baidu.com");
connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
InputStream in = connection.getInputStream();
//下面對獲取到的輸入流進行讀取
reader = new BufferedReader(new InputStreamReader(in));
StringBuilder response = new StringBuilder();
String line;
while((line = reader.readLine)!=null){
response.append(line);
}
showResponse(response.toString);
}catch(Exception e){
e.printStackTrace();
}
}
}).start;
}
private void showResponse(final String response){
runOnUiThread(new Runnable() {
@Override
public void run() {
responseText.setText(response);
}
});
}
}
1. 在Send Request按鈕的點擊事件裏調用了sendRequestWithHttpURLConnection()方法
2.在此方法中開啓了一個子線程,調用HttpURLConnection()發出一條請求,目標地址是百度
3. 利用BufferedReader對服務器返回的流進行讀取,將結果傳入showResponse()方法中
4. 在showResponse()方法中調用了一個runOnUiThread()方法,因爲Android不允許在子線程中進行UI操作,通過這個方法將線程切換到主線程
提交數據給服務器
只需要將HTTP請求的方法改成POST,並在獲取輸入流之前把要提交的數據寫出即可。注意每條數據都要以鍵值對的形式存在,數據與數據之間用“&”符號隔開,比如我們要向服務器提交用戶名和密碼,如下:
connection.setRequestMethod("POST");
DataOutputStream out = new DataOutputStream(connection.getOutputStream);
out.writeBytes("username=admin&password=123456");
2. 使用OkHttp
在使用OkHttp之前需要在項目中添加依賴OkHttp庫的依賴
implementation 'com.squareup.okhttp3:okhttp:4.1.0'
1. 首先需要創建一個OkHttpClient的實例
OkHttpClient client = new OkHttpClient();
2. 要想發起一條HTTP請求,需要創建一個Request對象
Request request = new Request.Builder().build();
3. 上述代碼只是創建了一個空的Request對象,我們可以在build()之前通過連綴很多其他方法豐富這個Request對象
Request request = new Request.Builder()
.url("https://www.baidu.com")
.build;
4. 之後調用OkHttpClient的newCall()方法來創建一個Call對象,並調用它的execute()方法來發送請求並獲取服務器返回的數據
Response response = client.newCall(request).execute();
其中response就是服務器返回的數據
5. 通過如下方法獲得數據的具體內容
String responseData = response.body().string();
示例(xml文件不需要改動)
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
TextView responseText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button sendRequest = (Button)findViewById(R.id.send_request);
responseText = (TextView)findViewById(R.id.response_content);
sendRequest.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if(view.getId()==R.id.send_request){
sendRequestWithOkHttp();
}
}
private void sendRequestWithOkHttp() {
new Thread(){
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();
}
private void showResponse(final String response){
runOnUiThread(new Runnable() {
@Override
public void run() {
responseText.setText(response);
}
});
}
}
提交數據給服務器
首先構建出一個RequestBody對象來存放帶提交的數據
RequestBody requestBody= new FormBody.Builder()
.add("username","admin")
.add("password","123456")
.build();
然後在Request.Builder()中調用一下post()方法,並將RequestBody對象傳入
Request request = new Request.Builder()
.url("https://www.baidu.com")
.post(requestBody)
.build();
3. 解析XML格式數據
在網絡上傳輸數據時最常用的格式有兩種:XML和JSON
我們搭建一個簡單的Web服務器,在這個服務器上提供一段XML文本,然後我們在程序裏去訪問這個服務器,再對得到的XML文本進行解析。
1. 搭建簡單的web服務器
進入網站,點擊如圖所示:
如下圖所示,點擊
選擇下圖框中任一版本下載
下載後是一個壓縮包,將其解壓
1. 配置
進入 Apache24/conf/httpd.conf
Define SRVROOT “D:\MyDownloads\httpd-2.4.39-lre-2.9.2-x64-vc14\Apache24”
ServerRoot “D:\MyDownloads\httpd-2.4.39-lre-2.9.2-x64-vc14\Apache24”
將雙引號裏面的內容改爲當前Apache24文件夾的地址
Listen 80 ServerName
這兩個後面都有端口號,開始設置爲80,你也可以改爲其他的,一般小於1024的端口號已經被佔用了,這裏我修改爲18011
2 . 安裝
以管理員身份打開控制檯(右鍵點擊開始),到bin目錄下,輸入 .\httpd -k install
安裝成功後,運行ApacheMonitor.exe,如圖
選擇Apache2.4,點擊start
在瀏覽器地址欄輸入,localhost:端口號,若出現如下頁面則表示啓動成功
2. 開始
1. 進入htdocs文件夾,新建get_data.xml文件,編輯,並加入如下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>
Pull解析方式
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
TextView responseText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button sendRequest = (Button)findViewById(R.id.send_request);
responseText = (TextView)findViewById(R.id.response_content);
sendRequest.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if(view.getId()==R.id.send_request){
sendRequestWithOkHttp();
}
}
private void sendRequestWithOkHttp() {
new Thread(){
public void run(){
try {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://10.0.2.2:18011/get_data.xml")
.build();
Response response = client.newCall(request).execute();
String responseData = response.body().string();
parseXMLWithPull(responseData);
}catch(Exception e){
e.printStackTrace();
}
}
}.start();
}
private void parseXMLWithPull(String xmlData){
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
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的實例,並藉助這個實例得到XmlPullParser對象
2 . 調用XmlPullParser的setInput()方法將服務器返回的XML數據設置進去
3. 通過getEventType()可以得到當前解析事件,然後在一個while循環中不斷地解析
4. 如果當前的解析事件不等於XmlPullParser.END_DOCUMENT,說明解析沒有完成,調用next()獲取下一個解析事件
5 . 在while循環中,通過getName()得到當前節點的名字,如果發現節點名等於id,name或者version,就調用nextText()方法獲取節點的具體內容
SAX解析
新建一個ContentHandler類繼承DefaultHandler
public class ContentHandler extends DefaultHandler {
private static final String TAG = "ContentHandler";
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 endDocument() throws SAXException {
super.endDocument();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
nodeName = localName;
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if("app".equals(localName)){
Log.d(TAG, "id is "+id.toString().trim());
Log.d(TAG, "name is "+name.toString().trim());
Log.d(TAG, "version is "+version.toString().trim());
id.setLength(0);
name.setLength(0);
version.setLength(0);
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
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);
}
}
}
startDocument()方法會在開始XML解析的時候調用。
startElement()方法會在開始解析某個節點時調用
characters()方法會在獲取節點中的內容時調用
endElement()方法會在完成解析某個節點時調用
endDocument()方法會在完成整個XML解析時調用
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
TextView responseText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button sendRequest = (Button)findViewById(R.id.send_request);
responseText = (TextView)findViewById(R.id.response_content);
sendRequest.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if(view.getId()==R.id.send_request){
sendRequestWithOkHttp();
}
}
private void sendRequestWithOkHttp() {
new Thread(){
public void run(){
try {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://10.0.2.2:18011/get_data.xml")
.build();
Response response = client.newCall(request).execute();
String responseData = response.body().string();
parseXMLWithSAX(responseData);
}catch(Exception e){
e.printStackTrace();
}
}
}.start();
}
private void parseXMLWithSAX(String xmlData) {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
XMLReader xmlReader = factory.newSAXParser().getXMLReader();
ContentHandler handler = new ContentHandler();
xmlReader.setContentHandler(handler);
xmlReader.parse(new InputSource(new StringReader(xmlData)));
}catch (Exception e){
e.printStackTrace();
}
}
}
parserXMLWithSAX方法先創建了一個SAXParserFactory對象,然後再獲取XMLReader對象,接着將我們編寫的ContentHandler的實例設置到XMLReader中,最後調用parse()方法
4. 解析JSON格式數據
1. 進入htdocs文件夾,新建get_data.json文件,編輯,並加入如下JSON格式的內容
[{"id":"5","version":"5.5","name":"clash of clans"},
{"id":"6","version":"7","name":"boom beach"},
{"id":"7","version":"3.5","name":"clash royale"}]
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
TextView responseText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button sendRequest = (Button)findViewById(R.id.send_request);
responseText = (TextView)findViewById(R.id.response_content);
sendRequest.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if(view.getId()==R.id.send_request){
sendRequestWithOkHttp();
}
}
private void sendRequestWithOkHttp() {
new Thread(){
public void run(){
try {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://10.0.2.2:18011/get_data.json")
.build();
Response response = client.newCall(request).execute();
String responseData = response.body().string();
parseJSONWithJSONObject(responseData);
}catch(Exception e){
e.printStackTrace();
}
}
}.start();
}
private void parseJSONWithJSONObject(String jsonData){
try {
JSONArray jsonArray = new JSONArray(jsonData);
for (int i = 0; i < jsonArray.length(); i++) {
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();
}
}
}
由於我們在服務器中定義的時一個JSON數組,因此這裏首先將服務器返回的數據傳入到了一個JSONArray對象
中,然後循環遍歷這個JSONArray,從中取出的每一個元素都是JSONObject對象,每個對象中又會包含id,name,version這些數據