Android 解析WindowManagerService
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 解析WindowManagerService相关的知识,希望对你有一定的参考价值。
参考技术A WMS是窗口的管理者,它负责窗口的启动、添加和删除,另外窗口的大小和层级也是由WMS进行管理的。窗口管理的核心成员有DisplayContent、WindowToken和WindowState。窗口间进行切换时,使用窗口动画可以显得更炫一些,窗口动画由WMS的动画子系统来负责,动画子系统的管理者为WindowAnimator。
通过对窗口的触摸从而产生触摸事件,InputManagerService(IMS)会对触摸事件进行处理,它会寻找一个最合适的窗口来处理触摸反馈信息,WMS是窗口的管理者,因此,WMS“理所应当”的成为了输入系统的中转站。
窗口并不具备有绘制的功能,因此每个窗口都需要有一块Surface来供自己绘制。为每个窗口分配Surface是由WMS来完成的。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
WindowManagerPolicy(WMP)类型的变量。WindowManagerPolicy是窗口管理策略的接口类,用来定义一个窗口策略所要遵循的通用规范,并提供了WindowManager所有的特定的UI行为。它的具体实现类为PhoneWindowManager,这个实现类在WMS创建时被创建。WMP允许定制窗口层级和特殊窗口类型以及关键的调度和布局。
ArraySet类型的变量,元素类型为Session.它主要用于进程间通信,其他的应用程序进程想要和WMS进程进行通信就需要经过Session,并且每个应用程序进程都会对应一个Session,WMS保存这些Session用来记录所有向WMS提出窗口管理服务的客户端。
WindowHashMap类型的变量,WindowHashMap继承了HashMap,它限制了HashMap的key值的类型为IBinder,value值的类型为WindowState。WindowState用于保存窗口的信息,在WMS中它用来描述一个窗口。综上得出结论,mWindowMap就是用来保存WMS中各种窗口的集合。
ArrayList类型的变量,元素类型为AppWindowToken,它是WindowToken的子类。
WindowToken主要有两个作用:
InputManagerService类型的变量,输入系统的管理者。InputManagerService(IMS)会对触摸事件进行处理,它会寻找一个最合适的窗口来处理触摸反馈信息,WMS是窗口的管理者,因此,WMS“理所应当”的成为了输入系统的中转站。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService .java
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
frameworks/base/core/java/android/view/WindowManagerGlobal.java
WindowManagerService服务就可以通过它在内部所创建的WindowState对象的成员变量mClient来 要求运行在应用程序进程这一侧的Activity组件来配合管理窗口的状态,例如:
ViewRootImpl这个类在android的UI结构中扮演的是一个中间者的角色,连接的是PhoneWindow 和WindowManagerService,也就是窗口管理系统与窗口呈现系统之间的桥梁。
它的主要作用有两个:
1.向DecorView分发收到的用户发起的event事件,如按键,触屏等事件;
2.与WindowManagerService交互,完成整个Activity的GUI的绘制。
3.里面两个重要的变量 mWindowSessoin和mWindow。
每个App的Activity对应一个AppWindowToken。其中的appToken为IApplicationToken类型,连接着对应的AMS中的ActivityRecord::Token对象,有了它就可以顺着AppWindowToken找到AMS中相应的ActivityRecord。其中allAppWindows是一个无序的列表,包含该Activity中所有的窗口。用dumpsys window display可以查看z-ordered的AppWindowToken列表。
表示一个显示设备上的内容,这个显示设备可以是外接显示屏,也可以是虚拟显示屏。其中 mWindows是一个WindowState的有序(Z-ordered,底部最先)列表。mStackBoxes包含了若干个StackBox,其中 一个为HomeStack,另一个是App的StackBox。所有的StackBox被组织成二叉树,StackBox是其中的节点,其中有三个重要成 员变量,mFirst和mSecond指向左和右子结点(也是StackBox),StackBox的成员mStack才是我们真正关心的东西 -TaskStack。可以看到,为了要把TaskStack存成树的结构,需要一个容器,这个容器就是StackBox。
参考链接: http://liuwangshu.cn/tags/Android%E6%A1%86%E6%9E%B6%E5%B1%82/
出版的“Android进阶解密”这本书值得看
Android解析服务器响应数据
文章目录
Android解析服务器响应数据
解析XML格式数据
- 通常情况下,每一个需要访问网络的应用程序都会有一个自己的服务器,我们可以向服务器提交自己的数据
- 在网络上传输数据最常用的格式一般有两种:XML和JSON
- 搭建一个最简单的服务器,然后在这个服务器上获取一段XML格式的数据
- 在Windows上面搭建一个Apache服务器,方式如下
- Windows搭建apache服务器_林中云雾的博客-CSDN博客_windows搭建apache服务器
Pull解析方式
- 解析XML格式的数据其实也有挺多种,比较常用的两种是Pull解析和SAX解析
- 修改MainActivity中的代码
/**
* 使用Pull的方式解析XML数据
*
* @param xmlData String
*/
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()
- 在这里首先将HTTP请求的地址改成了http://10.0.2.2/get_data.xml,10.0.2.2 因为10.0.2.2对于模拟器来说就是本计算机的IP地址,在得到服务器返回的数据之后,我们不再直接将其进行展示,而是调用了parseXMLWithPull()方法解析服务器返回的数据.
- 因为从Android9.0系统开始,应用程序默认只允许使用HTTPS类型的网络请求,HTTP类型的网络请求因为有安全隐患默认不再被支持,而搭建的Apache服务器使用的就是HTTP,所在还需要再进行一项额外的配置.
- 为了能够让程序使用HTTP,还需要在res目录下New->Directory,创建一个xml目录,接着右击xml目录->New->File,创建一个network_config.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就是以明文的方式传输数据的
- 接下来修改AndroidManifest.xml中的代码来启用刚创建的配置文件
- 这样就可以在程序中使用HTTP协议了,现在运行项目,可以看到,已经将XML数据中的指定内容成功解析出来了
SAX解析方式
- Pull解析方式虽然好用,但是并不是唯一的选择,还有一种SAX解析方式也是最常用的一种解析方式
- 要使用SAX解析方式,通常情况下需要新建一个类,继承自DefaultHandler,并重写父类的5个方法
class MyHandler : DefaultHandler()
override fun startDocument()
override fun startElement(uri: String, localName: String, qName: String, attributes: Attributes)
override fun characters(ch: CharArray, start: Int, length: Int)
override fun endElement(uri: String, localName: String, qName: String)
override fun endDocument()
- 新建一个ContentHandler类继承自DefaultHandler,并重写5个方法
package com.zb.networktest
import android.util.Log
import org.xml.sax.Attributes
import org.xml.sax.helpers.DefaultHandler
import kotlin.text.StringBuilder
/**
* @Description:
* @Author zb~
* @Date 2022/12/26 14:15
*/
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()
/**
* 该方法在开始解析某一个节点的时候进行调用
*
* @param uri String
* @param localName String
* @param qName String
* @param attributes Attributes
*/
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")
/**
* 该方法会在获取节点种内容的时候进行调用
*
* @param ch CharArray
* @param start Int
* @param length Int
*/
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)
/**
* 该方法会在完成解析某一个节点的时候进行调用
*
* @param uri String
* @param localName String
* @param qName String
*/
override fun endElement(uri: String?, localName: String?, qName: String?)
if ("app" == localName)
//trim()方法可以去掉字符串两端的多余空格
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)
/**
* 该方法会在完成整个XML解析的时候进行调用
*/
override fun endDocument()
- 接下来修改MainActivity中的代码
/**
* 使用SAX的方式来解析XML数据
*
* @param xmlData String
*/
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()
- 在得到服务器返回的数据之后,通过上面这个parseXMLWithSAX()方法来解析XML数据
- parseXMLWithSAX()方法中先是获取到了SAXParseFactory的对象,然后再获取XMLReader对象,接着我们将编写的ContentHandler的实例设置到XMLReader中,最后调用parse()方法进行解析
- 最后运行程序,发现同样也是能够解析XML数据
解析JSON数据
- 相比于XML数据,JSON数据的优势在于它的体积更小,在网络上传输的时候更省流量,但是它的缺点在于它的语义比较差,看起来不如XML直观
使用JSONObject
- 解析JSON数据也有很多种方法,可以使用官方提供的JSONObject,也可以使用Google的开源GSON,另外还有一些第三方开源库比如:Jackson,FastJSON等也非常不错.
- 先看一下JSONObject的使用方法
- 修改MainActivity中的代码,编写相关解析方法
/**
* JSONObject的方式解析JSON数据
*
* @param jsonData Response
*/
private fun parseJSONWithJSONObject(jsonData: Response)
try
//由于在服务器中定义的是一个JSON数组,所以首先将服务器返回的数据传入一个JSONArray对象中
val jsonArray = JSONArray(jsonData)
//然后循环遍历这个JSONArray,从中取出的每一个元素都是JSONObject对象
for (i in 0 until jsonArray.length())
//然后循环遍历这个JSONArray,从中取出的每一个元素都是JSONObject对象
val jsonObject = jsonArray.getJSONObject(i)
//每个JSONObject对象中又会包含id,name,version这些数据,接下来只需要调用getString()方法将这些数据进行取出才可以
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()
- 运行程序即可发现成功解析JSON数据
使用GSON的方式来解析JSON数据
- 使用JSONObject来解析JSON数据是非常的简单,但是使用GSON来进行数据的解析,同样也是十分的简单
- 因为GSON并没有添加到Android官方的API当中,所以想要使用这个功能,需要在项目当中添加GSON的依赖,添加方式如下
implementation("com.google.code.gson:gson:2.8.9")
- GSON的强大之处在于,可以将一段JSON格式的字符串自动映射成为一个对象,从而不需要我们再动手编写代码进行解析了
- 比如一段JSON格式的数据如下所示:
"name:"Tom", "age":"20"
- 我们就可以定义一个JSON类,并加入name和age字段,然后只需要简单的调用如下代码就可以将JSON数据自动解析成为一个Person对象了
val gson = Gson()
val person = gson.fromJson(jsonData, Person::class.java)
- 但是如果需要解析的是一段json数组,会比较麻烦,比如如下的格式
[
"name":"Tom",
"age":"20"
,
"name":"Jack",
"age":"18"
,
"name":"Lily",
"age":"22"
]
- 这个时候,我们需要借助TypeToken将期望解析成的数据类型传入fromJson()方法中,如下所示:
val typeOf = object : TypeToken<List<Person>>() .type
val people = gson.fromJson<List<Person>>(jsonData, typeOf)
- 综上所述就是GSON的基本用法,下面新建一个App类来解析Apache的json数据
class App(val id: String, val name: String, val version: String)
- 然后修改MainActivity当中的代码,编写GSON方法具体的解析逻辑
/**
* 使用GSON来解析Json数据
*
* @param jsonData String
*/
private fun parseJSONWithGSON(jsonData: String)
val gson = Gson()
val typeOf = object : TypeToken<List<App>>() .type
val appList = gson.fromJson<List<App>>(jsonData, typeOf)
for (app in appList)
Log.d("MainActivity", "id id $app.id")
Log.d("MainActivity", "name id $app.name")
Log.d("MainActivity", "id id $app.id")
- 所实现的效果和JSONObject是一样的.
以上是关于Android 解析WindowManagerService的主要内容,如果未能解决你的问题,请参考以下文章