Android中网络相关的技术,主要分别两种,一种为直接显示网页,另外一种为获取服务器中的数据进行设置。
权限声明
访问网络是需要声明权限
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.webviewtest"><uses-permission android:name="android.permission.INTERNET" />...</manifest>
WebView的使用
通过WebView的控件,直接显示网页,不需要考虑网页中的具体数据。
class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)webView.settings.javaScriptEnabled=truewebView.webViewClient = WebViewClient()webView.loadUrl("https://www.baidu.com")}}
调用了WebView的setWebViewClient()方法,并传入了一个WebViewClient的实例。这段代码的作用是,当需要从一个网页跳转到另一个网页时,我们希望目标网页仍然在当前WebView中显示,而不是打开系统浏览器。
HttpURLConnection的使用
get获取数据
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? = nulltry {val response = StringBuilder()val url = URL("https://www.baidu.com")connection = url.openConnection() as HttpURLConnectionconnection.connectTimeout = 8000connection.readTimeout = 8000val input = connection.inputStream// 下面对获取到的输入流进行读取val reader = BufferedReader(InputStreamReader(input))reader.use {reader.forEachLine {response.append(it)}}} catch (e: Exception) {e.printStackTrace()} finally {connection?.disconnect()}}}}
}
post发送数据
connection.requestMethod = "POST"
val output = DataOutputStream(connection.outputStream)
output.writeBytes("username=admin&password=123456")
okHttp的使用
项目主页地址:
https://github.com/square/okhttp
添加依赖:
dependencies {...implementation 'com.squareup.okhttp3:okhttp:4.1.0'
}
版本根据官网中的最新版本进行添加
okHttp的具体用法
获取Client的实例
val client = OkHttpClient()
创建get的request实例
创建request,需要知道是否需要设置token
val request = Request.Builder().url("https://www.baidu.com").build()
获取response
同步获取
val response = client.newCall(request).execute()
val responseData = response.body?.string()
异步获取:
client.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(@NotNull Call call, @NotNull IOException e) {//响应失败}@Overridepublic void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {//响应成功val responseData = response.body?.string()}});
发起Post的请求
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
文件。
<?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文件中的内容,如下所示:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.networktest">...<applicationandroid: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格式的数据
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.eventTypevar id = ""var name = ""var version = ""while (eventType != XmlPullParser.END_DOCUMENT) {val nodeName = xmlPullParser.namewhen (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方法
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().XMLReaderval handler = ContentHandler()// 将ContentHandler的实例设置到XMLReader中xmlReader.contentHandler = handler// 开始执行解析xmlReader.parse(InputSource(StringReader(xmlData)))} catch (e: Exception) {e.printStackTrace()}}
}
ContentHandler的定义:
class ContentHandler : DefaultHandler() {private var nodeName = ""private lateinit var id: StringBuilderprivate lateinit var name: StringBuilderprivate lateinit var version: StringBuilderoverride fun startDocument() {id = StringBuilder()name = StringBuilder()version = StringBuilder()}override fun startElement(uri: String, localName: String, qName: String, attributes:Attributes) {// 记录当前节点名nodeName = localNameLog.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
......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解析
添加依赖
dependencies {...implementation 'com.google.code.gson:gson:2.8.5'
}
解析一段字符串
{“name”:“Tom”,“age”:20}
设置Person的类,直接进行转换
val gson = Gson()
val person = gson.fromJson(jsonData, Person::class.java)
解析字符串数组
需要借助TypeToken将期望解析成的数据类型传入fromJson()方法中
val typeOf = object : TypeToken<List<Person>>() {}.type
val people = gson.fromJson<List<Person>>(jsonData, typeOf)
jsonData为需要解析的字符串数组
网络请求回调方法:
interface HttpCallbackListener {fun onFinish(response: String)fun onError(e: Exception)
}
object HttpUtil {fun sendHttpRequest(address: String, listener: HttpCallbackListener) {thread {var connection: HttpURLConnection? = nulltry {val response = StringBuilder()val url = URL(address)connection = url.openConnection() as HttpURLConnectionconnection.connectTimeout = 8000connection.readTimeout = 8000val input = connection.inputStreamval 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
object HttpUtil {...fun sendOkHttpRequest(address: String, callback: okhttp3.Callback) {val client = OkHttpClient()val request = Request.Builder().url(address).build()client.newCall(request).enqueue(callback)}
}
使用
HttpUtil.sendOkHttpRequest(address, object : Callback {override fun onResponse(call: Call, response: Response) {// 得到服务器返回的具体内容val responseData = response.body?.string()}override fun onFailure(call: Call, e: IOException) {// 在这里对异常情况进行处理}
})