温习Android基础知识——《第一行代码(第三版)》读书笔记 Chapter 11 网络编程

第十一章:看看精彩的世界,使用网络技术

注意,使用网络需要在权限中声明

WebView

当我们需要在应用程序里显示一些网页时就可以使用这个控件。

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")
    }
}

使用HTTP访问网络

原理:客户端向服务器发出一条HTTP请求,服务器收到请求之后返回一些数据给客户端,客户端再将这些数据进行解析和处理。

HttpURLConnection

private fun sendRequestWithHttpURLConnection() {
        thread {
            var connection : HttpURLConnection? = null
            try {
                val response = StringBuilder()
                val url = URL("https://www.baidu.com")
                //获取HttpURLConnection实例
                connection = url.openConnection() as HttpURLConnection
                //设置HTTP请求所使用的方法,GET表示从服务器获取,POST表示向服务器提交
                connection.requestMethod = "GET"
                //设置连接超时与读取超时的毫秒数
                connection.connectTimeout = 8000
                connection.readTimeout = 8000
                //获取返回的数据
                val input = connection.inputStream
                val reader = BufferedReader(InputStreamReader(input))
                reader.use {
                    reader.forEachLine {
                        response.append(it)
                    }
                }
                showResponse(response.toString())
            }catch (e : Exception){
                e.printStackTrace()
            }finally {
            	//关闭HTTP连接
                connection?.disconnect()
            }
        }
    }

    private fun showResponse(response: String) {
    	//该方法就是对异步消息处理机制进行了一层封装,背后原理是一样的
        runOnUiThread {
            responseText.text = response
        }
    }

如果想要提交数据:

connection.requestMethod = "POST"
val output = DataOutputStream(connection.outputStream)
ouyput.writeBytes("username=admin&password=123456")

OkHttp

private fun sendRequestWithOkHttp() {
        thread {
            try {
            	//获取OkHttpClient实例
                val client = OkHttpClient()
                //创建Request对象
                val request = Request.Builder()
                        .url("http://10.0.2.2/get_data.json")
                        .build()
                //调用OkHttpClient的newCall方法来创建一个Call对象,并调用其execute方法发送请求并获取服务器返回的数据
                val response = client.newCall(request).execute()
                val responseData = response.body?.string()
                if (responseData!=null){
                    parseJSONWithGSON(responseData)
                }
            }catch (e : Exception){
                e.printStackTrace()
            }
        }
    }

如果是发起一条POST请求,需要构建一个Request Body对象来存放待提交的参数:

val requestBody = FormBody.Builder()
			.add("username","admin")
			.add("password","123456")
			.build()

解析XML数据

Pull

private fun parseXMLWithPull(xmlData: String) {
        try {
        	//创建XmlPullParserFactory实例,并借其得到xmlPullParser对象
            val factory = XmlPullParserFactory.newInstance()
            val xmlPullParser = factory.newPullParser()
            //将服务器返回的XML数据setInput里
            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

SAX用法比Pull复杂一些,但语义方面会更加清楚。

private fun parseXMLWithSAX(xmlData: String) {
        try {
            val factory = SAXParserFactory.newInstance()
            val xmlReader = factory.newSAXParser().xmlReader
            val handler = ContentHandler()
            xmlReader.contentHandler = handler
            xmlReader.parse(InputSource(StringReader(xmlData)))
        }catch (e:Exception){
            e.printStackTrace()
        }
    }
class ContentHandler : DefaultHandler() {

    private var nodeName = ""

    private lateinit var id : StringBuilder

    private lateinit var name : StringBuilder

    private lateinit var version : StringBuilder
	//开始解析XML时调用
    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) {
        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()}")
            id.setLength(0)
            name.setLength(0)
            version.setLength(0)
        }
    }
	//完成XML解析时调用
    override fun endDocument() {
        super.endDocument()
    }
}

解析JSON数据

JSONObject

//我们在服务器中定义了一个数组,传入其中
private fun parseJSONWithJSONObject(jsonData: String) {
        try {
        	//将数据传入JSONArray
            val jsonArray = JSONArray(jsonData)
            //对jsonArray中的每一个元素依次取出我们需要的数据
            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

GSON的强大之处在于可以将一段JSON的字符串自动映射成一个对象。

private fun parseJSONWithGSON(jsonData: String) {
        val gson = Gson()
        //获取数组中对象的类型
        val typeOf = object : TypeToken<List<App>>(){}.type
        //根据解析出的类型将JSON数据自动解析成对象
        val appList = gson.fromJson<List<App>>(jsonData, typeOf)
        for (app in appList){
            Log.d("MainActivity", "id is ${app.id}")
            Log.d("MainActivity", "name is ${app.name}")
            Log.d("MainActivity", "version is ${app.version}")
        }
    }

网络请求回调的实现方式

一般我们应该将通用的网络操作提取到一个公共的类里,并提供一个公共的方法,每当发起网络请求时,只需要简单地调用一下这个方法即可。

如何在使用线程的提升返回响应的数据?
利用回调机制。
在HTTPURLConnection中,传入接口

//发送请求时同时传入该接口,使得
intertface HttpCallbackListener{
	//服务器成功响应时调用该方法
	fun onFinish(response : String)
	//网络操作出现错误时调用该方法
	fun onError(e : Exception)
}

在OKHTTP中,有自带的回调接口okhttp3.Callback。将execute方法改为enqueue方法,并把okhttp3.Callback参数传入即可。

注意,回调接口是在子线程中运行的,切忌在其中进行UI操作。

Android 9.0的HTTP适配问题

从Android 9.0开始,应用程序默认只允许使用HTTPS类型的网络请求,HTTP类型的网络请求被认为因为有安全隐患默认不再被支持。

新建一个xml,并配置进application中。

<?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>
<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>

Retrofit

Retrofit是在OkHttp的基础上进一步开放出来的应用层网络通信库,它的定位与OkHttp不同,前者侧重上册接口的封装,后者侧重底层通信的实现。

设计思想

Retrofit的设计基于以下几个事实

1.同一款应用程序中发起的网络请求绝大多数指向的是同一个服务器域名

Retrofit可以设置根路径,然后再指定服务器接口时只需使用相对路径即可

2.服务器提供的接口通常是可以根据功能来归类的

Retrofit允许我们对服务器接口进行分类,将功能同属于一类的服务器接口定义到同一个接口文件中,从而让代码架构变得更加合理。

3.开发者更习惯于“调用一个接口,获得它的返回值”这样的编码方式

我们不用关心网络通信的细节,只需要在接口文件中声明一系列方法和返回值 ,然后通过注解的方式指定该方法对应哪个服务器接口,以及需要提供哪些参数。当我们调用该方法是,Retrofit会自动向对应的服务器接口发起请求,并将相应的数据解析成返回值声明的类型。这就使得我们可以用更加面向对象的思维来进行网络操作。

用法

//具体功能种类名开头,Service结尾
interface AppService {
	//GET注解表示会发对该路径(相对路径)发起GET请求
    @GET("get_data.json")
    //这里的返回值必须声明为Retrofit内置的Call类型,并通过泛型来指定服务器响应的数据应该转换成什么对象
    fun getAppData() : Call<List<App>>
}
object ServiceCreator {
    private const val BASE_URL = "http://10.0.2.2/"
	//使用Retrofit.Builder构建Retrofit对象
    val retrofit = Retrofit.Builder()
    	//指定根路径
        .baseUrl(BASE_URL)
        //指定Retrofit解析数据用的转换库
        .addConverterFactory(GsonConverterFactory.create())
        .build()
	//利用泛型实化获取泛型的类型信息
    inline fun <reified T> create() : T = retrofit.create(T::class.java)
}
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        getAppDataBtn.setOnClickListener {
            val appService = ServiceCreator.create<AppService>()
//Retrofit在发起请求时自动开启子线程,数据回调到Callback后又会自动切换回主线程
            appService.getAppData().enqueue(object :Callback<List<App>>{
                override fun onFailure(call: Call<List<App>>, t: Throwable) {
                    t.printStackTrace()
                }

                override fun onResponse(call: Call<List<App>>, response: Response<List<App>>) {
                    val list = response.body()
                    if (list!=null){
                        for (app in list){
                            Log.d("MainActivity", "id is ${app.id}")
                            Log.d("MainActivity", "name is ${app.name}")
                            Log.d("MainActivity", "version is ${app.version}")
                        }
                    }
                }

            })
        }
    }
}

Retrofit中的注解

处理复杂的接口地址类型

灵活运用@Path,@Query注解

指定HTTP请求的header
静态声明

@Headers注解

动态指定

@Header注解

HTTP请求类型

GETPOSTPUTPATCHDELETE
从服务器获取数据向服务器提交数据修改服务器上的数据(全部更新)修改服务器上的数据(局部更新)删除服务器上的数据
### 回答1: 《现代信号处理第三版课后习题pdf》是一本辅助学习《现代信号处理》第三版的习题集。作为一本信号处理的教材,它帮助学生理解和掌握信号处理的基本理论和方法。 该习题集包含了与《现代信号处理第三版》中相应节配套的习题,涵盖了信号的表示与处理、线性时不变系统、频域分析与滤波、采样与模数转换、多通道信号与线性滤波、时频表示与分析等方面的内容。 通过解答这些习题,学生可以巩固课堂上的知识,加深对信号处理理论的理解,并通过实际操作提高分析和解决实际问题的能力。此外,习题集还提供了答案和解析,学生可以通过对答案的核对来检验自己的学习进展,并及时纠正错误。 对于学习信号处理的学生来说,这本习题集是一个非常有价值的学习工具。它不仅帮助学生进行复习和巩固知识,而且提供了实际问题的解决思路,培养了学生的问题分析和解决问题的能力。 总之,《现代信号处理第三版课后习题pdf》是一本对课堂教学进行补充和加强的教材。通过反复练习习题,学生可以提高对信号处理理论和方法的掌握程度,为将来的实际工作打下坚实的基础。 ### 回答2: 《现代信号处理第三版》是一本经典的信号处理教材,课后习题pdf是该教材的一个附属学习资料。这个pdf文件包含了各节的习题与解答,是供学生们进行练习和巩固所学知识的一个重要资源。 首先,在这个pdf中,我们可以找到大量与信号处理相关的习题。这些习题分布在课本中各个节之后,贯穿了整个学习过程。通过完成这些习题,可以更好地巩固课本中所学的概念、理论和方法,并深入理解信号处理的基本原理与技术。 其次,这个习题pdf中还提供了每个习题的详细解答。学生们可以通过参考这些解答,验证自己的答案是否正确,纠正可能存在的错误,并进一步加深对习题中涉及的知识点的理解和掌握。 此外,这个习题pdf对于自学者来说也是一个很好的辅助工具。自学者可以通过习题pdf进行自我测试,自我评估自己的学习进度和掌握情况,并及时发现和填补知识上的漏洞。 总的来说,《现代信号处理第三版》课后习题pdf是一份很好的学习资源。它能够帮助学生们更好地学习信号处理知识,巩固所学内容,并提升自己的解题能力和理论水平。无论是课堂学习还是自学探索,这份习题pdf都是一份非常有价值的参考资料,对于提高信号处理水平具有重要作用。 ### 回答3: 《现代信号处理第三版》课后习题.pdf是一本与现代信号处理相关的教材,适用于对信号处理有一定基础的学生或者从事相关领域的工程师。本书的习题是由书中的知识点延伸而来,旨在加深学生对信号处理理论与应用的理解。以下是对该习题pdf的回答: 该习题pdf共包含多道选择题、简答题和计算题。选择题是对于信号处理的基础知识进行测试,考察学生对信号、系统、频谱等概念的理解。简答题则要求学生阐述信号处理的一些基本原理、方法和应用,并能结合实例进行解答,考察学生的综合能力。计算题则要求学生运用信号处理算法和公式进行计算和推导,考察学生的计算和分析能力。 对于这些习题,学生可以通过翻看课本、复习课堂笔记以及查阅相关参考资料来进行解答。在解答过程中,可以参考例题和习题的解析,以及对应节的知识点进行温习和复习。同时,可以与同学或者老师进行讨论,互相帮助和交流,共同提高对信号处理的理解和应用能力。 通过认真解答该习题pdf,学生可以巩固信号处理的基本概念,加深对信号处理算法和方法的理解,并提高对信号处理应用的把握能力。同时,通过做题的过程,学生也能够培养自己的分析和解决问题的能力,为日后研究和应用信号处理打下坚实的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值