Android中网络相关的技术,主要分别两种,一种为直接显示网页,另外一种为获取服务器中的数据进行设置。
权限声明
访问网络是需要声明权限
java
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.webviewtest">
<uses-permission android:name="android.permission.INTERNET" />
...
</manifest>
WebView的使用
通过WebView的控件,直接显示网页,不需要考虑网页中的具体数据。
java
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
webView.settings.javaScriptEnabled=true
webView.webViewClient = WebViewClient()
webView.loadUrl("https://www.baidu.com")
}
}
调用了WebView的setWebViewClient()方法,并传入了一个WebViewClient的实例。这段代码的作用是,当需要从一个网页跳转到另一个网页时,我们希望目标网页仍然在当前WebView中显示,而不是打开系统浏览器。
HttpURLConnection的使用
get获取数据
java
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
sendRequestBtn.setOnClickListener {
sendRequestWithHttpURLConnection()
}
}
private fun sendRequestWithHttpURLConnection() {
// 开启线程发起网络请求
thread {
var connection: HttpURLConnection? = null
try {
val response = StringBuilder()
val url = URL("https://www.baidu.com")
connection = url.openConnection() as HttpURLConnection
connection.connectTimeout = 8000
connection.readTimeout = 8000
val input = connection.inputStream
// 下面对获取到的输入流进行读取
val reader = BufferedReader(InputStreamReader(input))
reader.use {
reader.forEachLine {
response.append(it)
}
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
connection?.disconnect()
}
}
}
}
}
post发送数据
java
connection.requestMethod = "POST"
val output = DataOutputStream(connection.outputStream)
output.writeBytes("username=admin&password=123456")
okHttp的使用
项目主页地址:
https://github.com/square/okhttp
添加依赖:
java
dependencies {
...
implementation 'com.squareup.okhttp3:okhttp:4.1.0'
}
版本根据官网中的最新版本进行添加
okHttp的具体用法
获取Client的实例
java
val client = OkHttpClient()
创建get的request实例
创建request,需要知道是否需要设置token
java
val request = Request.Builder()
.url("https://www.baidu.com")
.build()
获取response
同步获取
java
val response = client.newCall(request).execute()
val responseData = response.body?.string()
异步获取:
java
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
//响应失败
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
//响应成功
val responseData = response.body?.string()
}
});
发起Post的请求
java
val requestBody = FormBody.Builder()
.add("username", "admin")
.add("password", "123456")
.build()
val request = Request.Builder()
.url("https://www.baidu.com")
.post(requestBody)
.build()
从Android 9.0系统开始,应用程序默认只允许使用HTTPS类型的网络请求,HTTP类型的网络请求因为有安全隐患默认不再被支持,如果使用的是HTTP,需要设置文件,res -> xml ->network_config.xml
文件。
java
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
</network-security-config>
这段配置文件是允许我们以明文的方式在网络上传输数据,而HTTP使用的就是明文传输方式。
需要修改manifest文件中的内容,如下所示:
java
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.networktest">
...
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:networkSecurityConfig="@xml/network_config">
...
</application>
</manifest>
解析XML数据的两种方法:
①pull方法
方法parseXMLWithPull中的,即为使用Pull方法解析XML格式的数据
java
class MainActivity : AppCompatActivity() {
...
private fun sendRequestWithOkHttp() {
thread {
try {
val client = OkHttpClient()
val request = Request.Builder()
// 指定访问的服务器地址是计算机本机
.url("http://10.0.2.2/get_data.xml")
.build()
val response = client.newCall(request).execute()
val responseData = response.body?.string()
if (responseData != null) {
parseXMLWithPull(responseData)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
...
private fun parseXMLWithPull(xmlData: String) {
try {
val factory = XmlPullParserFactory.newInstance()
val xmlPullParser = factory.newPullParser()
xmlPullParser.setInput(StringReader(xmlData))
var eventType = xmlPullParser.eventType
var id = ""
var name = ""
var version = ""
while (eventType != XmlPullParser.END_DOCUMENT) {
val nodeName = xmlPullParser.name
when (eventType) {
// 开始解析某个节点
XmlPullParser.START_TAG -> {
when (nodeName) {
"id" -> id = xmlPullParser.nextText()
"name" -> name = xmlPullParser.nextText()
"version" -> version = xmlPullParser.nextText()
}
}
// 完成解析某个节点
XmlPullParser.END_TAG -> {
if ("app" == nodeName) {
Log.d("MainActivity", "id is $id")
Log.d("MainActivity", "name is $name")
Log.d("MainActivity", "version is $version")
}
}
}
eventType = xmlPullParser.next()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
②SAX方法
java
class MainActivity : AppCompatActivity() {
...
private fun sendRequestWithOkHttp() {
thread {
try {
val client = OkHttpClient()
val request = Request.Builder()
// 指定访问的服务器地址是计算机本机
.url("http://10.0.2.2/get_data.xml")
.build()
val response = client.newCall(request).execute()
val responseData = response.body?.string()
if (responseData != null) {
parseXMLWithSAX(responseData)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
...
private fun parseXMLWithSAX(xmlData: String) {
try {
val factory = SAXParserFactory.newInstance()
val xmlReader = factory.newSAXParser().XMLReader
val handler = ContentHandler()
// 将ContentHandler的实例设置到XMLReader中
xmlReader.contentHandler = handler
// 开始执行解析
xmlReader.parse(InputSource(StringReader(xmlData)))
} catch (e: Exception) {
e.printStackTrace()
}
}
}
ContentHandler的定义:
java
class ContentHandler : DefaultHandler() {
private var nodeName = ""
private lateinit var id: StringBuilder
private lateinit var name: StringBuilder
private lateinit var version: StringBuilder
override fun startDocument() {
id = StringBuilder()
name = StringBuilder()
version = StringBuilder()
}
override fun startElement(uri: String, localName: String, qName: String, attributes:
Attributes) {
// 记录当前节点名
nodeName = localName
Log.d("ContentHandler", "uri is $uri")
Log.d("ContentHandler", "localName is $localName")
Log.d("ContentHandler", "qName is $qName")
Log.d("ContentHandler", "attributes is $attributes")
}
override fun characters(ch: CharArray, start: Int, length: Int) {
// 根据当前节点名判断将内容添加到哪一个StringBuilder对象中
when (nodeName) {
"id" -> id.append(ch, start, length)
"name" -> name.append(ch, start, length)
"version" -> version.append(ch, start, length)
}
}
override fun endElement(uri: String, localName: String, qName: String) {
if ("app" == 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 fun endDocument() {
}
}
id、name和version中都可能是包括回车或换行符的,需要调用trim。
解析JSON数据
官方提供的JSONObject,使用Google的开源库GSON。或者使用一些第三方的开源库如Jackson、FastJSON等。
①官方的JSONObject
java
......
private fun parseJSONWithJSONObject(jsonData: String) {
try {
val jsonArray = JSONArray(jsonData)
for (i in 0 until jsonArray.length()) {
val jsonObject = jsonArray.getJSONObject(i)
val id = jsonObject.getString("id")
val name = jsonObject.getString("name")
val version = jsonObject.getString("version")
Log.d("MainActivity", "id is $id")
Log.d("MainActivity", "name is $name")
Log.d("MainActivity", "version is $version")
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
②使用GSON解析
添加依赖
java
dependencies {
...
implementation 'com.google.code.gson:gson:2.8.5'
}
解析一段字符串
{"name":"Tom","age":20}
设置Person的类,直接进行转换
java
val gson = Gson()
val person = gson.fromJson(jsonData, Person::class.java)
解析字符串数组
需要借助TypeToken将期望解析成的数据类型传入fromJson()方法中
java
val typeOf = object : TypeToken<List<Person>>() {}.type
val people = gson.fromJson<List<Person>>(jsonData, typeOf)
jsonData为需要解析的字符串数组
网络请求回调方法:
java
interface HttpCallbackListener {
fun onFinish(response: String)
fun onError(e: Exception)
}
java
object HttpUtil {
fun sendHttpRequest(address: String, listener: HttpCallbackListener) {
thread {
var connection: HttpURLConnection? = null
try {
val response = StringBuilder()
val url = URL(address)
connection = url.openConnection() as HttpURLConnection
connection.connectTimeout = 8000
connection.readTimeout = 8000
val input = connection.inputStream
val reader = BufferedReader(InputStreamReader(input))
reader.use {
reader.forEachLine {
response.append(it)
}
}
// 回调onFinish()方法
listener.onFinish(response.toString())
} catch (e: Exception) {
e.printStackTrace()
// 回调onError()方法
listener.onError(e)
} finally {
connection?.disconnect()
}
}
}
}
使用json
java
object HttpUtil {
...
fun sendOkHttpRequest(address: String, callback: okhttp3.Callback) {
val client = OkHttpClient()
val request = Request.Builder()
.url(address)
.build()
client.newCall(request).enqueue(callback)
}
}
使用
java
HttpUtil.sendOkHttpRequest(address, object : Callback {
override fun onResponse(call: Call, response: Response) {
// 得到服务器返回的具体内容
val responseData = response.body?.string()
}
override fun onFailure(call: Call, e: IOException) {
// 在这里对异常情况进行处理
}
})