月度归档: 2021 年 3 月

python爬虫–协程(初识)

python爬虫–协程(初识)

python爬虫–协程
基本知识
event_loop:事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上,当满足某些条件的时候,函数就会被循环执行。
coroutine:携程对象,我们可以将携程对象注册到事件循环中,它会被时间循环调用。我们可以使用async关键字来定义一个方法,这个方法在调用时不会被立即执行,而是返回一个协程对象。
task:任务,它是对协程对象的进一步封装, 包含了任务的各个状态。
future:代表将来执行或还没有执行的任务,实际上和task 没有本质区别。
async定义-个协程.
await用来挂起阻塞方法的执行。
协程的基本使用
import asyncio

async def request(url):
print(‘正在请求的url是:’,url)
print(‘请求成功:’,url)
#async修饰的函数,调用之后返回的一个协程对象
c = request(‘www.baidu.com’)

# #创建一个事件循环对象
# loop = asyncio.get_event_loop()
#
# #将携程对象注册到loop中,然后启动loop
# loop.run_until_complete(c)

# #task的使用
# loop = asyncio.get_event_loop()
# #基于loop创建一个task对象
# task = loop.create_task(c)
# print(task)
# loop.run_until_complete(task)
# print(task)

#future的使用
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(c)
print(task)
loop.run_until_complete(task)
print(task)

多任务协程实现
import asyncio
import time

async def request(url):
print(‘正在请求的url是:’,url)
#在异步协程中如果出现同步模块相关的代码,那么就无法实现异步
#time.sleep(2)
await asyncio.sleep(2)
print(‘请求成功:’,url)
#async修饰的函数,调用之后返回的一个协程对象
start = time.time()
urls = {
‘www.123.com’,
‘www.234.com’,
‘www.345.com’
}
#存放多个任务对象
stask = []
for url in urls:
c = request(url)
task = asyncio.ensure_future(c)
stask.append(task)
loop = asyncio.get_event_loop()

loop.run_until_complete(asyncio.wait(stask))

print(time.time()-start)

多任务协程异步实现
在进行多任务协程实现前,还需要建立一个简单的本地http服务

from flask import Flask
import time

app = Flask(__name__)

@app.route(‘/azb’)
def index_azb():
time.sleep(2)
return ‘Hello azb’

@app.route(‘/xx’)
def index_xx():
time.sleep(2)
return ‘Hello xx’

@app.route(‘/hh’)
def index_hh():
time.sleep(2)
return ‘Hello hh’

if __name__ == ‘__main__’:
app.run(threaded=True)

实现%title插图%num

import requests,asyncio,time

start = time.time()
urls = [
‘http://127.0.0.1:5000/azb’,’http://127.0.0.1:5000/xx’,’http://127.0.0.1:5000/hh’
]

async def get_page(url):
print(‘正在下载’,url)
#request是基于同步,必须使用基于异步的网络请求模块
response = requests.get(url=url)
print(‘下载成功!’,url)

tasks = []

for url in urls:
c = get_page(url)
task = asyncio.ensure_future(c)
tasks.append(task)

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

print(‘总耗时’,time.time()-start)

aiohttp模块引入%title插图%num
import requests,asyncio,time,aiohttp

start = time.time()
urls = [
‘http://127.0.0.1:5000/azb’,’http://127.0.0.1:5000/xx’,’http://127.0.0.1:5000/hh’
]

async def get_page(url):
async with aiohttp.ClientSession() as session:
async with await session.get(url) as response:
#text()返回字符串形式的响应数据
#read()返回的二进制形式的响应数据
#json()返回的就是json对象
#获取响应数据操作之前一定要使用await进行手动挂起

page_text = await response.text()
print(page_text)

tasks = []

for url in urls:
c = get_page(url)
task = asyncio.ensure_future(c)
tasks.append(task)

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

print(‘总耗时’,time.time()-start)

%title插图%num

协程还是没有理解

Android 反编译的常用方式

首先反编译别人的APK,主要是学习别人的实现逻辑,给自己一个思路。比较商业的APK也不会给你留下这个漏洞,一些核心的业务处理会在后台进行操作;本地的 APK 也会进行混淆加密等。

好了废话不多说,步入正题:

方法一:在线反编译

很简单方便的一个方法,只要打开网页,把APK放进入,就可以在线编译了,不过速度比较慢,对于比较大型的APK还是不要用这种方法,容易出错。

网址:http://www.javadecompilers.com/apk

%title插图%num

方法二:利用apktool工具

需要用的工具有:

1、APK TOOL:这是谷歌提供的的编译工具,我们知道APK其实就是一个压缩文件,我们完全可以把.apk文件后缀改成.zip,这样我们就可以获取里面的资源文件(图片,res目录下的xml文件)和AndroidManifest.xml(这个是乱码的)如果你使用 apk tool 来进行反编译 apk 就不会出现这种问题了。下载地址:https://ibotpeaches.github.io/Apktool/install/ (需要fq)

2、dex2jar:将apk反编译成java源码(classes.dex转化成jar文件) 下载地址:http://sourceforge.net/projects/dex2jar/files/

3、jd-gui:用来查看 jar 包里面的代码的一种工具。官网下载地址:http://jd.benow.ca/

如果不方便fq,这里打包好三个工具下载:https://download.csdn.net/download/li815705994/12367490

下面说一下具体如何使用这个工具

*步:apk反编译得到程序的源代码、图片、XML配置、语言资源等文件

下载上述工具中的apktool,解压得到3个文件:aapt.exe,apktool.bat,apktool.jar ,将需要反编译的APK文件放到该目录下,

打开命令行界面(运行-CMD) ,定位到apktool文件夹,输入以下命令:apktool.bat d -f  winsh.apk  -o text(apktool2.0以后用这个命令)

注意:(命令中winsh.apk指的是要反编译的APK文件全名, text为反编译后资源文件存放的目录名称,即为:apktool.bat   d  -f    [apk文件 ]  -o  [输出文件夹])

%title插图%num
如果成功了那你的目录下会出现一个text的文件夹

%title插图%num

text就是我们反编译的内容了,当然如果修改完想打包apk也是可以的,因为主题的原因这个就不细说了,网上有很多。

第二步:Apk反编译得到Java源代码

所需工具:dex2jar

将要反编译的APK后缀名改为.rar或则 .zip,并解压,得到其中的额classes.dex文件(它就是java文件编译再通过dx工具打包而成的),将获取到的classes.dex放到之前解压出来的工具dex2jar-2.0 文件夹内,

在命令行下定位到dex2jar.bat所在目录,输入d2j-dex2jar.bat classes.dex,效果如下:

%title插图%num

第三步:使用jd-gui打开classes_dex2jar.jar文件

直接打开jd-gui.exe工具,在里面找到刚才的classes_dex2jar.jar,就可以了如下:

%title插图%num

注意:被混淆过的效果图(类文件名称以及里面的方法名称都会以a,b,c….之类的样式命名):

方法三:图形化反编译apk

需要下载Gapktool.jar,其实这个原理跟方法二是差不多的,只是这个更智能,不需要我们手动敲写命令,都是可视化的,简单方便。下载地址:https://download.csdn.net/download/li815705994/12367497

下载后直接点击Gapktool.bat,就会开启一个界面,把需要编译的apk放进去,在指定一个输出目录就可以了。

%title插图%num

1.运行Gapktool.bat
2.apk文件和输出目录使用英文路径

注意:默认使用jad反编译,如果编译出来的java代码难以阅读,请选择使用jd-gui反编译。
保留smali文件删除gen、src修改res里的文件后可以重修打包成apk使用。

总结:

开发者辛辛苦苦将自己的应用开发出来被别人一反编译便成为了他自己的东西,所以要进行混淆处理。

 

Python requests库参数提交 注意事项

Python requests库参数提交 注意事项

字典与json字符串区别

# python 中的字典格式,是dict类型
{‘a’: ‘sd’}
如果声明a = {“a”: “sd”},它仍是字典,不过python会默认将双引号换成单引号,*后打印的仍然为{‘a’: ‘sd’}

# python 中的json字符串,是str类型
{“a”: “sd”}
两者差别在于引号

在爬虫的过程中有些请求参数是json字符串的,有的是字典类型的,要区分json字符串和字典%title插图%num

json字符串格式如
点击view source%title插图%num

很显然是json字符串格式的,字典转json字符串可以用json.dumps()函数
用data=json.dumps({ })

字典格式如

%title插图%num
点击view source后得到的东西显然可以直接加到url后面的,直接用字典形式提交data={}即可

python爬虫-数据解析(xpath)

python爬虫-数据解析(xpath)

xpath基本概念
xpath解析:*常用且*便捷高效的一种解析方式。通用性强。

xpath解析原理
1.实例化一个etree的对象,且需要将被解析的页面源码数据加载到该对象中
2.调用etree对象中的xpath方法结合xpath表达式实现标签的定位和内容的捕获。
环境安装
pip install lxml
1
如何实例化一个etree对象:
from lxml import etree

1.将本地的html文件中的远吗数据加载到etree对象中:
etree.parse(filePath)
1
2.可以将从互联网上获取的原码数据加载到该对象中:
etree.HTML(‘page_text’)
1
xpath(‘xpath表达式’)
– /:表示的是从根节点开始定位。表示一个层级
– //:表示多个层级。可以表示从任意位置开始定位
– 属性定位://div[@class=’song’] tag[@attrName=’attrValue’]
– 索引定位://div[@class=’song’]/p[3] 索引从1开始的
– 取文本:
– /text()获取的是标签中直系的文本内容
– //text()标签中非直系的文本内容(所有文本内容)
– 取属性:
/@attrName ==>img/src

xpath爬取58二手房实例
爬取网址
https://xa.58.com/ershoufang/

完整代码
from lxml import etree
import requests

if __name__ == ‘__main__’:
headers = {
‘User-Agent’: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36’
}
url = ‘https://xa.58.com/ershoufang/’
page_text = requests.get(url=url,headers=headers).text
tree = etree.HTML(page_text)
div_list = tree.xpath(‘//section[@class=”list”]/div’)
fp = open(‘./58同城二手房.txt’,’w’,encoding=’utf-8′)
for div in div_list:
title = div.xpath(‘.//div[@class=”property-content-title”]/h3/text()’)[0]
print(title)
fp.write(title+’\n’+’\n’)

效果图

%title插图%num
xpath图片解析下载实例
爬取网址
https://pic.netbian.com/4kmeinv/

完整代码
import requests,os
from lxml import etree

if __name__ == ‘__main__’:
headers = {
‘User-Agent’: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36’
}
url = ‘https://pic.netbian.com/4kmeinv/’
page_text = requests.get(url=url,headers=headers).text
tree = etree.HTML(page_text)
li_list = tree.xpath(‘//div[@class=”slist”]/ul/li/a’)
if not os.path.exists(‘./piclibs’):
os.mkdir(‘./piclibs’)
for li in li_list:
detail_url =’https://pic.netbian.com’ + li.xpath(‘./img/@src’)[0]
detail_name = li.xpath(‘./img/@alt’)[0]+’.jpg’
detail_name = detail_name.encode(‘iso-8859-1’).decode(‘GBK’)
detail_path = ‘./piclibs/’ + detail_name
detail_data = requests.get(url=detail_url, headers=headers).content
with open(detail_path,’wb’) as fp:
fp.write(detail_data)
print(detail_name,’seccess!!’)

效果图%title插图%num

xpath爬取全国城市名称实例
爬取网址
https://www.aqistudy.cn/historydata/

完整代码
import requests
from lxml import etree

if __name__ == ‘__main__’:
url = ‘https://www.aqistudy.cn/historydata/’
headers = {
‘User-Agent’: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36’,
}
page_text = requests.get(url=url,headers=headers).content.decode(‘utf-8’)
tree = etree.HTML(page_text)
#热门城市 //div[@class=”bottom”]/ul/li
#全部城市 //div[@class=”bottom”]/ul/div[2]/li
a_list = tree.xpath(‘//div[@class=”bottom”]/ul/li | //div[@class=”bottom”]/ul/div[2]/li’)
fp = open(‘./citys.txt’,’w’,encoding=’utf-8′)
i = 0
for a in a_list:
city_name = a.xpath(‘.//a/text()’)[0]
fp.write(city_name+’\t’)
i=i+1
if i == 6:
i = 0
fp.write(‘\n’)
print(‘爬取成功’)

效果图

%title插图%num
xpath爬取简历模板实例
爬取网址
https://sc.chinaz.com/jianli/free.html

完整代码
import requests,os
from lxml import etree

if __name__ == ‘__main__’:
url = ‘https://sc.chinaz.com/jianli/free.html’
headers = {
‘User-Agent’: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36’,
}
page_text = requests.get(url=url,headers=headers).content.decode(‘utf-8’)
tree = etree.HTML(page_text)
a_list = tree.xpath(‘//div[@class=”box col3 ws_block”]/a’)
if not os.path.exists(‘./简历模板’):
os.mkdir(‘./简历模板’)
for a in a_list:
detail_url = ‘https:’+a.xpath(‘./@href’)[0]
detail_page_text = requests.get(url=detail_url,headers=headers).content.decode(‘utf-8’)
detail_tree = etree.HTML(detail_page_text)
detail_a_list = detail_tree.xpath(‘//div[@class=”clearfix mt20 downlist”]/ul/li[1]/a’)
for a in detail_a_list:
download_name = detail_tree.xpath(‘//div[@class=”ppt_tit clearfix”]/h1/text()’)[0]
download_url = a.xpath(‘./@href’)[0]
download_data = requests.get(url=download_url,headers=headers).content
download_path = ‘./简历模板/’+download_name+’.rar’
with open(download_path,’wb’) as fp:
fp.write(download_data)
print(download_name,’success!!’)

效果图

%title插图%num

Android反编译工具(二)

文章目录

1、apktool
命令
制作各个平台工具
**windows**
**linux**
**mac**
2、dex2jar + jd-gui
3、 enjarify + jd-gui
4、jadx
1、apktool
一般用于获取AndroidManifest.xml和资源文件

官网:https://ibotpeaches.github.io/Apktool/install/

下载地址:https://bitbucket.org/iBotPeaches/apktool/downloads/

命令

1 java -jar apktool.jar [解压文件名] -o [解压路径]

制作各个平台工具
windows
@echo off
if “%PATH_BASE%” == “” set PATH_BASE=%PATH%
set PATH=%CD%;%PATH_BASE%;
chcp 65001 2>nul >nul
java -jar -Duser.language=en -Dfile.encoding=UTF8 “%~dp0\apktool.jar” %*

linux
https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/linux/apktool

mac
https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/osx/apktool

2、dex2jar + jd-gui
将apk中dex文件反编译成jar,并使用jd-gui查看代码

下载地址:https://sourceforge.net/projects/dex2jar/files/

命令

1 d2j-dex2jar.bat app-debug.apk -o app-debug.jar

3、 enjarify + jd-gui
google出品

下载地址 :https://github.com/Storyyeller/enjarify

命令

1 enjarify *.apk -o out.jar

4、jadx
下载地址:https://github.com/skylot/jadx/releases

直接点击jadx-0.9.0\bin下的jadx-gui.bat

Android反编译工具(一)

Apktool
https://github.com/iBotPeaches/Apktool

可以反编译apk,也可以重新打包apk
下载地址:https://github.com/iBotPeaches/Apktool/releases
brew安装:brew install apktool
文档:https://ibotpeaches.github.io/Apktool/documentation
apktool d test.apk

dex2jar
https://github.com/pxb1988/dex2jar

可以将.dex文件反编译成.class文件并打包成jar
下载地址:https://github.com/pxb1988/dex2jar/releases
brew安装:brew install dex2jar
文档:https://github.com/pxb1988/dex2jar/wiki
d2j-dex2jar classes.dex

jd-gui
https://github.com/java-decompiler/jd-gui

%title插图%num

可以查看.class文件,用来查看dex2jar反编译出的jar
下载地址:https://github.com/java-decompiler/jd-gui/releases
brew安装:brew install jd-gui

jadx-gui
https://github.com/skylot/jadx

可以直接浏览反编译后的apk
下载地址:https://github.com/skylot/jadx/releases

uber-apk-signer
https://github.com/patrickfav/uber-apk-signer

可以对apk签名,支持V1、V2
下载地址:https://github.com/patrickfav/uber-apk-signer/releases
文档:https://github.com/patrickfav/uber-apk-signer/blob/master/README.md
java -jar uber-apk-signer.jar -a [apk路径] –ks [keystore路径] –ksAlias [alias名称]

java2smali
https://github.com/ollide/intellij-java2smali

%title插图%num %title插图%num

IntelliJ IDEA和Android Studio的插件,可以将Java和Kotlin文件编译为smali
下载地址:https://github.com/ollide/intellij-java2smali/releases

 

服务器硬盘分 4k 与 512

坑,*对的大坑;以前读书的时候,老师没教过,书本里也没有

以下资料来自微软:

Microsoft 对 Windows 中 4K 扇区硬盘驱动器提供的支持策略
适用于:Windows 10, version 1903Windows Server, version 1903Windows 10, version 1809 详细
摘要
在未来的几年中,数据存储行业内的硬盘驱动器物理格式将从 512 字节的扇区转变为 4,096 字节的扇区(也称为 4K 或 4KB 扇区)。 这次转变是由多种因素推动的。 其中包括存储密度和可靠性的增长。 这种转变会导致与现有软件(包括操作系统和应用程序)的不兼容问题。

本文介绍 Microsoft 当前针对 Windows 操作系统上这些新驱动器类型的支持策略。 应用程序和硬件设备与这些新类型的驱动器连接时,可能会出现可靠性和性能问题。 请联系您的应用程序和硬件供应商,以了解其对这些新驱动器类型的支持策略。

我们将在此讨论三种驱动器类型。 由于 Microsoft 对每种驱动器类型的支持策略各不相同,您应该在进一步阅读之前确认计算机已安装的驱动器类型。

公用名 报告的逻辑扇区大小 报告的物理扇区大小 Windows 版本及支持

512 字节原生,512n

512 字节

512 字节

所有 Windows 版本

高级格式,512e,AF,512 字节仿真

512 字节

4 KB

安装了更新 KB 2553708 的 Windows Vista

安装了更新 KB 2553708 的 Windows Server 2008*

安装了更新 KB 982018 的 Windows 7

安装了更新 KB 982018 的 Windows Server 2008 R2*

从 Windows 7 Server Pack 1 起的所有 Windows 版本和更高版本。

从 Server 2008 R2 Server Pack 1 起的所有 Server 版本和更高版本。

*除 Hyper-V 以外。请参阅“对于大扇区驱动器的应用程序支持要求”部分。

高级格式、AF、4K 原生、4Kn

4 KB

4 KB

从 Windows 8 起的所有 Windows 版本和更高版本。

从 Server 2012 起的所有 Server 版本和更高版本。

其他

非 4 KB 或 512 字节

非 4 KB 或 512 字节

不支持

若要确认您已安装的驱动器类型,请执行以下步骤:

安装 kb/982018。
从提升的命令提示符中运行以下命令:
Fsutil fsinfo ntfsinfo x: (其中 x 代表正在检查的驱动器)
使用“每扇区字节数”和“每物理扇区字节数”的值确定您安装的驱动器类型。 为此,请使用下表:

“每扇区字节数”值 “每物理扇区字节数”值 驱动器类型
4096 4096 4K 原生
512 4096 高级格式(也称为 512E )
512 512 512 字节原生

Python三十行代码实现简单人脸识别

Python三十行代码实现简单人脸识别

一、库介绍
opencv,face_recognition,numpy,以及dlib
注意:
安装opencv速度可能过慢,需要更换国内镜像源,参考:https://blog.csdn.net/X_xs_mxt/article/details/107069379
附带Python3.7,64位版本 dlib whl下载路径:
链接:https://pan.baidu.com/s/1jOmwQ2OpJcbyYPSMisIFlg
提取码:to50

二、库安装
pip install opencv-python
pip install face_recognition
pip nstall numpy

dlib库需进入whl文件路径下安装

pip install dlib-19.17.99-cp37-cp37m-win_amd64.whl

三、fce_recognition库简单介绍
face_recognition的load_image_file方法会加载图片,并返回一个ndarray类型的数据

face_path = “C://Users//25103//Desktop//Python人脸识别//face//徐先生.jpg”
image = face_recognition.load_image_file(face_path)

face_recognition的face_encoding方法,可从返回的ndarray类型数据中提取人脸特征,可同时提取多个特征,返回值为列表类型

face_encoding = face_recognition.face_encodings(image)[0]

face_recognition的face_location方法可以获取图片中所有人脸的位置,其返回值为一个列表

face_locations = face_recognition.face_locations(rgb_frame)

四、代码实现以及注释讲解
# coding = utf-8
import dlib
import cv2
import face_recognition
import os

# 创建视频对象
video_capture = cv2.VideoCapture(0)

# 加载需要识别的人脸图片(这张图片需要仅有一张脸)
# face_recognition的load_image_file方法会加载图片,并返回一个ndarray类型的数据
# ndarray类型就是NumPy的数组类型,其中的元素类型可以一致也可以不一致
face_path = “C://Users//25103//Desktop//Python人脸识别//face//徐先生.jpg”
image = face_recognition.load_image_file(face_path)

# face_recognition的face_encoding方法,可从返回的ndarray类型数据中提取人脸特征,可同时提取多个特征,返回值为列表类型
# 因为照片中只有一个人脸,所以我们取列表的*个值
face_encoding = face_recognition.face_encodings(image)[0]

while True:
# 从视频对象中读取一帧照片
ret,frame = video_capture.read()
# 将照片缩小,加快处理速度,这里将其缩小为原图的1/4
# frame = cv2.rectangle(frame,(0,0),fx=0.25,fy=0.25)
# 因为cv2用的是BGR色彩,我们组要将其转化为RGB进行处理
rgb_frame = frame[:,:,::-1] # 列表转置操作

# face_recognition的face_location方法可以获取图片中所有人脸的位置,其返回值为一个列表
face_locations = face_recognition.face_locations(rgb_frame)
print(“共从视频中找到了{}张人脸”.format(len(face_locations)))

# 获取视频中所有人脸的特征
face_encodings = face_recognition.face_encodings(rgb_frame,face_locations)

for face in face_encodings:
# 比较两个特征值——encoding1与encoding2,匹配返回True,否则返回False。tolerance越低,顾名思义,容错率越低,返回值为列表类型
match = face_recognition.compare_faces([face_encoding],face,tolerance=0.4)
name = “不认识的人”

if match[0]:
# face为图片名称
name = os.path.basename(face_path[0:-4])
print(“找到了{}”.format(name))

手把手教您Android自定义ViewGroup

手把手教您Android 自定义 ViewGroup

今天给大家带来一篇自定义ViewGroup的教程,说白了,就是教大家如何自定义ViewGroup,如果你对自定义ViewGroup还不是很了解,或者正想学习如何自定义,那么你可以好好看看这篇博客。

1、概述

在写代码之前,我必须得问几个问题:

1、ViewGroup的职责是啥?

ViewGroup相当于一个放置View的容器,并且我们在写布局xml的时候,会告诉容器(凡是以layout为开头的属性,都是为用于告诉容器的),我们的宽度(layout_width)、高度(layout_height)、对齐方式(layout_gravity)等;当然还有margin等;于是乎,ViewGroup的职能为:给childView计算出建议的宽和高和测量模式 ;决定childView的位置;为什么只是建议的宽和高,而不是直接确定呢,别忘了childView宽和高可以设置为wrap_content,这样只有childView才能计算出自己的宽和高。

2、View的职责是啥?

View的职责,根据测量模式和ViewGroup给出的建议的宽和高,计算出自己的宽和高;同时还有个更重要的职责是:在ViewGroup为其指定的区域内绘制自己的形态。

3、ViewGroup和LayoutParams之间的关系?

大家可以回忆一下,当在LinearLayout中写childView的时候,可以写layout_gravity,layout_weight属性;在RelativeLayout中的childView有layout_centerInParent属性,却没有layout_gravity,layout_weight,这是为什么呢?这是因为每个ViewGroup需要指定一个LayoutParams,用于确定支持childView支持哪些属性,比如LinearLayout指定LinearLayout.LayoutParams等。如果大家去看LinearLayout的源码,会发现其内部定义了LinearLayout.LayoutParams,在此类中,你可以发现weight和gravity的身影。

2、View的3种测量模式

上面提到了ViewGroup会为childView指定测量模式,下面简单介绍下三种测量模式:

EXACTLY:表示设置了精确的值,一般当childView设置其宽、高为精确值、match_parent时,ViewGroup会将其设置为EXACTLY;

AT_MOST:表示子布局被限制在一个*大值内,一般当childView设置其宽、高为wrap_content时,ViewGroup会将其设置为AT_MOST;

UNSPECIFIED:表示子布局想要多大就多大,一般出现在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此种模式比较少见。

注:上面的每一行都有一个一般,意思上述不是*对的,对于childView的mode的设置还会和ViewGroup的测量mode有一定的关系;当然了,这是*篇自定义ViewGroup,而且*大部分情况都是上面的规则,所以为了通俗易懂,暂不深入讨论其他内容。

3、从API角度进行浅析

上面叙述了ViewGroup和View的职责,下面从API角度进行浅析。

View的根据ViewGroup传人的测量值和模式,对自己宽高进行确定(onMeasure中完成),然后在onDraw中完成对自己的绘制。

ViewGroup需要给View传入view的测量值和模式(onMeasure中完成),而且对于此ViewGroup的父布局,自己也需要在onMeasure中完成对自己宽和高的确定。此外,需要在onLayout中完成对其childView的位置的指定。

4、完整的例子

需求:我们定义一个ViewGroup,内部可以传入0到4个childView,分别依次显示在左上角,右上角,左下角,右下角。

1、决定该ViewGroup的LayoutParams

对于我们这个例子,我们只需要ViewGroup能够支持margin即可,那么我们直接使用系统的MarginLayoutParams

  1. @Override
  2. public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs)
  3. {
  4. return new MarginLayoutParams(getContext(), attrs);
  5. }

重写父类的该方法,返回MarginLayoutParams的实例,这样就为我们的ViewGroup指定了其LayoutParams为MarginLayoutParams。

2、onMeasure

在onMeasure中计算childView的测量值以及模式,以及设置自己的宽和高:

  1. /**
  2. * 计算所有ChildView的宽度和高度 然后根据ChildView的计算结果,设置自己的宽和高
  3. */
  4. @Override
  5. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
  6. {
  7. /**
  8. * 获得此ViewGroup上级容器为其推荐的宽和高,以及计算模式
  9. */
  10. int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  11. int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  12. int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
  13. int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
  14. // 计算出所有的childView的宽和高
  15. measureChildren(widthMeasureSpec, heightMeasureSpec);
  16. /**
  17. * 记录如果是wrap_content是设置的宽和高
  18. */
  19. int width = 0;
  20. int height = 0;
  21. int cCount = getChildCount();
  22. int cWidth = 0;
  23. int cHeight = 0;
  24. MarginLayoutParams cParams = null;
  25. // 用于计算左边两个childView的高度
  26. int lHeight = 0;
  27. // 用于计算右边两个childView的高度,*终高度取二者之间大值
  28. int rHeight = 0;
  29. // 用于计算上边两个childView的宽度
  30. int tWidth = 0;
  31. // 用于计算下面两个childiew的宽度,*终宽度取二者之间大值
  32. int bWidth = 0;
  33. /**
  34. * 根据childView计算的出的宽和高,以及设置的margin计算容器的宽和高,主要用于容器是warp_content时
  35. */
  36. for (int i = 0; i < cCount; i++)
  37. {
  38. View childView = getChildAt(i);
  39. cWidth = childView.getMeasuredWidth();
  40. cHeight = childView.getMeasuredHeight();
  41. cParams = (MarginLayoutParams) childView.getLayoutParams();
  42. // 上面两个childView
  43. if (i == 0 || i == 1)
  44. {
  45. tWidth += cWidth + cParams.leftMargin + cParams.rightMargin;
  46. }
  47. if (i == 2 || i == 3)
  48. {
  49. bWidth += cWidth + cParams.leftMargin + cParams.rightMargin;
  50. }
  51. if (i == 0 || i == 2)
  52. {
  53. lHeight += cHeight + cParams.topMargin + cParams.bottomMargin;
  54. }
  55. if (i == 1 || i == 3)
  56. {
  57. rHeight += cHeight + cParams.topMargin + cParams.bottomMargin;
  58. }
  59. }
  60. width = Math.max(tWidth, bWidth);
  61. height = Math.max(lHeight, rHeight);
  62. /**
  63. * 如果是wrap_content设置为我们计算的值
  64. * 否则:直接设置为父容器计算的值
  65. */
  66. setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth
  67. : width, (heightMode == MeasureSpec.EXACTLY) ? sizeHeight
  68. : height);
  69. }

10-14行,获取该ViewGroup父容器为其设置的计算模式和尺寸,大多情况下,只要不是wrap_content,父容器都能正确的计算其尺寸。所以我们自己需要计算如果设置为wrap_content时的宽和高,如何计算呢?那就是通过其childView的宽和高来进行计算。

17行,通过ViewGroup的measureChildren方法为其所有的孩子设置宽和高,此行执行完成后,childView的宽和高都已经正确的计算过了

43-71行,根据childView的宽和高,以及margin,计算ViewGroup在wrap_content时的宽和高。

80-82行,如果宽高属性值为wrap_content,则设置为43-71行中计算的值,否则为其父容器传入的宽和高。

3、onLayout对其所有childView进行定位(设置childView的绘制区域)

  1. // abstract method in viewgroup
  2. @Override
  3. protected void onLayout(boolean changed, int l, int t, int r, int b)
  4. {
  5. int cCount = getChildCount();
  6. int cWidth = 0;
  7. int cHeight = 0;
  8. MarginLayoutParams cParams = null;
  9. /**
  10. * 遍历所有childView根据其宽和高,以及margin进行布局
  11. */
  12. for (int i = 0; i < cCount; i++)
  13. {
  14. View childView = getChildAt(i);
  15. cWidth = childView.getMeasuredWidth();
  16. cHeight = childView.getMeasuredHeight();
  17. cParams = (MarginLayoutParams) childView.getLayoutParams();
  18. int cl = 0, ct = 0, cr = 0, cb = 0;
  19. switch (i)
  20. {
  21. case 0:
  22. cl = cParams.leftMargin;
  23. ct = cParams.topMargin;
  24. break;
  25. case 1:
  26. cl = getWidth() – cWidth – cParams.leftMargin
  27. – cParams.rightMargin;
  28. ct = cParams.topMargin;
  29. break;
  30. case 2:
  31. cl = cParams.leftMargin;
  32. ct = getHeight() – cHeight – cParams.bottomMargin;
  33. break;
  34. case 3:
  35. cl = getWidth() – cWidth – cParams.leftMargin
  36. – cParams.rightMargin;
  37. ct = getHeight() – cHeight – cParams.bottomMargin;
  38. break;
  39. }
  40. cr = cl + cWidth;
  41. cb = cHeight + ct;
  42. childView.layout(cl, ct, cr, cb);
  43. }
  44. }

代码比较容易懂:遍历所有的childView,根据childView的宽和高以及margin,然后分别将0,1,2,3位置的childView依次设置到左上、右上、左下、右下的位置。

如果是*个View(index=0) :则childView.layout(cl, ct, cr, cb); cl为childView的leftMargin , ct 为topMargin , cr 为cl+ cWidth , cb为 ct + cHeight

如果是第二个View(index=1) :则childView.layout(cl, ct, cr, cb);

cl为getWidth() – cWidth – cParams.leftMargin- cParams.rightMargin;

ct 为topMargin , cr 为cl+ cWidth , cb为 ct + cHeight

剩下两个类似~

这样就完成了,我们的ViewGroup代码的编写,下面我们进行测试,分别设置宽高为固定值,wrap_content,match_parent

5、测试结果

布局1:

  1. <com.example.zhy_custom_viewgroup.CustomImgContainer xmlns:android=“http://schemas.android.com/apk/res/android”
  2. xmlns:tools=“http://schemas.android.com/tools”
  3. android:layout_width=“200dp”
  4. android:layout_height=“200dp”
  5. android:background=“#AA333333” >
  6. <TextView
  7. android:layout_width=“50dp”
  8. android:layout_height=“50dp”
  9. android:background=“#FF4444”
  10. android:gravity=“center”
  11. android:text=“0”
  12. android:textColor=“#FFFFFF”
  13. android:textSize=“22sp”
  14. android:textStyle=“bold” />
  15. <TextView
  16. android:layout_width=“50dp”
  17. android:layout_height=“50dp”
  18. android:background=“#00ff00”
  19. android:gravity=“center”
  20. android:text=“1”
  21. android:textColor=“#FFFFFF”
  22. android:textSize=“22sp”
  23. android:textStyle=“bold” />
  24. <TextView
  25. android:layout_width=“50dp”
  26. android:layout_height=“50dp”
  27. android:background=“#ff0000”
  28. android:gravity=“center”
  29. android:text=“2”
  30. android:textColor=“#FFFFFF”
  31. android:textSize=“22sp”
  32. android:textStyle=“bold” />
  33. <TextView
  34. android:layout_width=“50dp”
  35. android:layout_height=“50dp”
  36. android:background=“#0000ff”
  37. android:gravity=“center”
  38. android:text=“3”
  39. android:textColor=“#FFFFFF”
  40. android:textSize=“22sp”
  41. android:textStyle=“bold” />
  42. </com.example.zhy_custom_viewgroup.CustomImgContainer>

ViewGroup宽和高设置为固定值

效果图:

%title插图%num

布局2:

  1. <com.example.zhy_custom_viewgroup.CustomImgContainer xmlns:android=“http://schemas.android.com/apk/res/android”
  2. xmlns:tools=“http://schemas.android.com/tools”
  3. android:layout_width=“wrap_content”
  4. android:layout_height=“wrap_content”
  5. android:background=“#AA333333” >
  6. <TextView
  7. android:layout_width=“150dp”
  8. android:layout_height=“150dp”
  9. android:background=“#E5ED05”
  10. android:gravity=“center”
  11. android:text=“0”
  12. android:textColor=“#FFFFFF”
  13. android:textSize=“22sp”
  14. android:textStyle=“bold” />
  15. <TextView
  16. android:layout_width=“50dp”
  17. android:layout_height=“50dp”
  18. android:background=“#00ff00”
  19. android:gravity=“center”
  20. android:text=“1”
  21. android:textColor=“#FFFFFF”
  22. android:textSize=“22sp”
  23. android:textStyle=“bold” />
  24. <TextView
  25. android:layout_width=“50dp”
  26. android:layout_height=“50dp”
  27. android:background=“#ff0000”
  28. android:gravity=“center”
  29. android:text=“2”
  30. android:textColor=“#FFFFFF”
  31. android:textSize=“22sp”
  32. android:textStyle=“bold” />
  33. <TextView
  34. android:layout_width=“50dp”
  35. android:layout_height=“50dp”
  36. android:background=“#0000ff”
  37. android:gravity=“center”
  38. android:text=“3”
  39. android:textColor=“#FFFFFF”
  40. android:textSize=“22sp”
  41. android:textStyle=“bold” />
  42. </com.example.zhy_custom_viewgroup.CustomImgContainer>

ViewGroup的宽和高设置为wrap_content
效果图:

%title插图%num

布局3:

  1. <com.example.zhy_custom_viewgroup.CustomImgContainer xmlns:android=“http://schemas.android.com/apk/res/android”
  2. xmlns:tools=“http://schemas.android.com/tools”
  3. android:layout_width=“match_parent”
  4. android:layout_height=“match_parent”
  5. android:background=“#AA333333” >
  6. <TextView
  7. android:layout_width=“150dp”
  8. android:layout_height=“150dp”
  9. android:background=“#E5ED05”
  10. android:gravity=“center”
  11. android:text=“0”
  12. android:textColor=“#FFFFFF”
  13. android:textSize=“22sp”
  14. android:textStyle=“bold” />
  15. <TextView
  16. android:layout_width=“50dp”
  17. android:layout_height=“50dp”
  18. android:background=“#00ff00”
  19. android:gravity=“center”
  20. android:text=“1”
  21. android:textColor=“#FFFFFF”
  22. android:textSize=“22sp”
  23. android:textStyle=“bold” />
  24. <TextView
  25. android:layout_width=“50dp”
  26. android:layout_height=“50dp”
  27. android:background=“#ff0000”
  28. android:gravity=“center”
  29. android:text=“2”
  30. android:textColor=“#FFFFFF”
  31. android:textSize=“22sp”
  32. android:textStyle=“bold” />
  33. <TextView
  34. android:layout_width=“150dp”
  35. android:layout_height=“150dp”
  36. android:background=“#0000ff”
  37. android:gravity=“center”
  38. android:text=“3”
  39. android:textColor=“#FFFFFF”
  40. android:textSize=“22sp”
  41. android:textStyle=“bold” />
  42. </com.example.zhy_custom_viewgroup.CustomImgContainer>

ViewGroup的宽和高设置为match_parent

%title插图%num

可以看到无论ViewGroup的宽和高的值如何定义,我们的需求都实现了预期的效果~~

好了,通过这篇教程,希望大家已经能够初步掌握自定义ViewGroup的步骤,大家可以尝试自定义ViewGroup去实现LinearLayout的效果~~

关于分布式数据库,你该了解的几件事

随着业务对大数据技术需求的不断演变,分布式数据库在整个生态圈中的地位愈加重要,已可预见必将成为未来大数据技术发展的又一个核心,而其中OLAP(联机分析处理)显得尤其重要。

基本理论

数据库的基本理论ACID

原子性(Atomic)。整个事务中的所有操作要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

一致性(Consistent)。在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。

隔离性(Isolated)。隔离状态执行事务,使它们好像是在给定时间内系统执行的唯一操作。如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。

持久性(Durable)。在事务完成以后,该事务对数据库所作的更改便持久地保存在数据库之中,并不会被回滚。

对于ACID的实现方式主要有两个,一个是日志式的方式(Write ahead logging),几乎所有的数据库系统(MySQL、Oracle等)都基于日志的方式。另外一种是Shadow paging,代表的数据库主要是SQLite,Android或者iOS APP开发的话应该会比较了解,但大型的数据库都不会用到。

%title插图%num

图1 事务隔离性一览

分布式数据库的CAP理论

一致性(C)。分布式系统中所有数据备份在同一时刻的值是否相同。

可用性(A)。当集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求(可用性不仅包括读,还有写)。

分区容忍性(P)。集群中的某些节点无法联系后,集群整体是否还能继续进行服务。

%title插图%num

图2 CAP三大定律

NoSQL分类

如果同时满足这三点,成本将会非常高,所以建议根据业务的具体需求做好平衡选择。把NoSQL做一个简单分类将包括如下几种:

Key/Value 或 ‘the big hash table’。典型的有Amazon S3 (Dynamo)、Voldemort、Scalaris、Memcached (in-memory key/value store)、Redis等。

Schema-less。典型的有Cassandra (column-based)、CouchDB (document-based)、MongoDB(document-based)、Neo4J (graph-based)、HBase (column-based)、ElasticSearch(document-based)等。

OLTP和OLAP的对比分析

目前分布式数据库主要分为两种场景——OLTP(联机事务处理)和OLAP(联机分析处理)。随着大数据技术发展,数据库选择越来越多,其主要差别包括:面向事务还是面向分析;数据内容是当前的、详细的数据还是历史的、汇总的数据;数据库设计是实体联系模型ER和面向应用的数据库设计,还是星型、雪花模型和面向主题的数据库设计等。前者指的是OLTP场景,后者指的是OLAP场景。

表1 OLTP和OLAP对比

%title插图%num

基于分布式数据库的理论,不管是数据库的优化还是设计、使用,遇到的问题非常多。举例说,现在硬件发展很好,特别SSD,如果其设备性能远远没有达到,那么使用SSD的数据库性能该如何提高。如果只是为了满足业务当前的简单需求,可以把现在很多数据库的传输引擎存储直接换成SSD,可以快速地解决很大的问题。另外还有一个很经典的问题,怎么保证在高可靠的前提下提高数据库插入和查询性能。刚才说的是单机模式,多机的分布式模式下又该怎么提高数据调用性能,也有许多挑战。总之,一定要根据业务的需求来选择*合适自己的数据库系统。

分布式数据库实际案例

HBase

在HBase的设计原则中,每个列族可以包含任意多的列,每个列可以有任意多的版本,列只有在有值时才存在,列本身是排序的。

重点看一下Zookeeper的模型,它用了一个非常经典的模型叫Leader/Follower。举个例子说,在去餐厅吃饭时,进餐厅肯定有领班把你领过去,安排到某个座位,点菜则不是他的工作,而由其同事完成,这是非常传统的半同步模型。而Leader/Follower模型是领班把你领过去帮你点菜,他在之前会再选一个Follower做Leader,通过选举制来实现,这样会减少线程的调度,这对数据库的性能会有很大的提升。

%title插图%num

图3 HBase中的功能实现

ElasticSearch(ES)

对于分布式数据库里把ElasticSearch也作为一种分布式数据库是有原因的,如果需要快速查询,但列很多,HBase的SQL支持不太好,使用不方便。而ES对于前端工程师开发非常简单,不需要对分布式数据库内核了解很深就可以很快使用起来,而只需要了解RestfulAPI就可以了,并且也很方便。ES底层都是分布式的Lucene,如Github使用Elasticsearch搜索20TB的数据,包括13亿的文件。ES的模型比较清晰比较简单,就两个步骤,一个步骤是怎么把数据建索引,建完索引主要是做查询,怎么把SQL的语句做查询。

%title插图%num

图4 ElasticSearch亮点

ES*重要的是建索引,每个的记录都会根据需求建索引,这么做有好有坏——如果突然来了100亿条记录,建索引的时间会很长,对于业务索引是不能忍受的。所以如果支持离线建立索引,后面实时增量建索引这样会更好,目前ES这个模型还不能支持。 但是ES时下已发展的比较成熟,现在能对接的接口都能支持,所以是非常方便的。

分布式数据库系统对比

%title插图%num

图5 ElasticSearch功能模块

这里主要对比Pinot和Druid,支持多维度实时查询的分布式系统。

表2 Druid和Pinot功能实现对比

%title插图%num

由于Pinot跟ES系统架构很类似,而Pinot比Druid支持存储格式更多一些,所以我们用Pinot和ES做了一个性能测试对比,测试条件如下:

  • 记录条数分为100亿以内和1000亿条
  • 服务器数量为70台,配置为:CPU 12核,内存96G,硬盘48T
  • 测试语句:select count(*) from test where age > 25 and gender > 0 and os > “500” and sc in (“0001009″,”0002036″,”0016030″,”…”) or bs>585 and group by age,gender,os,bs
  • 总共12列:动态列为3列(多值列),普通列为9列

表3 ElasticSearch和Pinot百亿条检索对比

%title插图%num

表4 ElasticSearch和Pinot千亿条检索对比

%title插图%num

对于Pinot和ES有一个共性,他们都有多值列的属性,即类似的属性可以放入同一列,这样查的话大部分需要把一个列的数据查出来,从而更有益于性能。

真实案例分析

业务需求:

1.每天请求数超过 100 亿

2. 每天增长超过 5TB 级数据

3. 每天对几千亿条记录进行上 1000 种维度的计算

4. 客户有流式、实时、离线需求

图6是系统数据流程。

%title插图%num

图6 系统数据流程

数据采集用WebService,如Nginx;数据收集服务用Kafka和Flume;数据清洗服务Storm,采用Storm主要有下面两个原因,业务需求在毫秒级需要;有严格要求的时间序列,如原来输入是1、2、3、4、5,输出还必须是1、2、3、4、5。其他用Spark Streaming将会比较好。

接下来把Kafka分流出来的数据对应每一条不同的业务,然后导入对应的存储,如HBase、HDFS等,通过不同的流来解决不同的业务问题,然后基于不同存储做各种算法分析;*后将各种结果数据导入ElasticSearch或者MySQL给前端做数据可视化。

通过阅读上述知识相信各位对分布式数据库的发展和不同系统的技术特点已经有了一定的了解,限于篇幅的原因,笔者以分享几个ES的使用心得结束:

1.用 ES 的 Alias 特性实现数据的水平扩展。

2. 用 Kibana 分析和展现数据(ELK三剑客)可以满足很多公司业务80%以上的需求,ELK是指ElasticSearch、Logstash、Kibana,它们分别功能为:ElasticSearch是负责日志检索和分析;Logstash负责日志的收集,处理和储存;Kibana负责日志的可视化,建议用Kibana4版本。

3. 多条件聚合查询,布尔查询。

4. 定制分词插件(IK),实现对特殊字符的精确匹配,目前现在主流的搜索引擎在搜索关键词的时候对标点符号是忽略的,但是在实现一些对监控微博等社交数据时,如果微博里有很多符号,举例来说“:)”其实代表的是笑脸,而笑脸对于我们来判断正负面是非常有用的,所以判断正负面不只是语义分析的,还有对标点符号分析也非常重要。

作者简介:卢亿雷,AdMaster技术副总裁,资深大数据技术专家。关注高可靠、高可用、高扩展、高性能系统服务,关注Hadoop/HBase/Storm/Spark/Flink/ElasticSearch等离线、流式及实时分布式计算技术。

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速