日期: 2021 年 10 月 12 日

有没有公司用 Python 或者 django 搭建集群、微服务成功的案例?

如果有的话请大佬帮忙讲讲怎么实现的:集群或者微服务都用到了什么,用什么做网关、服务之间怎么调用的。

随着公司业务发展,发现一台服务器上部署的项目满足不了需求,想把项目部署到多台服务器上,也想把其中一些业务独立出来解耦,数据库也想搞一个读写分离,现在毫无头绪;

集群 Python django 服务器20 条回复 • 2021-10-12 10:26:26 +08:00
homelab 1
homelab 1 天前
网关可以用 kong,可以用与认证、限流
服务之间用 rpc 调用,但你用 django 是 http 同步框架,只能走 http 相互调用吧
ruanimal 2
ruanimal 1 天前
目前就一台机器就想上集群,小心步子迈太大
676529483 3
676529483 1 天前
我现在用 k8s,相对简单,网关你看需要加,服务注册和负载均衡直接用 svc 和 ingress,用 django 的话就 http 呗。至于读写分离,做好主从同步,django 里配置多个数据库或者数据库路由就行
Ehco1996 4
Ehco1996 1 天前
其实业界已经有很标准的做法了,抄作业的*快办法就是

1 上云买云版本 k8s
2 容器化你们当前的服务
3 slb 直接打到 k8s 的 ingress controller

至于业务 /db 拆分 这就是写代码的事情了, 可以慢慢考虑

—-
但是,如果现在毫无头绪的话建议不要上来就集群微服务

单机性能有瓶颈了先上 nginx 后面拉几台机器负载均衡一下

等心里面有数了(有人力)之后再考虑上微服务集群那一套
arischow 5
arischow 1 天前 via iPhone
现在就一台服务器的单体要开始计划读写分离?需要补充更多语境
podel 6
podel 1 天前
我司就是。
要不要过来投个简历。偷师学艺一下吖~~~[手动滑稽]
主要 python 里面的计算逻辑尽量的少。
某些搜索分流到 ES 之类的。 频繁信息直接内存缓存或者 Redis 。*后数据库存储原始数据。
web 只做 web 的事情,不要做其他事情。
这样子 可以轻松的水平扩容。 (加 WEB POD 数量,加 ES 集群数量,升数据库配置。)
另外阿里云 SLB+ K8S + istio 的 service mesh(不需要可以不用) 轻轻松松水平扩展吖。
longmeier90 7
longmeier90 1 天前
@podel 大佬什么公司呀
longmeier90 8
longmeier90 1 天前
@arischow 是因为财务统计、运营统计数据量大的情况下 cpu 经常报警;想着是读和写分开这样的话可以分担压力
chengxiao 9
chengxiao 1 天前
v2 不就是么
hushao 10
hushao 1 天前
想半天,默默打出一个负载均衡???
gengchun 11
gengchun 1 天前
有 40 个核数以上吗?

看上去是传统的业务,都是物理机的可能性较大,估计后面只有 mysql/mariadb,再加上 redis 就差不多了。这样 100 个核两三台用不到什么集群。nginx + uwsgi 光是 Python 的话,什么都对付过去了。

做点内核参数调整就对付过去了。实在想的话,也就用一下 docker,用 ns 特性做点隔离就差不多了。

数据库读写分离都不需要。搞个主从备份下就好了。早年阿里的中间件现在都不维护了。maxscale 这种东西。复杂业务也要踩坑。

如果不是真心希望向运维方向发展的话,专注了解业务,不要在这上面浪费时间。
ch2 12
ch2 1 天前
先搞清楚无状态计算跟有状态计算再说吧
roundgis 13
roundgis 1 天前 via Android
@longmeier90 如果瓶頸在 db 和 django 關係不大
dayeye2006199 14
dayeye2006199 1 天前
我们在 aws 上的简单配置:
1. 多 EC2 实例部署 django 应用
2. 接个 ASG 保证 HA
3. *前面再接个 API gateway

LZ 的问题听者像是数据库瓶颈的问题。如果经常需要做大量数据统计和报表的功能,一个是考虑优化一下 query 的性能;一个是可以考虑设置定时任务预计算一些常用的报表;另一个是考虑上 OLAP 的数据库,例如我们的 OLTP 用的 postgre,OLAP 用的是相关的 greenplum
christopheredwar 15
christopheredwar 1 天前
同学习中
longmeier90 16
longmeier90 22 小时 53 分钟前
@dayeye2006199 大佬能不能详细说一下 ASG 是个什么东西,从来没听说过,API 网关用的是什么?负载用的什么策略,配置中心用了没有?
podel 17
podel 19 小时 46 分钟前
@longmeier90 一家小公司。只不过技术栈比较新啦。
公司信息[保护隐私,就不直接贴出来了] https://tttemp.oss-cn-shanghai.aliyuncs.com/current-company.txt
longmeier90 18
longmeier90 19 小时 20 分钟前
@podel 你要不要跳槽到俺们公司,眼科医疗行业,前景好、待遇高,加班少
younglinuxer786 20
younglinuxer786 2 小时 44 分钟前
1.看了下帖子,如果数据库和应用都在一台服务器上,先把数据独立放在一台服务器上(磁盘,cpu,men 这些搞好点)应该会好很多
2.如果只是想搞个集群问题不大,微服务要拆分业务太费时间 能保证每个应用下没有单独保存的数据,同时共用数据库 即可实现集群。多个应用之间可以使用 nginx 作为入口 负载到下面的多个应用… (当然这是比较传统的方式 也比较好理解)
3.你也可以考虑将应用打包成 docker 然后使用 k8s 来做集群 一个 pod 启动多个实例 使用 k8s 的 services 来做不同服务的负载 集群要方便管理点(满足第三点的前提)
4.读写分离考虑使用 mycat 或者其他数据库中间件对业务影响不大
5. 看了上面的帖子猜测是统计执行的 sql 造成数据压力有点大 建议先执行*条…

MIUI 的手机目前有啥推荐的吗? k30s 至尊纪念版、k40、还是即将要发布的 k40s?

看大家在用的有 k40 和 mi10s,我再看看
k40 k40s k30s MIUI129 条回复 • 2021-10-12 13:01:38 +08:00
1 2
2
❮ ❯
ioschina1 101
ioschina1 19 小时 22 分钟前
@archiyuan 应用双开不叫系统分身。
系统分身的功能可以参考 8848 的广告语:
一部手机,两个密码,两个空间,分别存储,互不干扰,8848 钛金手机。
生活才能会工作,8848 钛金手机,双密码,双空间,工作生活分别存储,互不干扰。

比如你如果拿个 8848,周围的人都会怀疑你手机里有很多小秘密,但是 MIUI 有 8848 宣传的卖点功能,你的小秘密只有你自己知道
jntitor 102
jntitor 19 小时 12 分钟前
看起来 civi 不错 其他的都太重了
lscexpress 103
lscexpress 19 小时 6 分钟前
选 k40,我手持小米 8 下部就换 k40,因为 888 处理器太拉胯了所以选择次旗舰。
daviddeng 104
daviddeng 19 小时 3 分钟前
@jdhao 个人感觉可以观望一下反馈,等 MIUI 反馈好点再买
user1121114685 105
user1121114685 18 小时 57 分钟前 via Android
只有我建议用 k40 游戏增强版吗? 1 可刷印度固件,2 电池大,3 充电快,4 信号好。5nfc 红外都有。而且天机 1200 性能也够用。简直日用强的一逼呀!
user1121114685 106
user1121114685 18 小时 55 分钟前 via Android
还有一点,解锁不用等 168 个小时。哈哈哈
Torpedo 107
Torpedo 18 小时 51 分钟前
10s,除了重,其他还好
Huelse 108
Huelse 18 小时 41 分钟前
k30s 至尊纪念版在用,一代神 u865+5000 毫安的电池,真的一充一天,有时候能用 2 天,不完游戏不看视频。好像只有线下还有货
archiyuan 109
archiyuan 18 小时 10 分钟前
@ioschina1 我什么时候说应用分身=系统分身了?
archiyuan 110
archiyuan 18 小时 9 分钟前
@user1121114685 刷印度固件有哪些优点啊?
yaojin 111
yaojin 18 小时 6 分钟前
为啥这么多人黑小米, 我觉得挺好用的啊, 经济实惠, 用的 k30pro, 满足日常使用, 现在二手也就 1000 多一点, 不像以前用 iphone 小心翼翼的使用, 现在手机摔了也不心疼, 哈哈, 喜欢 miui 的系统, 真的非常符合国人的使用, iphone 的系统的确好, 但是很多功能的设计都是考虑国外的用户习惯的, 不过 miui 的确有时候有点 bug, 但是不至于没法使用, 而且出现 bug 的几率很低, 不知道为啥这么多人黑, 特别是 B 站, 不知道是不是请的水军
FawkesV 112
FawkesV 18 小时 4 分钟前
@Huelse 就是太重了 我用的也是这个 其他的感觉啥都好 就是重
yeqizhang 113
yeqizhang 17 小时 35 分钟前 via Android
手持 k40 半年,感觉还行吧,12gb 用起来很爽。希望还能继续强势用上三年。
Linon 114
Linon 17 小时 1 分钟前 via Android
k30s
Awes0me 115
Awes0me 16 小时 58 分钟前
@user1121114685

为啥不用等 168 ?
Aaron01 116
Aaron01 16 小时 50 分钟前 via Android
k20pro 用了 2 年多了,没发现什么 bug
az22c 117
az22c 16 小时 45 分钟前
@Huelse 平时中午吃个饭 提个手机太重了
wenjiu 118
wenjiu 16 小时 22 分钟前
供参考:手上有台红米 10x 5g,miui12.5 将近半年多没推送新系统了,目前*新版本 wifi 断流严重(很多人有此 bug 而且都被反应烂了没人处理),官方论坛还有很多人反应别的各种小毛病,得不到处理。
目前百度上搜红米 10x 5g 依然只能搜到各种推荐,性价比之王,怀疑删评论严重。

不知道是因为不是旗舰机没人权还是因为各个机器体质不一样。
amirobotics 119
amirobotics 14 小时 52 分钟前 ❤️ 1
我曾经看到其他人投诉 MIUI bug,都认为是黑粉。

直到我遇到这些 miui bug 后才深刻体会。

比如 notification,

1. 老板通过 WhatsApp 催工作进度。
2. 老母亲通过短信慰问。
3. 各位大佬在 telegram 发讯息。

*后会看到一个通知是:“老板 whatsapp 的头像”+“老母亲的电话号码”+“telegram 的讯息”。

没错,whatsapp,telegram,message 的通知全都搞砸了,都混在一起,差不多 2 个月多的时间才要修复。真的非常影响体验,基本生活都搞砸了,分不清谁是谁。

此外,还有 notification bubble 的 bug,launcher 在 6 inch 的屏幕里,只能有 3 inch 的操作界面。

MIUI 广告那些的都是小问题,我能手动解决。但是那些 bug 的修复速度让我觉得,下一台电话不会是小米了。

*后想说,MIUI 有 bug,顶多影响体验。但雷军造车,刹车如果失灵,后果可是人命。MIUI 都做不好,还想造车?倒不如直接造宇宙飞船。
zpxshl 120
zpxshl 14 小时 33 分钟前 via Android ❤️ 1
@yaojin 为什么这么不喜欢小米呢。119 楼就是一个例子。你没遇到的问题不代表别人没遇到,正常反馈问题总被怀疑是水军或者黑粉。
就这质量还怀疑被水军黑,真是相当自信。 就 miui 这质量,几年前就 bug 满天飞了,反馈渠道还失灵。
tanranran 121
tanranran 14 小时 23 分钟前
有一说一,多年 MIUI 的粉,手持小米 10 pro 512G 版本,发热真的是越来越严重,服了
mengqingshuise 122
mengqingshuise 14 小时 16 分钟前
@AKAMichael 我说怎么我的谷歌账号登不上,原来是升级系统的事。
Huelse 123
Huelse 13 小时 26 分钟前
@FawkesV #112
@az22c #117
的确重,毕竟 216g,个人觉得不用手机壳会舒服点

现在看 13pro 也有 203g,就还好
bigwhite 124
bigwhite 13 小时 22 分钟前 ❤️ 1
bilibili 有一个吐槽小米 11 的视频火爆了,我以为不会有人推荐小米 11 呢。。
superBearL 125
superBearL 1 小时 51 分钟前
为什么想不开呢,建议看看 B 站《小米圣经》
rexmann 126
rexmann 1 小时 45 分钟前
k30s 在手,除了重一点没太多问题,也没遇到多少 bug,但是*近有点发热,不知道是因为动态桌面还是 5G 的原因
zkhhkz123 127
zkhhkz123 1 小时 36 分钟前
@sxox P50 标准版是直屏的 其他配置也有大杯水准 可惜是 4G
user1121114685 128
user1121114685 2 分钟前 via Android
@archiyuan 无广告,全套谷歌框架,稳定些,安装软件什么的骚扰也少
user1121114685 129
user1121114685 1 分钟前 via Android
@Awes0me 因为 mtk 的 cpu 可以强解 bl 。

关于 Java 学习微服务或分布式学习路径的问题

目前学习完了 Springboot 一些框架,一些 redis,es 等框架。想进一步学习 Java 微服务或分布式等!看了部分的 Spring Cloud 的相关介绍,技术太多,而且有些已经过时,不知道学习那种比较好,所以希望大佬指导一下后面的学习路径!*好是列出相关的技术栈

学习 Java 分布式 路径9 条回复 • 2021-10-12 09:28:05 +08:00
ming168 1
ming168 14 小时 3 分钟前
+1
pengtdyd 2
pengtdyd 13 小时 56 分钟前
别卷了,不值得
546L5LiK6ZOt 3
546L5LiK6ZOt 13 小时 12 分钟前 ❤️ 3
这里够学的了: http://icyfenix.cn/
Wien 4
Wien 12 小时 47 分钟前
@546L5LiK6ZOt 周志明大佬,著作 Java 虚拟机必读书籍
xuanbg 5
xuanbg 12 小时 34 分钟前 ❤️ 1
具体的应用和工具使用方法有什么可学的。。。
1 、为什么需要微服务
2 、微服务是如何解决他需要解决的问题的
搞清楚这两个点就足够了。
RayDG 6
RayDG 10 小时 20 分钟前 via Android
可以看看黑马*新的微服务视频,会对微服务有个比较全面的认识
RayDG 7
RayDG 10 小时 19 分钟前 via Android
@RayDG 忘了说,是 B 站视频
yogogo 8
yogogo 3 小时 47 分钟前
框架看多了也没啥用,还是得上手才行。看看 Java 基础还有用一点
wangpugod2003 9
wangpugod2003 2 小时 2 分钟前
B 站油管找个 JAVA 语言的基于微服务的系统,跟着做就行了。
一般现在主流的有两个路线,一个是完全的 JAVA 栈,spring cloud 框架; 一个是 k8s,应用 java 写,docker 镜像部署到 K8s 集群。
看看自己想学哪个。

1password 太烂了,为何这么多人开车?

1,一共长期使用过三款密码管理器,分别是:lastpass,1password,bitwarden,其他的只试用了下。
2,但从编程的角度来说,lastpass,bitwarden 都很好,1password,是*差的,bug *多,尤其是 pc 客户端和网页端,这样的软件水平,我怀疑在国内也要丢饭碗。
3,从功能的角度来讲,lastpass 无疑是*激进的,1password 和 lastpass 都采用的入侵式对直接在输入框旁边进行提醒,1password 硕大的提醒栏经常对用户正常使用造成干扰,1password 甚至还让 mini 客户端跟送浏览器插件一起启动,这太让人恶心了,lastpass 的提醒比较小,基本不会造成干扰,bitwarden 只是在浏览器插件上显示数字提醒,刚开始不太适应,但用的久的反而喜欢这种简洁的提醒方式。
4,1password 在 pc 端使用的逻辑流程混乱,客户端,网页端,浏览器插件端,来回的跳,相比 BITwarden 的简洁高效简直天壤之别,BITwarden 仅仅使用浏览器插件能完成 90%的工作,只有涉及一些账户问题才会跳转网页端。
5,为什么 1password 开车的人*多,我现在明白这仅仅是这个公司营销做得好而已,先卖高价,然后采用送一年,打半折等各种方式促销,让用户觉得赚了便宜。但实际上针对用户体验来说,我个人认为 bitwarden 才是做的*好的。
password bitwarden lastpass 浏览器125 条回复 • 2021-10-12 10:51:00 +08:00
1 2
2
❮ ❯
iCUStOin 101
iCUStOin 12 小时 45 分钟前 via Android
我也更喜欢 bitwarden, 另外 lastpass 的移动端应用程序是一个套壳浏览器挺令人不愉快的。
rekulas 102
rekulas 12 小时 44 分钟前
在用 lastpass,因为担心有些风险自己本地额外加了层加密,安全性升级脱裤也不怕所以敢存储敏感信息
https://github.com/del-xiong/lastpass_dopass

https://camo.githubusercontent.com/e543c84f03b4829e1b2c15c8c259070fb39adef3de0d5ce348e0e75322d02f95/687474703a2f2f70686f746f7a6f6f6d2d7374617469632e73746f722e73696e616170702e636f6d2f322e6a7067

缺点是要麻烦些,也没有做手机端
zhjits 103
zhjits 12 小时 40 分钟前
为啥那么多人开车,那是因为友商衬托得好啊。

*早用的 LastPass,当时它连个存 TOTP 的功能都没有,有了 1Password 以后果断叛逃了。
BitWarden 也用过,那个交互逻辑,尤其是创建了 org 以后那些 org 里面的条目是否能搜到都不好说,莫名其妙的……

1Password 你不要用那个纯 JS 的浏览器插件,用跟原生应用通信的老版本就会好很多。当然 Windows 还是二等公民,那 .Net 客户端小细节问题很多,还是 macOS 客户端用着舒服……
haoliang 104
haoliang 12 小时 7 分钟前
翻了两页,只有一位提到了 [pass]( https://www.passwordstore.org),我想再增加一次曝光。

多年前我使用过两年的 lastpass,爆出安全事故后,遍找替代品,*后安顿在 pass 上:linux 上使用 pass,android 上使用 password store + openKeychain ;虽然不能自动填入,但胜在安全性。pass 的密码库使用 git 管理,具体文件内容使用 gpg 加密。
WebKit 105
WebKit 12 小时 4 分钟前 via Android
1p 没遇到过你说的问题,相反 bt 感觉非常难用,几次想转 bt,用了几次后还是放弃。你喜欢的 bt 在浏览器上提醒我感觉非常难用,比较喜欢 1p 的 mini 窗口方式。
skiy 106
skiy 11 小时 59 分钟前
已经弃 lastpass 了。免费的只能登录一个终端。(之前不是这样的,上个月发现 WEB 端和手机端 只能二选一,网页端还老是登录不上。索性弃用了。)

目前用 keepassxc + syncthing 同步。不好的地方是只能 PC 端,不支持手机和网页,数据库也是本地的。
xiparos 107
xiparos 11 小时 42 分钟前
1password 在 macOS 上用户体验还不错,回头试试你说的 bitwarden
bigwhite 108
bigwhite 11 小时 41 分钟前
对我来说只需要在电脑网页上登录,所以 lastpass 免费版够用,挺好用的, 没觉得有啥问题
smilingsun 109
smilingsun 11 小时 7 分钟前 via Android
刚发现 Firefox Nightly on Android 可以装 Bitwarden 的 addon,自动填充再也不用担心 Bitwarden 被杀后台了。
leverestfish 110
leverestfish 11 小时 6 分钟前
@dingwen07 居然还有这种功能,nb !
tomari 111
tomari 11 小时 3 分钟前 via iPhone
Bitwarden 用了半年,前几天发现公司免费领 3 年 1password,立马换了,真的舒服。能在输入框旁边就出现密码选择,和 chrome 自带密码的逻辑一样,这几天还支持了 ipad 和 iOS Safari extension,体验是很棒的。
edwinxe2v 112
edwinxe2v 10 小时 19 分钟前
Windows 的话,用 KeePass 本地存储,家配套的 Chrome 插件*好用。
dreamramon 113
dreamramon 5 小时 37 分钟前
bitwarden 存附件,截图什么的,即使自己部署,付费了都有容量限制,这个大家遇到过吗。。。
TossPig 114
TossPig 4 小时 24 分钟前
@dreamramon 肯定用 bitwarden_rs 呀,自己部署自用
vonsis 115
vonsis 3 小时 9 分钟前
@skfu 当年我从 Lastpass 转到 1Password,就是因为那时爆出 Lastpass 被脱裤,把我给气了个半死。改掉所有重要密码,并迁移到乘机打折的 1Password 上。
这每年 30 多刀,也是很肉疼的,但回过头你想想:这种软件的公司舒舒服服赚着这个钱,但凡敢出一点安全性上的问题,我就不可能再给他一个钢镚了。
f2ck 116
f2ck 2 小时 18 分钟前
信仰无敌,国外的月亮圆又圆,觉得密码放着国外安全。
ccppgo 117
ccppgo 2 小时 11 分钟前
@Andreas8 你说的这个东西没有 Windows 客户端吗
WebKit 118
WebKit 2 小时 1 分钟前
@f2ck #116 主要国内也没有能用的啊。。
darksora 119
darksora 1 小时 51 分钟前
现在换成 keepass 了,用坚果云同步一下。
之前用的 lastpass 。
Frauhling 120
Frauhling 1 小时 28 分钟前
我是 7 年 1password 老用户;办公时多浏览器混用,不加插件;日常跨平台存储靠 Dropbox 同步。Windows 下 Dropbox 挂到 OneDrive 上进行双重备份。
对我而言,1password 只用来生成密码及记录各项密码相关信息,足够日常使用。But 现在这么多人开车吗?楼主是不是要确认下自己因为信息茧房产生了焦虑?
jameswush 121
jameswush 56 分钟前 via iPhone
bitwarden 免费版够用,想安全的话在 nas 上自己搭也方便
ohwind 122
ohwind 47 分钟前
@f2ck 属实脑子沾点,你难道觉得国内有信得过的公司放密码?而且密码管理器就一定要放别人服务器?
ohwind 123
ohwind 46 分钟前
@f2ck 而且密码管理器许多都是开源的,懂开源啥意思不?
skfu 124
skfu 34 分钟前
@Frauhling #120 事实就是 pc 客户端和网页端使用体验*差,不能说用的久,习惯了,他就没问题了,农村的大娘大爷活了 60 年了,还天天念叨党的政策好,只有党才能救中国呢,真相可不是这么回事。
sicifus 125
sicifus 29 分钟前
我用 keepass+webdav 同步

MySQL 1000 万数据如何*快全量迁移?

自建的数据库(大概 1000 万条数据,2M 带宽),现在想迁移到阿里云的 RDS 上,如何用*少的时间迁移过去?

用了阿里云的数据迁移服务 DTS,非常的慢,而且一度把源服务器上的带宽占满,导致整个网站挂了。

问一问,大家有没有比较好的解决方法呢?

迁移 带宽 MySQL RDS23 条回复 • 2021-10-12 10:58:38 +08:00
taobibi 1
taobibi 19 小时 4 分钟前
虽然没解答,水 1 楼
https://v2ex.com/t/806269
我这边要迁移几千万个图片现在还在犯愁中
blackeeper 2
blackeeper 18 小时 59 分钟前
在阿里云的服务器上建一个从库,怎么迁移都没问题,又不影响你自建的主库
xiao109 3
xiao109 18 小时 55 分钟前
本地先切成小库,再分批迁移过去
blue7wings 4
blue7wings 18 小时 48 分钟前
@blackeeper 建立一个从库,历史数据还是要导入过去的吧,我现在想直接把这部分数据导入过去就可以了。
blackeeper 5
blackeeper 18 小时 41 分钟前
@blue7wings 你这个问题就是限速,避免影响现有的服务呗。你可以导出 SQL,然后压缩、用 rsync 限速传输到阿里云服务器就可以了
myd 6
myd 18 小时 3 分钟前
1. 导出 SQL 文件
2. 导入到新库
3. 配置主从(新库为从库)
4. 等到主从延迟为 0 的时候,停掉业务服务器
5. 应用这边切换数据库到新库
6. 启动业务
Xusually 7
Xusually 17 小时 38 分钟前
1 、做好方案,操作顺序捋清楚。
2 、临时升级带宽到满足迁移需要。
3 、迁移。
4 、带宽回退到 2M 。

花不了多少钱。
gengchun 8
gengchun 16 小时 19 分钟前
PB 一级的话,可以考虑阿里的离线迁移服务。比这小的话,其实可以试试看。*少也要几 TB 吧。

当然,没有到 TB,还是直接升带宽吧。
defunct9 9
defunct9 15 小时 35 分钟前 via iPhone
很简单啊,做主从。慢慢来就行了
netnr 10
netnr 15 小时 19 分钟前 via Android
开一台同区域的临时宽带按量计费的机子,再做端口转发
bing0 11
bing0 15 小时 16 分钟前
添加一块网卡 1G,好像可以找客服申请 5G 口,按量付费,迁移完后删除。
moult 12
moult 15 小时 9 分钟前
自建的数据库的服务商有没有云存储卖?有的话继续往下看。
导出 SQL 文件 – 打压缩包 – 通过内网传输到云存储上 – 开一台阿里云的按量付费实例 – 下载 SQL 压缩包 – 解压 – 通过内网导入 RDS
整个过程一小时肯定能搞定,因为云存储服务可以按流量计费,基本都不限速的。
Felldeadbird 13
Felldeadbird 13 小时 37 分钟前
内网迁移,服务器高度写 1000 万数据很快就完成了。 当然,不知道你 1000 万数据有什么内容。5G SQL 文件服务迁移大概 10 分钟完成。
bybyte 14
bybyte 10 小时 41 分钟前
物理迁移
helloworld000 15
helloworld000 10 小时 31 分钟前
直接邮寄硬盘
Junzhou 16
Junzhou 6 小时 36 分钟前 via iPhone
备份,然后 srync 或者临时带宽升级
jsrgqinbin 17
jsrgqinbin 2 小时 57 分钟前
阿里有免费的数据迁移的服务的,你找 DTS 这个产品看下
BeijingBaby 18
BeijingBaby 2 小时 50 分钟前
这么小的数据,根本不是问题啊。
qwerthhusn 19
qwerthhusn 2 小时 45 分钟前
2M 带宽,说明此站流量不大。
那为啥不把公网 IP 转成按量的,然后使用共享流量包呢?
可以用 200M 带宽,只收个流量包的费用
cxh116 20
cxh116 2 小时 29 分钟前 ❤️ 1
1. XtraBackup 备份导出.
2. 上传备份文件 OSS.
3. 在机器上面拉取 OSS 的文件(走内网,不限速的).
4. 使用 XtraBackup 恢复.
5. 查看 XtraBackup 的备份文件里的信息,设从库连到主库,等从库跟上主库.
6. 同步完成,从库转主库.
jixiangqd 21
jixiangqd 29 分钟前
@cxh116 阿里云 RDS 现在支持从 oss 导入 xtrabackup 的备份了
wuwukai007 22
wuwukai007 22 分钟前
直接把 mysql/data 压缩发过去
zhengxiaowai 23
zhengxiaowai 20 分钟前
#6 + #7 应该是*快的
导出 sql 上传到 oss,起一个机器从 oss 下载 然后导入 mysql

Android 屏幕适配(二)增强版百分比布局库(percent-support-lib)

Android 屏幕适配(二)增强版百分比布局库(percent-support-lib)

一 概述

上周一我们发布了Android 百分比布局库(percent-support-lib) 解析与扩展中对percent-support这个库进行了解析和添加了PercentLinearLayout的支持。

那么为什么本篇博客的存在的意义是什么呢?

首先我们回顾下百分比布局库的用法,提供了PercentRelativeLayoutPercentFrameLayout供大家在编写的时候,对于以下属性:

layout_widthPercentlayout_heightPercent
layout_marginPercentlayout_marginLeftPercent
layout_marginTopPercentlayout_marginRightPercent
layout_marginBottomPercentlayout_marginStartPercentlayout_marginEndPercent

可以使用百分比进行设置宽、高、边距,的确给我们在适配上提供了*大的便利,但是在使用过程中,觉得存在一些场景无法得到满足。什么场景呢?下面我举几个例子。

  1. 当使用图片时,无法设置宽高的比例

    比如我们的图片宽高是200*100的,我们在使用过程中我们设置宽高为20%、10%,这样会造成图片的比例失调。为什么呢?因为20%参考的是屏幕的宽度,而10%参考的是屏幕的高度。

  2. 很难使用百分比定义一个正方形的控件

    比如,我现在界面的右下角有一个FloatingActionButton,我希望其宽度和高度都为屏幕宽度的10%,很难做到。

  3. 一个控件的margin四个方向值一致

    有些时候,我设置margin,我希望四边的边距一致的,但是如果目前设置5%,会造成,上下为高度的5%,左右边距为宽度的5%。

综合上述这些问题,可以发现目前的percent-support-lib并不能完全满足我们的需求,所以我们考虑对其进行扩展。说白了,我们就希 望在布局的时候可以自己设定参考看度还是高度,比如上述2,我们对于宽高可以写成10%w,10%w。也就是在不改变原库的用法的前提下,添加一些额外的 支持。


二 扩展的功能

目前我初步对该库进行了改写,github地址:android-percent-support-extend,对于官方库,做了如下的改变:

  1. 不改变原有库的用法
  2. 添加了PercentLinearLayout
  3. 支持百分比指定特定的参考值,比如宽度或者高度。

    例如:app:layout_heightPercent="50%w"app:layout_marginPercent="15%w",
    app:layout_marginBottomPercent="20%h".

  4. 支持通过app:layout_textSizePercent设置textView的textSize
  5. 对于外层套ScrollView的问题,目前可以在PercentLinearLayout的外层使用ScrollView,不过对于宽度的百分比参考的就是android.R.id.content的高度(因为,无法参考父控件的高度,父控件的高度理论上依赖于子View高度,且模式为UNSPECIFIED)。

对于如何导入,也是相当的简单,android studio的用户,直接:

dependencies {
    //...
    compile 'com.zhy:percent-support-extends:1.0.1'
}

不需要导入官方的percent-support-lib了。

对于的三个类分别为:

com.zhy.android.percent.support.PercentLinearLayout
com.zhy.android.percent.support.PercentRelativeLayout
com.zhy.android.percent.support.PercentFrameLayout

对于eclipse的用户:github上自行下载源码,就几个类和一个attrs.xml,也可以在bintray.com/percent-support-extends 下载相关文件。

下面看几个具体的示例。


三 具体的示例

Demo 1

%title插图%num

xml:

<?xml version="1.0" encoding="utf-8"?>


<com.zhy.android.percent.support.PercentFrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.zhy.android.percent.support.PercentFrameLayout
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_gravity="center"
        android:background="#ff44aacc"
        app:layout_heightPercent="50%w"
        app:layout_widthPercent="50%w">

        <com.zhy.android.percent.support.PercentFrameLayout
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_gravity="center"
            android:background="#ffcc5ec7"
            app:layout_heightPercent="50%w"
            app:layout_widthPercent="50%w">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                android:background="#ff7ecc16"
                android:gravity="center"
                android:text="margin 15% of w"
                app:layout_marginPercent="15%w"
                />

        </com.zhy.android.percent.support.PercentFrameLayout>

    </com.zhy.android.percent.support.PercentFrameLayout>

    <TextView android:layout_width="0dp"
              android:layout_height="0dp"
              android:layout_gravity="bottom|right"
              android:background="#44ff0000"
              android:gravity="center"
              android:text="15%w,15%w"
              app:layout_heightPercent="15%w"
              app:layout_marginPercent="5%w"
              app:layout_widthPercent="15%w"/>


</com.zhy.android.percent.support.PercentFrameLayout>

Demo 2

%title插图%num

xml:

<?xml version="1.0" encoding="utf-8"?>
<com.zhy.android.percent.support.PercentRelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clickable="true">

    <TextView
        android:id="@+id/row_one_item_one"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_alignParentTop="true"
        android:background="#7700ff00"
        android:text="w:70%,h:20%"
        android:gravity="center"
        app:layout_heightPercent="20%"
        app:layout_widthPercent="70%"/>

    <TextView
        android:id="@+id/row_one_item_two"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_toRightOf="@+id/row_one_item_one"
        android:background="#396190"
        android:text="w:30%,h:20%"
        app:layout_heightPercent="20%"
        android:gravity="center"
        app:layout_widthPercent="30%"/>


    <ImageView
        android:id="@+id/row_two_item_one"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:src="@drawable/tangyan"
        android:scaleType="centerCrop"
        android:layout_below="@+id/row_one_item_one"
        android:background="#d89695"
        app:layout_heightPercent="70%"/>

    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_below="@id/row_two_item_one"
        android:background="#770000ff"
        android:gravity="center"
        android:text="width:100%,height:10%"
        app:layout_heightPercent="10%"
        app:layout_widthPercent="100%"/>


</com.zhy.android.percent.support.PercentRelativeLayout>

ok,例子都比较简单,主要就一个布局文件,可以看出上述我们可以给宽度、高度,边距等指定参考值为宽度或者高度。这样的话,在保证图片宽、高比例、控件设置为正方形等需求就没问题了。


接下来还有个例子,功能主要是设置TextView对于textSize的百分比设置;以及对于ScrollView的支持。当然了,对于ScrollView的支持,这个理论上是不支持的,因为大家都清楚,如果PercentLinearLayout在ScrollView中,那么高度的模式肯定是UNSPECIFIED, 那么理论上来说高度是无限制的,也就是依赖于子View的高度,而百分比布局的高度是依赖于父View的高度的,所有是互斥的。而我们支持是:考虑到编写 代码的时候,大多参考的是屏幕高度(android.R.id.content)的高度,所以如果在ScrollView中,编写10%h,这个百分比是 依赖于屏幕高度的(不包括ActionBar的高度)。

Demo 3

%title插图%num

xml:

<?xml version="1.0" encoding="utf-8"?>

<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.zhy.android.percent.support.PercentLinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="#ff44aacc"
            android:gravity="center"
            android:text="width:60%,height:5%,ts:3%"
            android:textColor="#ffffff"
            app:layout_heightPercent="5%"
            app:layout_marginBottomPercent="5%"
            app:layout_textSizePercent="3%"
            app:layout_widthPercent="60%"/>

        <TextView
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="#ff4400cc"
            android:gravity="center"
            android:text="width:70%,height:10%"
            android:textColor="#ffffff"
            app:layout_heightPercent="10%"
            app:layout_marginBottomPercent="5%"
            app:layout_widthPercent="70%"/>
        <TextView
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="#ff44aacc"
            android:gravity="center"
            android:text="w:80%,h:15%,textSize:5%"
            android:textColor="#ffffff"
            app:layout_heightPercent="15%"
            app:layout_marginBottomPercent="5%"
            app:layout_textSizePercent="5%"
            app:layout_widthPercent="80%"/>
        <TextView
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="#ff4400cc"
            android:gravity="center"
            android:text="width:90%,height:5%"
            android:textColor="#ffffff"
            app:layout_heightPercent="20%"
            app:layout_marginBottomPercent="5%"
            app:layout_widthPercent="90%"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:background="#ff44aacc"
            android:gravity="center"
            android:text="width:100%,height:25%"
            android:textColor="#ffffff"
            app:layout_heightPercent="25%"
            app:layout_marginBottomPercent="5%"
            />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:background="#ff44aacc"
            android:gravity="center"
            android:text="width:100%,height:30%"
            android:textColor="#ffffff"
            app:layout_heightPercent="30%"
            app:layout_marginBottomPercent="5%"
            />


    </com.zhy.android.percent.support.PercentLinearLayout>
</ScrollView>

上面的第三个TextView的字体设置的就是5%(默认参考容器高度)。整个PercentLinearLayout在ScrollView中。ok~ 姑且这样,由于源码比较简单,大家可以根据自己的实际需求去修改,前提尽可能不要改变原有的功能。


四 扩展的相关源码

(一) 关于attrs.xml

原库中所有的属性的format为fraction,但是由于我期望的写法有10%w,10%h,10%,没有找到合适的format,就直接定义为string了~string我可以自己去解析~

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="PercentLayout_Layout">
        <attr name="layout_widthPercent" format="string"/>
        <attr name="layout_heightPercent" format="string"/>
        <attr name="layout_marginPercent" format="string"/>
        <attr name="layout_marginLeftPercent" format="string"/>
        <attr name="layout_marginTopPercent" format="string"/>
        <attr name="layout_marginRightPercent" format="string"/>
        <attr name="layout_marginBottomPercent" format="string"/>
        <attr name="layout_marginStartPercent" format="string"/>
        <attr name="layout_marginEndPercent" format="string"/>
        <attr name="layout_textSizePercent" format="string"/>
    </declare-styleable>
</resources>

(二) 获取自定义属性的值及使用

如果看了上篇博文的话,应该清楚,对于自定义属性的值是在PercentLayoutHelper.getPercentLayoutInfo(c, attrs)中获取的。
简单看下修改后的代码:


    public static PercentLayoutInfo getPercentLayoutInfo(Context context,                                                    AttributeSet attrs)
    {
        PercentLayoutInfo info = null;
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PercentLayout_Layout);

        String sizeStr = array.getString(R.styleable.PercentLayout_Layout_layout_widthPercent);
        PercentLayoutInfo.PercentVal percentVal = getPercentVal(sizeStr, true);
        if (percentVal != null)
        {
            if (Log.isLoggable(TAG, Log.VERBOSE))
            {
                Log.v(TAG, "percent width: " + percentVal.percent);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.widthPercent = percentVal;
        } 

        //省略了获取其他的类似属性
        array.recycle();
        return info;
    }


    private static final String REGEX_PERCENT = "^(([0-9]+)([.]([0-9]+))?|([.]([0-9]+))?)%([wh]?)$";

    /**
     * widthStr to PercentVal
     * <br/>
     * eg: 35%w => new PercentVal(35, true)
     *
     * @param percentStr
     * @param isOnWidth
     * @return
     */
    private static PercentLayoutInfo.PercentVal getPercentVal(String percentStr, boolean isOnWidth)
    {
        //valid param
        if (percentStr == null)
        {
            return null;
        }
        Pattern p = Pattern.compile(REGEX_PERCENT);
        Matcher matcher = p.matcher(percentStr);
        if (!matcher.matches())
        {
            throw new RuntimeException("the value of layout_xxxPercent invalid! ==>" + percentStr);
        }
        int len = percentStr.length();
        //extract the float value
        String floatVal = matcher.group(1);
        String lastAlpha = percentStr.substring(len - 1);

        float percent = Float.parseFloat(floatVal) / 100f;
        boolean isBasedWidth = (isOnWidth && !lastAlpha.equals("h")) || lastAlpha.equals("w");

        return new PercentLayoutInfo.PercentVal(percent, isBasedWidth);
    }

首先我们获取自定义属性的填写的值,通过getPercentVal方法,在该方法内部通过正则校验其合法性,如果合法,则将其拆解封装成 PercentVal对象,该对象中记录百分比值,已经知否参考宽度的布尔值(如果参考宽度则为true,否则为false)。对于没有后缀w|h的,和 原库的解析方式相同。

PercentVal对象如下:

public static class PercentVal
{

     public float percent = -1;
     public boolean isBaseWidth;

     public PercentVal(float percent, boolean isBaseWidth)
     {
          this.percent = percent;
          this.isBaseWidth = isBaseWidth;
     }
}       

对于定义的自定义属性获取完成之后,剩下的无非是测量时候对于原本的LayoutParams中的宽度和高度的赋值做简单的修改。参考上一篇的源码,我们直接看 PercentLayoutInfo.fillLayoutParams(params, widthHint, heightHint);方法:


 public void fillLayoutParams(ViewGroup.LayoutParams params, int widthHint,
                                     int heightHint)
        {
            // Preserve the original layout params, so we can restore them after the measure step.
            mPreservedParams.width = params.width;
            mPreservedParams.height = params.height;
            /*
            if (widthPercent >= 0) {
                params.width = (int) (widthHint * widthPercent);
            }
            if (heightPercent >= 0) {
                params.height = (int) (heightHint * heightPercent);
            }*/
            if (widthPercent != null)
            {
                int base = widthPercent.isBaseWidth ? widthHint : heightHint;
                params.width = (int) (base * widthPercent.percent);
            }
            if (heightPercent != null)
            {
                int base = heightPercent.isBaseWidth ? widthHint : heightHint;
                params.height = (int) (base * heightPercent.percent);
            }

            if (Log.isLoggable(TAG, Log.DEBUG))
            {
                Log.d(TAG, "after fillLayoutParams: (" + params.width + ", " + params.height + ")");
            }
        }

原本的源码比较简单,只需要将widthHint/heightHint乘以百分比即可(见上代码注释),而我们修改的也比较容易,首先判断参考宽度还是高度,然后乘以百分比(根据我们的对象PercentVal的属性)。

ok,大概的源码修改就是上述的内容,有兴趣的可以直接查看源码。

当然了,上述库中肯定还存在或多或少的问题,大家可以fork完善下,或者直接留言提意见都可以。

Android 屏幕适配(一)百分比布局库(percent-support-lib) 解析与扩展

一、概述

发现android-percent-support-lib-sample这个项目,Google终于开始支持百分比的方式布局了,瞬间脉动回来,啊咧咧。对于这种历史性的时刻,不出篇文章难以表达我内心的激动。

还记得不久前,发了篇文:Android 屏幕适配方案,这篇文章以Web页面设计引出一种适配方案,*终的目的就是可以通过百分比控制控件的大小。当然了,存在一些问题,比如:

  • 对于没有考虑到屏幕尺寸,可能会出现意外的情况;
  • apk的大小会增加;

当然了android-percent-support这个库,基本可以解决上述问题,是不是有点小激动,稍等,我们先描述下这个support-lib。

这个库提供了:

  • 两种布局供大家使用:
    PercentRelativeLayoutPercentFrameLayout,通过名字就可以看出,这是继承自FrameLayoutRelativeLayout两个容器类;
  • 支持的属性有:

layout_widthPercentlayout_heightPercent
layout_marginPercentlayout_marginLeftPercent
layout_marginTopPercentlayout_marginRightPercent
layout_marginBottomPercentlayout_marginStartPercentlayout_marginEndPercent

可以看到支持宽高,以及margin。

也就是说,大家只要在开发过程中使用PercentRelativeLayoutPercentFrameLayout替换FrameLayoutRelativeLayout即可。

是不是很简单,不过貌似没有LinearLayout,有人会说LinearLayout有weight属性呀。但是,weight属性只能支持一个方向呀~~哈,没事,刚好给我们一个机会去自定义一个PercentLinearLayout

好了,本文分为3个部分:

  • PercentRelativeLayoutPercentFrameLayout的使用
  • 对上述控件源码分析
  • 自定义PercentLinearLayout

二、使用

关于使用,其实及其简单,并且github上也有例子,android-percent-support-lib-sample。我们就简单过一下:

首先记得在build.gradle添加:

 compile 'com.android.support:percent:22.2.0'

(一)PercentFrameLayout

<?xml version="1.0" encoding="utf-8"?>
<android.support.percent.PercentFrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_gravity="left|top"
        android:background="#44ff0000"
        android:text="width:30%,height:20%"
        app:layout_heightPercent="20%"
        android:gravity="center"
        app:layout_widthPercent="30%"/>

    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_gravity="right|top"
        android:gravity="center"
        android:background="#4400ff00"
        android:text="width:70%,height:20%"
        app:layout_heightPercent="20%"
        app:layout_widthPercent="70%"/>


    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_gravity="bottom"
        android:background="#770000ff"
        android:text="width:100%,height:10%"
        android:gravity="center"
        app:layout_heightPercent="10%"
        app:layout_widthPercent="100%"/>


</android.support.percent.PercentFrameLayout>

3个TextView,很简单,直接看效果图:

%title插图%num


(二) PercentRelativeLayout

<?xml version="1.0" encoding="utf-8"?>
<android.support.percent.PercentRelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clickable="true">

    <TextView
        android:id="@+id/row_one_item_one"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_alignParentTop="true"
        android:background="#7700ff00"
        android:text="w:70%,h:20%"
        android:gravity="center"
        app:layout_heightPercent="20%"
        app:layout_widthPercent="70%"/>

    <TextView
        android:id="@+id/row_one_item_two"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_toRightOf="@+id/row_one_item_one"
        android:background="#396190"
        android:text="w:30%,h:20%"
        app:layout_heightPercent="20%"
        android:gravity="center"
        app:layout_widthPercent="30%"/>


    <ImageView
        android:id="@+id/row_two_item_one"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:src="@drawable/tangyan"
        android:scaleType="centerCrop"
        android:layout_below="@+id/row_one_item_one"
        android:background="#d89695"
        app:layout_heightPercent="70%"/>

    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_below="@id/row_two_item_one"
        android:background="#770000ff"
        android:gravity="center"
        android:text="width:100%,height:10%"
        app:layout_heightPercent="10%"
        app:layout_widthPercent="100%"/>


</android.support.percent.PercentRelativeLayout>

ok,依然是直接看效果图:

%title插图%num

使用没什么好说的,就是直观的看一下。


三、源码分析

其实细想一下,Google只是对我们原本熟悉的RelativeLayout和FrameLayout进行的功能的扩展,使其支持了percent相关的属性。

那么,我们考虑下,如果是我们添加这种扩展,我们会怎么做:

  • 通过LayoutParams获取child设置的percent相关属性的值
  • onMeasure的时候,将child的width,height的值,通过获取的自定义属性的值进行计算(eg:容器的宽 * fraction ),计算后传入给child.measure(w,h);

ok,有了上面的猜想,我们直接看PercentFrameLayout的源码。

public class PercentFrameLayout extends FrameLayout {
    private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this);

    //省略了,两个构造方法

    public PercentFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }



    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (mHelper.handleMeasuredStateTooSmall()) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        mHelper.restoreOriginalParams();
    }

    public static class LayoutParams extends FrameLayout.LayoutParams
            implements PercentLayoutHelper.PercentLayoutParams {
        private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;

        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
            mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
        }
        //省略了一些代码...

        @Override
        public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() {
            return mPercentLayoutInfo;
        }

        @Override
        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
            PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);
        }
    }
}

代码是相当的短,可以看到PercentFrameLayout里面首先重写了generateLayoutParams方法,当然了,由于支持了一些新的layout_属性,那么肯定需要定义对应的LayoutParams。


(一)percent相关属性的获取

可以看到PercentFrameLayout.LayoutParams在原有的FrameLayout.LayoutParams基础上,实现了PercentLayoutHelper.PercentLayoutParams接口。

这个接口很简单,只有一个方法:

public interface PercentLayoutParams {
        PercentLayoutInfo getPercentLayoutInfo();
    }

而,这个方法的实现呢,也只有一行:return mPercentLayoutInfo;,那么这个mPercentLayoutInfo在哪完成赋值呢?

看PercentFrameLayout.LayoutParams的构造方法:

public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
            mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
        }

可以看到,将attrs传入给getPercentLayoutInfo方法,那么不用说,这个方法的内部,肯定是获取自定义属性的值,然后将其封装到PercentLayoutInfo对象中,*后返回。

代码如下:

public static PercentLayoutInfo getPercentLayoutInfo(Context context,
            AttributeSet attrs) {
        PercentLayoutInfo info = null;
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PercentLayout_Layout);
        float value = array.getFraction(R.styleable.PercentLayout_Layout_layout_widthPercent, 1, 1,
                -1f);
        if (value != -1f) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "percent width: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.widthPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_heightPercent, 1, 1, -1f);
        if (value != -1f) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "percent height: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.heightPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginPercent, 1, 1, -1f);
        if (value != -1f) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "percent margin: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.leftMarginPercent = value;
            info.topMarginPercent = value;
            info.rightMarginPercent = value;
            info.bottomMarginPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginLeftPercent, 1, 1,
                -1f);
        if (value != -1f) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "percent left margin: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.leftMarginPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginTopPercent, 1, 1,
                -1f);
        if (value != -1f) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "percent top margin: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.topMarginPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginRightPercent, 1, 1,
                -1f);
        if (value != -1f) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "percent right margin: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.rightMarginPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginBottomPercent, 1, 1,
                -1f);
        if (value != -1f) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "percent bottom margin: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.bottomMarginPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginStartPercent, 1, 1,
                -1f);
        if (value != -1f) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "percent start margin: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.startMarginPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginEndPercent, 1, 1,
                -1f);
        if (value != -1f) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "percent end margin: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.endMarginPercent = value;
        }
        array.recycle();
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "constructed: " + info);
        }
        return info;
    }

是不是和我们平时的取值很类似,所有的值*终封装到PercentLayoutInfo对象中。

ok,到此我们的属性获取就介绍完成,有了这些属性,是不是onMeasure里面要进行使用呢?


(二) onMeasue中重新计算child的尺寸

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (mHelper.handleMeasuredStateTooSmall()) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

可以看到onMeasure中的代码页很少,看来核心的代码都被封装在mHelper的方法中,我们直接看mHelper.adjustChildren方法。

/**
     * Iterates over children and changes their width and height to one calculated from percentage
     * values.
     * @param widthMeasureSpec Width MeasureSpec of the parent ViewGroup.
     * @param heightMeasureSpec Height MeasureSpec of the parent ViewGroup.
     */
    public void adjustChildren(int widthMeasureSpec, int heightMeasureSpec) {
        //...
        int widthHint = View.MeasureSpec.getSize(widthMeasureSpec);
        int heightHint = View.MeasureSpec.getSize(heightMeasureSpec);
        for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
            View view = mHost.getChildAt(i);
            ViewGroup.LayoutParams params = view.getLayoutParams();

            if (params instanceof PercentLayoutParams) {
                PercentLayoutInfo info =
                        ((PercentLayoutParams) params).getPercentLayoutInfo();
                if (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, "using " + info);
                }
                if (info != null) {
                    if (params instanceof ViewGroup.MarginLayoutParams) {
                        info.fillMarginLayoutParams((ViewGroup.MarginLayoutParams) params,
                                widthHint, heightHint);
                    } else {
                        info.fillLayoutParams(params, widthHint, heightHint);
                    }
                }
            }
        }
    }

通过注释也能看出,此方法中遍历所有的孩子,通过百分比的属性重新设置其宽度和高度。

首先在widthHint、heightHint保存容器的宽、高,然后遍历所有的孩子,判断其LayoutParams是否是PercentLayoutParams类型,如果是,通过params.getPercentLayoutInfo拿出info对象。

是否还记得,上面的分析中,PercentLayoutInfo保存了percent相关属性的值。

如果info不为null,则判断是否需要处理margin;我们直接看fillLayoutParams方法(处理margin也是类似的)。

 /**
         * Fills {@code ViewGroup.LayoutParams} dimensions based on percentage values.
         */
        public void fillLayoutParams(ViewGroup.LayoutParams params, int widthHint,
                int heightHint) {
            // Preserve the original layout params, so we can restore them after the measure step.
            mPreservedParams.width = params.width;
            mPreservedParams.height = params.height;

            if (widthPercent >= 0) {
                params.width = (int) (widthHint * widthPercent);
            }
            if (heightPercent >= 0) {
                params.height = (int) (heightHint * heightPercent);
            }
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "after fillLayoutParams: (" + params.width + ", " + params.height + ")");
            }
        }

首先保存原本的width和height,然后重置params的width和height为(int) (widthHint * widthPercent)(int) (heightHint * heightPercent);

到此,其实我们的百分比转换就结束了,理论上就已经实现了对于百分比的支持,不过Google还考虑了一些细节。

我们回到onMeasure方法:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (mHelper.handleMeasuredStateTooSmall()) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

下面还有个mHelper.handleMeasuredStateTooSmall的判断,也就是说,如果你设置的百分比,*终计算出来的MeasuredSize过小的话,会进行一些操作。
代码如下:

public boolean handleMeasuredStateTooSmall() {
        boolean needsSecondMeasure = false;
        for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
            View view = mHost.getChildAt(i);
            ViewGroup.LayoutParams params = view.getLayoutParams();
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "should handle measured state too small " + view + " " + params);
            }
            if (params instanceof PercentLayoutParams) {
                PercentLayoutInfo info =
                        ((PercentLayoutParams) params).getPercentLayoutInfo();
                if (info != null) {
                    if (shouldHandleMeasuredWidthTooSmall(view, info)) {
                        needsSecondMeasure = true;
                        params.width = ViewGroup.LayoutParams.WRAP_CONTENT;
                    }
                    if (shouldHandleMeasuredHeightTooSmall(view, info)) {
                        needsSecondMeasure = true;
                        params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
                    }
                }
            }
        }
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "should trigger second measure pass: " + needsSecondMeasure);
        }
        return needsSecondMeasure;
    }

首先遍历所有的孩子,拿出孩子的layoutparams,如果是PercentLayoutParams实例,则取出info。如果info不为null,调用shouldHandleMeasuredWidthTooSmall判断:

private static boolean shouldHandleMeasuredWidthTooSmall(View view, PercentLayoutInfo info) {
        int state = ViewCompat.getMeasuredWidthAndState(view) & ViewCompat.MEASURED_STATE_MASK;
        return state == ViewCompat.MEASURED_STATE_TOO_SMALL && info.widthPercent >= 0 &&
                info.mPreservedParams.width == ViewGroup.LayoutParams.WRAP_CONTENT;
    }

这里就是判断,如果你设置的measuredWidth或者measureHeight过小的话,并且你在布局文件中layout_w/h 设置的是WRAP_CONTENT的话,将params.width / height= ViewGroup.LayoutParams.WRAP_CONTENT,然后重新测量。

哈,onMeasure终于结束了~~~现在我觉得应该代码结束了吧,尺寸都设置好了,还需要干嘛么,but,你会发现onLayout也重写了,我们又不改变layout规则,在onLayout里面干什么毛线:

@Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        mHelper.restoreOriginalParams();
    }

继续看mHelper.restoreOriginalParams

 /**
     * Iterates over children and restores their original dimensions that were changed for
     * percentage values. Calling this method only makes sense if you previously called
     * {@link PercentLayoutHelper#adjustChildren(int, int)}.
     */
    public void restoreOriginalParams() {
        for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
            View view = mHost.getChildAt(i);
            ViewGroup.LayoutParams params = view.getLayoutParams();
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "should restore " + view + " " + params);
            }
            if (params instanceof PercentLayoutParams) {
                PercentLayoutInfo info =
                        ((PercentLayoutParams) params).getPercentLayoutInfo();
                if (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, "using " + info);
                }
                if (info != null) {
                    if (params instanceof ViewGroup.MarginLayoutParams) {
                        info.restoreMarginLayoutParams((ViewGroup.MarginLayoutParams) params);
                    } else {
                        info.restoreLayoutParams(params);
                    }
                }
            }
        }
    }

噗,原来是重新恢复原本的尺寸值,也就是说onMeasure里面的对值进行了改变,测量完成后。在这个地方,将值又恢复成如果布局文件中的值,上面写的都是0。恢复很简单:


public void restoreLayoutParams(ViewGroup.LayoutParams params) {
            params.width = mPreservedParams.width;
            params.height = mPreservedParams.height;
        }

你应该没有忘在哪存的把~忘了的话,麻烦Ctrl+F ‘mPreservedParams.width’ 。

也就是说,你去打印上面写法,布局文件中view的v.getLayoutParams().width,这个值应该是0。

这里感觉略微不爽~这个0没撒用处呀,还不如不重置~~

好了,到此就分析完了,其实主要就几个步骤:

  • LayoutParams中属性的获取
  • onMeasure中,改变params.width为百分比计算结果,测量
  • 如果测量值过小且设置的w/h是wrap_content,重新测量
  • onLayout中,重置params.w/h为布局文件中编写的值

可以看到,有了RelativeLayout、FrameLayout的扩展,竟然没有LinearLayout几个意思。好在,我们的核心代码都由PercentLayoutHelper封装了,自己扩展下LinearLayout也不复杂。


三、实现PercentLinearlayout

可能有人会说,有了weight呀,但是weight能做到宽、高同时百分比赋值嘛?

好了,代码很简单,如下:


(一)PercentLinearLayout

package com.juliengenoud.percentsamples;

import android.content.Context;
import android.content.res.TypedArray;
import android.support.percent.PercentLayoutHelper;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.LinearLayout;

/**
 * Created by zhy on 15/6/30.
 */
public class PercentLinearLayout extends LinearLayout
{

    private PercentLayoutHelper mPercentLayoutHelper;

    public PercentLinearLayout(Context context, AttributeSet attrs)
    {
        super(context, attrs);

        mPercentLayoutHelper = new PercentLayoutHelper(this);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        mPercentLayoutHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (mPercentLayoutHelper.handleMeasuredStateTooSmall())
        {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b)
    {
        super.onLayout(changed, l, t, r, b);
        mPercentLayoutHelper.restoreOriginalParams();
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs)
    {
        return new LayoutParams(getContext(), attrs);
    }


    public static class LayoutParams extends LinearLayout.LayoutParams
            implements PercentLayoutHelper.PercentLayoutParams
    {
        private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;

        public LayoutParams(Context c, AttributeSet attrs)
        {
            super(c, attrs);
            mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
        }

        @Override
        public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo()
        {
            return mPercentLayoutInfo;
        }

        @Override
        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr)
        {
            PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);
        }

        public LayoutParams(int width, int height) {
            super(width, height);
        }


        public LayoutParams(ViewGroup.LayoutParams source) {
            super(source);
        }

        public LayoutParams(MarginLayoutParams source) {
            super(source);
        }

    }

}

如果你详细看了上面的源码分析,这个代码是不是没撒解释的了~


(二)测试布局

<?xml version="1.0" encoding="utf-8"?>


<com.juliengenoud.percentsamples.PercentLinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#ff44aacc"
        android:text="width:60%,height:5%"
        android:textColor="#ffffff"
        app:layout_heightPercent="5%"
        app:layout_marginBottomPercent="5%"
        app:layout_widthPercent="60%"/>

    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#ff4400cc"
        android:gravity="center"
        android:textColor="#ffffff"
        android:text="width:70%,height:10%"
        app:layout_heightPercent="10%"
        app:layout_marginBottomPercent="5%"
        app:layout_widthPercent="70%"/>
    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#ff44aacc"
        android:gravity="center"
        android:text="width:80%,height:15%"
        android:textColor="#ffffff"
        app:layout_heightPercent="15%"
        app:layout_marginBottomPercent="5%"
        app:layout_widthPercent="80%"/>
    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#ff4400cc"
        android:gravity="center"
        android:text="width:90%,height:5%"
        android:textColor="#ffffff"
        app:layout_heightPercent="20%"
        app:layout_marginBottomPercent="10%"
        app:layout_widthPercent="90%"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:background="#ff44aacc"
        android:gravity="center"
        android:text="width:100%,height:25%"
        android:textColor="#ffffff"
        app:layout_heightPercent="25%"
        app:layout_marginBottomPercent="5%"
        />


</com.juliengenoud.percentsamples.PercentLinearLayout>

我们纵向排列的几个TextView,分别设置宽/高都为百分比,且之间的间隔为5%p。


(三)效果图

%title插图%num

ok,到此,我们使用、源码分析、扩展PercentLinearLayout就结束了。

Android Studio错误提示

错误异常 (1)Android Studio错误提示:Gradle project sync failed. Basic functionality (eg. editing, debugging) will not work properly

【已解决】

 

【问题】

Android Studio中出现提示:

Gradle project sync failed. Basic functionality (eg. editing, debugging) will not work properly

 

【解决过程】

1.点击了:

Show Log in Explorer

打开找到了log文件:

C:\Users\Administrator\.AndroidStudio\system\log\idea.log

C Users Administrator AndroidStudio system log idea.log file

Log文件内容很长,*后一部分是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
Consult IDE log for more details (Help | Show Log)
2015-04-01 10:37:20,131 [1257636]   INFO - indexing.UnindexedFilesUpdater - Indexable files iterated in 2694 ms
2015-04-01 10:37:20,131 [1257636]   INFO - indexing.UnindexedFilesUpdater - Unindexed files update started: 2500 files to update
2015-04-01 10:37:22,330 [1259835]   INFO - s.impl.stores.FileBasedStorage - Document was not loaded for $PROJECT_CONFIG_DIR$/codeStyleSettings.xml file is null
2015-04-01 10:37:22,333 [1259838]   INFO - s.impl.stores.FileBasedStorage - Document was not loaded for $PROJECT_CONFIG_DIR$/projectCodeStyle.xml file is null
2015-04-01 10:37:22,600 [1260105]   INFO - s.impl.stores.FileBasedStorage - Document was not loaded for $PROJECT_CONFIG_DIR$/fileColors.xml file is null
2015-04-01 10:37:25,627 [1263132]   INFO - s.impl.stores.FileBasedStorage - Document was not loaded for $APP_CONFIG$/web-browsers.xml file is null
2015-04-01 10:37:36,269 [1273774]   INFO - indexing.UnindexedFilesUpdater - Unindexed files update done in 16138 ms
2015-04-01 10:37:36,941 [1274446]   INFO - nject.config.XPathSupportProxy - XPath Support is not available
2015-04-01 10:37:37,119 [1274624]   INFO - s.impl.stores.FileBasedStorage - Document was not loaded for $APP_CONFIG$/IntelliLang.xml file is null
2015-04-01 10:37:37,122 [1274627]   INFO - s.impl.stores.FileBasedStorage - Document was not loaded for $PROJECT_CONFIG_DIR$/IntelliLang.xml file is null
2015-04-01 10:37:37,947 [1275452]   INFO - s.impl.stores.FileBasedStorage - Document was not loaded for $APP_CONFIG$/feature.usage.statistics.xml file is null
2015-04-01 10:37:39,177 [1276682]   INFO - s.impl.stores.FileBasedStorage - Document was not loaded for $PROJECT_CONFIG_DIR$/gant_config.xml file is null
2015-04-01 10:37:40,335 [1277840]   INFO - s.impl.stores.FileBasedStorage - Document was not loaded for $APP_CONFIG$/remote-servers.xml file is null
2015-04-01 10:37:40,518 [1278023]   INFO - tor.impl.FileEditorManagerImpl - Project opening took 58569 ms
2015-04-01 10:37:46,714 [1284219]   INFO - s.impl.stores.FileBasedStorage - Document was not loaded for $APP_CONFIG$/file.template.settings.xml file is null
2015-04-01 10:37:51,540 [1289045]   INFO - s.impl.stores.FileBasedStorage - Document was not loaded for $APP_CONFIG$/cachedDictionary.xml file is null
2015-04-01 10:39:35,962 [1393467]   INFO - s.impl.stores.FileBasedStorage - Document was not loaded for $APP_CONFIG$/intentionSettings.xml file is null
2015-04-01 10:43:56,598 [1654103]   INFO - indexing.UnindexedFilesUpdater - Indexable files iterated in 3182 ms
2015-04-01 10:43:56,598 [1654103]   INFO - indexing.UnindexedFilesUpdater - Unindexed files update started: 22098 files to update
2015-04-01 10:44:23,144 [1680649]   INFO - util.EmbeddedDistributionPaths - Looking for embedded Gradle distribution at 'C:\Program Files\Android\Android Studio\gradle\gradle-2.2.1'
2015-04-01 10:44:23,252 [1680757]   INFO - s.plugins.gradle.GradleManager - Instructing gradle to use java from C:\Program Files\Java\jdk1.7.0_76
2015-04-01 10:44:23,253 [1680758]   INFO - s.plugins.gradle.GradleManager - Instructing gradle to use java from C:\Program Files\Java\jdk1.7.0_76
2015-04-01 10:44:23,254 [1680759]   INFO - util.EmbeddedDistributionPaths - Looking for embedded Maven repo at 'C:\Program Files\Android\Android Studio\gradle\m2repository'
2015-04-01 10:44:23,256 [1680761]   INFO - .project.GradleExecutionHelper - Passing command-line args to Gradle Tooling API: [-Pandroid.injected.build.model.only=true, -Pandroid.injected.build.model.only.advanced=true, -Pandroid.injected.invoked.from.ide=true, --init-script, C:\Users\Administrator\AppData\Local\Temp\asLocalRepo3577806095284007233.gradle, --init-script, C:\Users\Administrator\AppData\Local\Temp\ijinit6508239471401468090.gradle]
2015-04-01 10:44:40,498 [1698003]   INFO - .project.GradleProjectResolver - Gradle project resolve error
org.gradle.tooling.BuildException: Could not run build action using Gradle installation 'C:\Program Files\Android\Android Studio\gradle\gradle-2.2.1'.
 at org.gradle.tooling.internal.consumer.ResultHandlerAdapter.onFailure(ResultHandlerAdapter.java:57)
 at org.gradle.tooling.internal.consumer.async.DefaultAsyncConsumerActionExecutor$1$1.run(DefaultAsyncConsumerActionExecutor.java:57)
 at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
 at java.lang.Thread.run(Thread.java:745)
 at org.gradle.tooling.internal.consumer.BlockingResultHandler.getResult(BlockingResultHandler.java:46)
 at org.gradle.tooling.internal.consumer.DefaultBuildActionExecuter.run(DefaultBuildActionExecuter.java:46)
 at org.jetbrains.plugins.gradle.service.project.GradleProjectResolver.doResolveProjectInfo(GradleProjectResolver.java:186)
 at org.jetbrains.plugins.gradle.service.project.GradleProjectResolver.access$300(GradleProjectResolver.java:64)
 at org.jetbrains.plugins.gradle.service.project.GradleProjectResolver$ProjectConnectionDataNodeFunction.fun(GradleProjectResolver.java:361)
 at org.jetbrains.plugins.gradle.service.project.GradleProjectResolver$ProjectConnectionDataNodeFunction.fun(GradleProjectResolver.java:333)
 at org.jetbrains.plugins.gradle.service.project.GradleExecutionHelper.execute(GradleExecutionHelper.java:203)
 at org.jetbrains.plugins.gradle.service.project.GradleProjectResolver.resolveProjectInfo(GradleProjectResolver.java:116)
 at org.jetbrains.plugins.gradle.service.project.GradleProjectResolver.resolveProjectInfo(GradleProjectResolver.java:64)
 at com.intellij.openapi.externalSystem.service.remote.RemoteExternalSystemProjectResolverImpl$1.produce(RemoteExternalSystemProjectResolverImpl.java:41)
 at com.intellij.openapi.externalSystem.service.remote.RemoteExternalSystemProjectResolverImpl$1.produce(RemoteExternalSystemProjectResolverImpl.java:37)
 at com.intellij.openapi.externalSystem.service.remote.AbstractRemoteExternalSystemService.execute(AbstractRemoteExternalSystemService.java:59)
 at com.intellij.openapi.externalSystem.service.remote.RemoteExternalSystemProjectResolverImpl.resolveProjectInfo(RemoteExternalSystemProjectResolverImpl.java:37)
 at com.intellij.openapi.externalSystem.service.remote.wrapper.ExternalSystemProjectResolverWrapper.resolveProjectInfo(ExternalSystemProjectResolverWrapper.java:49)
 at com.intellij.openapi.externalSystem.service.internal.ExternalSystemResolveProjectTask.doExecute(ExternalSystemResolveProjectTask.java:48)
 at com.intellij.openapi.externalSystem.service.internal.AbstractExternalSystemTask.execute(AbstractExternalSystemTask.java:137)
 at com.intellij.openapi.externalSystem.service.internal.AbstractExternalSystemTask.execute(AbstractExternalSystemTask.java:123)
 at com.intellij.openapi.externalSystem.util.ExternalSystemUtil$2.execute(ExternalSystemUtil.java:406)
 at com.intellij.openapi.externalSystem.util.ExternalSystemUtil$3$2.run(ExternalSystemUtil.java:483)
 at com.intellij.openapi.progress.impl.ProgressManagerImpl$TaskRunnable.run(ProgressManagerImpl.java:471)
 at com.intellij.openapi.progress.impl.ProgressManagerImpl$2.run(ProgressManagerImpl.java:178)
 at com.intellij.openapi.progress.ProgressManager.executeProcessUnderProgress(ProgressManager.java:209)
 at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:212)
 at com.intellij.openapi.progress.impl.ProgressManagerImpl.runProcess(ProgressManagerImpl.java:171)
 at com.intellij.openapi.progress.impl.ProgressManagerImpl$8.run(ProgressManagerImpl.java:380)
 at com.intellij.openapi.application.impl.ApplicationImpl$8.run(ApplicationImpl.java:419)
 at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
 at java.util.concurrent.FutureTask.run(FutureTask.java:262)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
 at java.lang.Thread.run(Thread.java:745)
 at com.intellij.openapi.application.impl.ApplicationImpl$1$1.run(ApplicationImpl.java:149)
Caused by: org.gradle.internal.exceptions.LocationAwareException: A problem occurred configuring project ':cSipSimplemaster'.
 at org.gradle.initialization.DefaultExceptionAnalyser.transform(DefaultExceptionAnalyser.java:77)
 at org.gradle.initialization.MultipleBuildFailuresExceptionAnalyser.transform(MultipleBuildFailuresExceptionAnalyser.java:47)
 at org.gradle.initialization.StackTraceSanitizingExceptionAnalyser.transform(StackTraceSanitizingExceptionAnalyser.java:30)
 at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:108)
 at org.gradle.initialization.DefaultGradleLauncher.getBuildAnalysis(DefaultGradleLauncher.java:97)
 at org.gradle.launcher.exec.InProcessBuildActionExecuter$DefaultBuildController.configure(InProcessBuildActionExecuter.java:84)
 at org.gradle.tooling.internal.provider.ClientProvidedBuildAction.run(ClientProvidedBuildAction.java:43)
 at org.gradle.tooling.internal.provider.ClientProvidedBuildAction.run(ClientProvidedBuildAction.java:31)
 at org.gradle.tooling.internal.provider.ConfiguringBuildAction.run(ConfiguringBuildAction.java:119)
 at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:36)
 at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:26)
 at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:47)
 at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:34)
 at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:119)
 at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:35)
 at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:119)
 at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:24)
 at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:119)
 at org.gradle.launcher.daemon.server.exec.StartStopIfBuildAndStop.execute(StartStopIfBuildAndStop.java:33)
 at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:119)
 at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:71)
 at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:69)
 at org.gradle.util.Swapper.swap(Swapper.java:38)
 at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:69)
 at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:119)
 at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:60)
 at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:34)
 at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:119)
 at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:70)
 at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:34)
 at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:119)
 at org.gradle.launcher.daemon.server.exec.DaemonHygieneAction.execute(DaemonHygieneAction.java:39)
 at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:119)
 at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:46)
 at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:246)
 at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64)
Caused by: org.gradle.api.ProjectConfigurationException: A problem occurred configuring project ':cSipSimplemaster'.
 at org.gradle.configuration.project.LifecycleProjectEvaluator.addConfigurationFailure(LifecycleProjectEvaluator.java:91)
 at org.gradle.configuration.project.LifecycleProjectEvaluator.notifyAfterEvaluate(LifecycleProjectEvaluator.java:86)
 at org.gradle.configuration.project.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:65)
 at org.gradle.api.internal.project.AbstractProject.evaluate(AbstractProject.java:504)
 at org.gradle.api.internal.project.AbstractProject.evaluate(AbstractProject.java:83)
 at org.gradle.execution.TaskPathProjectEvaluator.configureHierarchy(TaskPathProjectEvaluator.java:47)
 at org.gradle.configuration.DefaultBuildConfigurer.configure(DefaultBuildConfigurer.java:35)
 at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:129)
 at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:106)
 ... 32 more
Caused by: java.lang.IllegalStateException: failed to find target android-19 : C:\Users\Administrator\AppData\Local\Android\sdk
 at com.android.builder.sdk.DefaultSdkLoader.getTargetInfo(DefaultSdkLoader.java:88)
 at com.android.build.gradle.internal.SdkHandler.initTarget(SdkHandler.java:90)
 at com.android.build.gradle.BasePlugin.ensureTargetSetup(BasePlugin.groovy:467)
 at com.android.build.gradle.BasePlugin.access$0(BasePlugin.groovy)
 at com.android.build.gradle.BasePlugin$_createTasks_closure9.doCall(BasePlugin.groovy:372)
 at org.gradle.listener.ClosureBackedMethodInvocationDispatch.dispatch(ClosureBackedMethodInvocationDispatch.java:40)
 at org.gradle.listener.ClosureBackedMethodInvocationDispatch.dispatch(ClosureBackedMethodInvocationDispatch.java:25)
 at org.gradle.listener.BroadcastDispatch.dispatch(BroadcastDispatch.java:83)
 at org.gradle.listener.BroadcastDispatch.dispatch(BroadcastDispatch.java:31)
 at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
 at com.sun.proxy.$Proxy12.afterEvaluate(Unknown Source)
 at org.gradle.configuration.project.LifecycleProjectEvaluator.notifyAfterEvaluate(LifecycleProjectEvaluator.java:79)
 ... 39 more
2015-04-01 10:44:40,500 [1698005]   WARN - nal.AbstractExternalSystemTask - failed to find target android-19 : C:\Users\Administrator\AppData\Local\Android\sdk
com.intellij.openapi.externalSystem.model.ExternalSystemException: failed to find target android-19 : C:\Users\Administrator\AppData\Local\Android\sdk
 at com.android.tools.idea.gradle.project.ProjectImportErrorHandler.createUserFriendlyError(ProjectImportErrorHandler.java:225)
 at com.android.tools.idea.gradle.project.ProjectImportErrorHandler.getUserFriendlyError(ProjectImportErrorHandler.java:103)
 at com.android.tools.idea.gradle.project.AndroidGradleProjectResolver.getUserFriendlyError(AndroidGradleProjectResolver.java:321)
 at org.jetbrains.plugins.gradle.service.project.GradleProjectResolver$ProjectConnectionDataNodeFunction.fun(GradleProjectResolver.java:367)
 at org.jetbrains.plugins.gradle.service.project.GradleProjectResolver$ProjectConnectionDataNodeFunction.fun(GradleProjectResolver.java:333)
 at org.jetbrains.plugins.gradle.service.project.GradleExecutionHelper.execute(GradleExecutionHelper.java:203)
 at org.jetbrains.plugins.gradle.service.project.GradleProjectResolver.resolveProjectInfo(GradleProjectResolver.java:116)
 at org.jetbrains.plugins.gradle.service.project.GradleProjectResolver.resolveProjectInfo(GradleProjectResolver.java:64)
 at com.intellij.openapi.externalSystem.service.remote.RemoteExternalSystemProjectResolverImpl$1.produce(RemoteExternalSystemProjectResolverImpl.java:41)
 at com.intellij.openapi.externalSystem.service.remote.RemoteExternalSystemProjectResolverImpl$1.produce(RemoteExternalSystemProjectResolverImpl.java:37)
 at com.intellij.openapi.externalSystem.service.remote.AbstractRemoteExternalSystemService.execute(AbstractRemoteExternalSystemService.java:59)
 at com.intellij.openapi.externalSystem.service.remote.RemoteExternalSystemProjectResolverImpl.resolveProjectInfo(RemoteExternalSystemProjectResolverImpl.java:37)
 at com.intellij.openapi.externalSystem.service.remote.wrapper.ExternalSystemProjectResolverWrapper.resolveProjectInfo(ExternalSystemProjectResolverWrapper.java:49)
 at com.intellij.openapi.externalSystem.service.internal.ExternalSystemResolveProjectTask.doExecute(ExternalSystemResolveProjectTask.java:48)
 at com.intellij.openapi.externalSystem.service.internal.AbstractExternalSystemTask.execute(AbstractExternalSystemTask.java:137)
 at com.intellij.openapi.externalSystem.service.internal.AbstractExternalSystemTask.execute(AbstractExternalSystemTask.java:123)
 at com.intellij.openapi.externalSystem.util.ExternalSystemUtil$2.execute(ExternalSystemUtil.java:406)
 at com.intellij.openapi.externalSystem.util.ExternalSystemUtil$3$2.run(ExternalSystemUtil.java:483)
 at com.intellij.openapi.progress.impl.ProgressManagerImpl$TaskRunnable.run(ProgressManagerImpl.java:471)
 at com.intellij.openapi.progress.impl.ProgressManagerImpl$2.run(ProgressManagerImpl.java:178)
 at com.intellij.openapi.progress.ProgressManager.executeProcessUnderProgress(ProgressManager.java:209)
 at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:212)
 at com.intellij.openapi.progress.impl.ProgressManagerImpl.runProcess(ProgressManagerImpl.java:171)
 at com.intellij.openapi.progress.impl.ProgressManagerImpl$8.run(ProgressManagerImpl.java:380)
 at com.intellij.openapi.application.impl.ApplicationImpl$8.run(ApplicationImpl.java:419)
 at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
 at java.util.concurrent.FutureTask.run(FutureTask.java:262)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
 at java.lang.Thread.run(Thread.java:745)
 at com.intellij.openapi.application.impl.ApplicationImpl$1$1.run(ApplicationImpl.java:149)
2015-04-01 10:44:40,500 [1698005]   WARN - radle.project.ProjectSetUpTask - 
2015-04-01 10:44:40,501 [1698006]   INFO - radle.project.ProjectSetUpTask - failed to find target android-19 : C:\Users\Administrator\AppData\Local\Android\sdk
Consult IDE log for more details (Help | Show Log)
2015-04-01 10:44:59,763 [1717268]   INFO - ellij.vfs.persistent.FSRecords - Contents:16384 of 144781555, reuses:82 of 73161 for 1173
2015-04-01 10:45:13,819 [1731324]   INFO - indexing.UnindexedFilesUpdater - Unindexed files update done in 77221 ms
2015-04-01 11:05:26,225 [2943730]   INFO - s.impl.stores.FileBasedStorage - Document was not loaded for $APP_CONFIG$/emmet.xml file is null
2015-04-01 11:05:26,335 [2943840]   INFO - s.impl.stores.FileBasedStorage - Document was not loaded for $APP_CONFIG$/postfixTemplates.xml file is null

2.对应着,Android Studio中也有错误提示:

1
2
Error:failed to find target android-19 : C:\Users\Administrator\AppData\Local\Android\sdk
<a href="install.android.platform">Install missing platform(s) and sync project</a>

failed to sync gradle project CSipSimple-master

很明显:

好像是由于:

没有安装android的image,所以找不到android-19.

抽空再去安装android的image

3.然后点击了:

Install missing platform(s) and sync project

后,去安装:

SDK quick installation android sdk platform 19

installing requested componets for android sdk

对应log:

1
2
3
4
5
6
7
8
9
10
11
12
13
Loading SDK information...
Refresh Sources:
  Fetched Add-ons List successfully
  Refresh Sources
  Failed to fetch URL https://dl-ssl.google.com/glass/gdk/addon.xml, reason: Connect Connection timed out: connect
Refresh Sources:
  Failed to fetch URL https://dl-ssl.google.com/glass/gdk/addon.xml, reason: Connect Connection timed out: connect
Installing Archives:
  Preparing to install archives
  Installing SDK Platform Android 4.4.2, API 19, revision 4
    Installed SDK Platform Android 4.4.2, API 19, revision 4
  Done. 1 package installed.

 

【总结】

此处Android Studio中出现

Error:failed to find target android-19

是因为没有安装Android SDK Platform 19(Android 4.4.2 API 19),去按照提示安装好即可消除此错误。

Android Studio中提示:Project SDK is not defined

Android Studio中提示:Project SDK is not defined
 

【背景】

之前用Android Studio去打开一个项目后,结果遇到提示:

Project SDK is not defined

如图:

android studio Project SDK is not defined

【解决过程】

1.试着去点击Setup SDK,出现设置对话框:

popup select project sdk choose android api 21 platform

然后就卡死了好一会。

然后就好了,就没了这个提示了。

 

【总结】

好像是Android Studio对于刚使用的时候,没有确定Android的SDK,所以需要去设置一下对应的所用的版本。

此处选择默认的Android API 21 Platform(java version 1.7.0_76)

即可。

其中java version 1.7.0_76是此处本地所安装的java(jdk)的版本。

Java中正则Matcher类的matches()、lookAt()和find()的区别

Java中正则Matcher类的matches()、lookAt()和find()的区别

在Matcher类中有matches、lookingAt和find都是匹配目标的方法,但容易混淆,整理它们的区别如下:

  • matches:整个匹配,只有整个字符序列完全匹配成功,才返回True,否则返回False。但如果前部分匹配成功,将移动下次匹配的位置。
  • lookingAt:部分匹配,总是从*个字符进行匹配,匹配成功了不再继续匹配,匹配失败了,也不继续匹配。
  • find:部分匹配,从当前位置开始匹配,找到一个匹配的子串,将移动下次匹配的位置。
  • reset:给当前的Matcher对象配上个新的目标,目标是就该方法的参数;如果不给参数,reset会把Matcher设到当前字符串的开始处。

使用示例代码来展示他们的区别更清晰明了:

  1. package net.oseye;
  2.  
  3. import java.util.regex.Matcher;
  4. import java.util.regex.Pattern;
  5.  
  6. public class IOTest {
  7. public static void main(String[] args){
  8. Pattern pattern = Pattern.compile(“\\d{3,5}”);
  9. String charSequence “123-34345-234-00”;
  10. Matcher matcher = pattern.matcher(charSequence);
  11.  
  12. //虽然匹配失败,但由于charSequence里面的”123″和pattern是匹配的,所以下次的匹配从位置4开始
  13. print(matcher.matches());
  14. //测试匹配位置
  15. matcher.find();
  16. print(matcher.start());
  17.  
  18. //使用reset方法重置匹配位置
  19. matcher.reset();
  20.  
  21. //*次find匹配以及匹配的目标和匹配的起始位置
  22. print(matcher.find());
  23. print(matcher.group()+” – “+matcher.start());
  24. //第二次find匹配以及匹配的目标和匹配的起始位置
  25. print(matcher.find());
  26. print(matcher.group()+” – “+matcher.start());
  27.  
  28. //*次lookingAt匹配以及匹配的目标和匹配的起始位置
  29. print(matcher.lookingAt());
  30. print(matcher.group()+” – “+matcher.start());
  31.  
  32. //第二次lookingAt匹配以及匹配的目标和匹配的起始位置
  33. print(matcher.lookingAt());
  34. print(matcher.group()+” – “+matcher.start());
  35. }
  36. public static void print(Object o){
  37. System.out.println(o);
  38. }
  39. }

输出结果:

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