第一天由于延误,到达桃园机场已是晚上十点多。几个航班几百号人同时排队入境,出机场前前后后花了一个多小时,负责接机的小哥一直催促。不过值得称赞的是,司机小哥的服务还算到位,主动帮忙搬行李。由于桃园机场距离台北市区四十五公里,入住酒店时已是深夜,深感疲惫。
第二天安排的行程比较紧,早早便起床了。为期两天的台北游,我们选择公车+捷运的方式出行。在酒店楼下 FamilyMart 购买了交通卡,步行前往我们行程的第一个景点。
工作日,整洁干净的街道上一排排停放整齐的机车。在台湾,机车不仅仅是机车更像是一种文化。
路过早餐摊,要了鸡蛋饼,味道不错,美好的一天当然是从早餐开始。
西门町步行街,白天人迹稀少,许多店铺还未营业,只有夜幕降临后才能体会到它的热闹。
紧邻西门町的西门红楼,建于 1908 年,曾经又破又旧,以专门放小电影而闻名,如今已变为剧场。
台北的公交站,没有大大广告牌。
龙山寺,台北第一名刹,精巧的设计,美妙绝伦,往来的香客也是络绎不绝。
台北总统府,每月第一个周六为总统府大范围开放日。这次时间上完美错过,只能隔个马路在外面拍照,门口正在搭台子庆祝双十节(台湾的国庆节)。
看到学校,有没有联想到台湾的青春电影?
中正纪念堂,为纪念蒋介石而建造的纪念堂,每个整点驻守的仪仗队交接仪式很值得一看。
交接仪式开始了,整个交接过程大概持续十几分钟,左右两边卫兵纹丝不动,差点以为是雕塑。
午饭吃到了正宗的卤肉饭,卤肉入口即化,肥而不腻。也不知道是不是太饿的原因,之后也再未遇到这么好吃的卤肉饭。(推荐中正纪念堂附近的“金峰鲁肉饭”)
整整一个上午游玩下来,家人觉得有点累,送完他们回酒店休息,一个人背着相机,扛着三角架,在街头乱逛。
国父纪念馆,为纪念孙中山而建造。同样有仪仗队交接仪式,中正纪念堂已经看过了,没有兴趣再重复看了。
逛完国父纪念馆,随后前往拍摄 101 高楼的绝佳地点–象山。象山虽然不高,但是带着相机和三角架,爬得也很吃力。为了拍到完美的夜景,硬是爬到摄手平台。
在摄手平台占到一个 C 位,也就拍了一个小时吧。
从象山下来便去了宁夏夜市。背着沉重的装备,挤在人山人海的夜市中,此时天空忽然下起了小雨,疲惫的一天,连照片都懒得拍了。望着长长的夜市,实在走不动了,打包了几份小吃,匆忙打车回了 酒店。
前一天的行程过于紧凑,暴走的一天导致腿脚酸痛。睡了个懒觉,来到西门町,吃到了超级好吃的“阿宗面线”。没错,我是站着把面吃完的!
奔向故宫博物馆的路上,空荡荡的公交车像是被我们包车了。(台北公车前后都能刷卡,非优惠卡在下车时刷卡既可)
故宫博物馆,这是我在台北见过的游客最多的景点。众所周知,由于某些政治原因,近几年赴台旅游的大陆游客骤降,爽了我们这些游客,但是也苦了当地的旅游业。
(博物馆禁止带水和食物,双肩包也必须存管,排队存包会浪费掉大量时间。)
镇店之宝,肉形石、大白菜。馆内藏品数量达 65 万 5 千多件,每次展出的数量有限,展件也会定期更换。这次虽然租了讲解器,但馆内人实在太多,根本没机会细细听完,最后也是走马观花匆匆逛完。
佛系的一天,虽然没走多少路,但逛完博物馆感觉已经迈不动脚步。决定回去吃晚饭,准确点来说应该是吃中饭。西门町夜景,“康是美”麻烦给我付广告费。
台湾三大夜市之一,士林夜市。真的可以走一路,吃一路,玩一路。
超级好吃的蚵仔煎(忠诚号蚵仔煎),酱料应该是秘制的。
台湾的便利店真的很方便,前一天晚上在 FamilyMart 打印好预订的台铁票,清晨便直奔台北车站坐台铁前往花莲。
月台等车,开启拍照模式,Hello Kitty 彩绘的太鲁阁号有点萌。
(早起毁一天,从上台铁的那一刻,一直睡觉,错过了沿途的海岸风光。作为普列,台铁整体体验接近于国内动车)
受台风影响沿途飘起了小雨,没想到到达花莲后,天空放晴了。
(花莲县位于台湾东部,夹在高山与大海之间,主要以自然景观为主,公共交通无法直达景区,因此只能包车或者骑摩托车游玩)
七星潭,不是潭,而是海湾,海滩没有沙子只有鹅卵石,迎面就是太平洋。经历了两天的城市游,在看到大海的那一刻,竟然莫名的兴奋。
太平洋的波涛汹涌之极,我深深的感受到了。
面对太平洋比心,吹吹海风,听听海浪,感觉自己快要被晒成咸鱼了。
七星潭往里走便是太鲁阁国家森林公园,公园的面积很大,峡谷、瀑布无处不见,人工开凿横贯台湾东西的山路弯曲在其中,一面是山另一面便是峡谷。
清水断崖,苏花公路上最美的地方。一边是悬崖峭壁,一边是茫茫大海。
29 个集装箱堆叠的星巴克,非常有设计感,适合打卡拍照。
花莲东大门夜市,今晚吃啥?
棺材板,好吃且有特色的小吃,吐司挖空,放上不同的食材,再盖上盖子。
今天的行程我们将继续包车前往垦丁,花莲-垦丁有“纵谷线”和“海岸线”可以选择。我们选择了“纵谷线”,全程三百公里左右,由于没有高速公路,沿着 11 号公路和 9 号公路行驶,早上 8 点多出发,包含中途游玩时间,直至下午 6 点多才到达垦丁。
花莲糖厂,日治时期由日本经营,现主要为观光用途,日式木构造建筑群,保留得非常完好。
瑞穗牧场,牧场商店的牛扎糖超好吃。
瑞穗北回归线纪念碑,亚热带与热带的分界点。
池上便当总铺,很有特色的快餐店,类似于铁路便当。
伯朗大道,金城武拍摄的“伯朗咖啡”广告而走红的小路,道路两旁翠绿的稻田,一望无际,非常惊艳。
路上拍到的日落。
这一路的行程,短暂但又令人惊叹。台湾的东部主要以农业为主,行驶在两山之间的峡谷道路上,两旁的农作物极为丰富,有水稻、玉米、凤梨、火龙果、甘蔗、槟榔、芭蕉等等。
原本计划这一天租机车游玩垦丁,了解到没有国际驾照的情况下只能租电动车,并且不可载人,不得不放弃这个方案,改为包车游。幸运的是,我们遇到了一个很好的司导,不仅给我们准备了冰水,送我们纪念品,还悄悄的给我们买好凤梨,最后在超时的情况下送我们去后壁湖吃海鲜,还毅然等我们吃完再我们送回民宿。
船帆石,耸立在海面的巨大珊瑚石,从外海看,形似船帆。
鹅銮鼻公园,全世界唯一的武装灯塔,南部海域多珊瑚礁石,灯塔是这片海域的指引明灯。
台湾最南点碑,国境之南。
龙磐公园,站在崩崖上,眺望远处的海岸,眼前的海景尽收眼底。(听司导说,这边的海风是冷风,吹久了会头痛)
风吹沙。
海角七号取景地,阿嘉的家。
白沙湾,少年派的奇幻漂流“Pi”最后登陆上岸的海滩。
关山,垦丁最佳拍日落的地方。
后壁湖,邱家生鱼片,价格很实惠的海鲜店,”龙虾” “刺身拼盘” “鲍鱼” “花螺”点了一堆海鲜,最后算下来人均 150 不到。
垦丁返回台北,为了安全和节省时间,选择了高铁,距离垦丁最近的高铁站就是高雄左营车站,垦丁到高雄最快的方式就是包车。我们的这段包车行程,体验非常差,先是上车时司机要跟我们确认订单,并告知我预订的明明是三个人。而后在车内又提醒我们在车上吃东西不要掉在车上(我们压根就没吃),全程也是一脸冷漠。
到达左营车站后,想着距离发车时间还很充足,我们决定去看看号称世界最美地铁站之一的美丽岛捷运站。看,果然很美!
(高雄到台北三百多公里的路程,高铁只需两个小时不到。这一路与在台铁上看到风景大为不同,台湾西部以平原为主,集中了众多工业,城市也相比东部城市更为发达。)
回到台北已是下午,闲不住去逛了下松山文创园区,顺路排队去买了网红凤梨酥,晚上又陪着两个女人药妆店买买买,各自收获满满的一天。
早上收拾好行李,忍不住又跑去西门町,吃了一次“阿宗面线”。八天的行程很仓促,台湾是一个适合深度游玩的地方,下次又会和谁一同重游呢?不知道又会是怎样的心情,带着憧憬和回忆踏上了返程之路。
]]>加油宝组件化改造背景:
加油宝组件化改造目标:
本次改造总共分为五个阶段,由简入繁,采用渐近式改造项目。
将基础通用资源拆分出来,独立为一个BaseRes Module。方便被业务组件引用。
将基础功能从项目中拆分出来,独立为一个BaseLib Module。方便被业务组件依赖。
各业务组件可按需选择合适的架构设计。
本文采用流水帐式叙述,多图杀猫,主要记录自己的看法与见闻。
从深圳湾口岸过关去香港机场,大清早人可真少,同行的小伙伴表示兴奋得一晚上没睡好。而我为了今天能保持良好的精神状态,头天晚上就早早入睡了。
提前两小时到达机场,托运了行李箱,先来环亚机场贵宾候机室吃个早饭。
准点起飞,平稳飞行后,空姐开始派发飞机餐,国泰航空的飞机餐很丰富。
乘坐的飞机,机龄虽然比较老,但好在机上影音娱乐系统还不错。看看片,小眯一会儿,历经三个多小时的飞行,已经开始准备降落了。看,下面就是大阪!
过海关入境,人并不多,大家都有秩序的在排队。一旁也有讲中文的工作人员引导入境。
机场取ICOCA交通卡,为了节省时间,坐JR去京都。
先研究下路线,坐哪个车呢?有点懵!
来不及解释了,先上车。这车头远没有咱高铁霸气!
到达京都站,眼前就是京都塔,转公交去民宿。等等坐哪路车来着?
看,有电车!好想坐。
入住民宿,已是晚上九点。未来三天睡塌塌米,看着很不错。奔波一天,此刻只想睡觉。
房东家客厅,房子不大,三室一厅,家电都是本土品牌。好奇他们晚餐吃什么。。。
总结:
到达关西机场,入境时会有讲中文的工作人员指引排队入境。整个入境过程非常轻松,工作人员态度也极好。大阪转JR去京都,千万要注意别坐错车,同一站台会停不同类型的列车。另外Google Map定位并不是很精准,导致在京都站坐公交,一直没找到站台。日本人大多很热情,在京都站找公交时,竟然有人主动上来询问我们是否需要帮助。日本是一个垃圾分类非常严格的国家,街上垃圾桶非常少,出行记得一定要带上垃圾袋。
清晨,安静祥和的居民区和毫无违和感的电线。猜猜我们住在哪幢?
电车!“滴”刷卡上车。
景区入门的小吃摊,没尝过,不知道好吃不好吃。
伏见稻荷大社,供奉农业之神稻荷。据说日本大神非常之多,令人发指。
真好看啊,侧面再来一张。
千本鸟居,《艺妓回忆录》既视感。
京都就在脚下。
存放着1001尊千手千眼观音像和二十八部众立像的三十三间堂。
巴士站
猜猜为什么公交车是倾斜的?
八坂神社
逛饿了,吃快餐。
偷拍穿和服的日本小姐姐。
整洁的街道和杂乱的电线。
传说有艺妓出没的”祗园角”,然而并没遇见。
花间小路,非常古风的街道。
二年坂
古色古香风格的星巴克,内部也是榻榻米式。
清水寺,最好看的主殿在维修。
总结:
塌塌米虽然睡着很舒服,但是隔音效果很差,早上很容易就被开关门的声音吵醒。纸糊窗户遮光效果也不好,导致早上七点就醒了。乘坐电车在站台刷卡,全凭自觉。京都的公交,停车时车门的那侧会下降,方便乘客上下车,司机会跟每个下车的人说ありがとうござい(谢谢)。公交站会有公交车到达时间,并且基本准确。日本都是直饮水,公园、神社、寺庙随处可见直饮设备。街头烟、饮料、冰淇淋自动贩售机也非常多。
太热了,抹茶甜筒来一个。
岚山公园,郊游的好去处。
发现艺妓,一路跟拍。
豆腐料理,含泪吃完了所有的豆腐。
金阁寺,下雪天应该更好看。
夜食网红”一兰拉面”,温泉蛋太好吃了。
总结:
抹茶甜筒一定要吃,好吃并且不贵。京都景点随处可以见米其林餐厅及百年老店,想吃的一定要提前网上预订,不然不接待。网红餐厅大都需要排队,吃饭最好准备现金,有些餐厅是不接受刷卡的。
二条城,德川家康所建,第十五代将军德川庆喜,在二条城举行“大政奉还”仪式,将政权归还给了天皇。行走在二之丸殿地板上会发出黄莺鸣叫的响声,据说是为了防刺客。
西本愿寺,没啥好看,迅速逛完。
再见北白川,下一站大阪。
大阪烧
总结:
千年古城的京都,算是一座生活节奏缓慢的城市,生活便利,交通发达,非常适合居住。房子以独门独户为主,自带车库。出行以公交为主,电车为辅,地铁较少。
大阪城天守阁,丰臣秀吉家族在这里被德川家康所灭。
大阪历史博物馆
黑门市场,主营海鲜水产。
我就看看,不乱买。
梅田大厦,看大阪夜景。
全日本第一家星巴克
北野异人馆街,明治时代外国人居住的地方。
南京町,猜猜他们都在排队买什么?
大丸百货,买买买的地方,每层都中文翻译,无障碍沟通。
奈良公园,随处可见的鹿群。
东大寺,距今1200历史的木造建筑是怎么被保留下来的?
还是东大寺
春日大社
排队两个小时,才吃到的午饭。
心斋桥
道顿掘,大阪最繁华的地方。
在Vultr注册帐号(Vultr相关优惠码请自行Google),充值并购买VPS主机,尽量选择高配置机器,不要被月付的费用吓倒,实际使用中是按小时计费。
Ubuntu 16.04 64-bit
CPU:6 vCore RAM:16384 MB
200 GB SSD
安装依赖
1 | sudo apt-get update && sudo apt-get install git ccache automake lzop bison gperf build-essential zip unzip curl zlib1g-dev zlib1g-dev:i386 g++-multilib python-networkx libxml2-utils bzip2 libbz2-dev libbz2-1.0 libghc-bzlib-dev squashfs-tools pngcrush schedtool dpkg-dev liblz4-tool make optipng maven bc pngquant imagemagick yasm |
安装JDK
Android Open Source Project(AOSP)的master分支需要Java 8。在Ubuntu下可以使用OpenJDK。
1 | sudo apt-get update |
本文以MoKee源码为例(MoKee Open Source是基于Google AOSP开发的一个Android分支,并适配了多个机型)。
配置Git
1 | git config --global user.name "Your Name" |
安装Repo
Repo是一个方便在Android中使用Git的工具
1 | mkdir -p ~/bin |
1 | curl http://commondatastorage.googleapis.com/git-repo-downloads/repo > ~/bin/repo |
1 | echo "export PATH=~/bin:$PATH" >> ~/.bashrc |
初始化Repo客户端
新建一个目录用来存放源码文件
1 | mkdir -p ~/mokee |
同步源码
1 | cd ~/mokee |
同步结束,查看源码目录
1 | root@AndroidDev:~/mokee# ls |
使用ccache编译缓存,让 C 和 C++ 更快的构建
1 | echo "export USE_CCACHE=1" >> ~/.bashrc |
1 | cd ~/mokee |
初始化环境
1 | source ./build/envsetup.sh |
选择编译目标(这里作了省略未列出全部编译目标)。
1 | lunch |
选择Nexus4
可在mokee查找自己所需的设备代码,如Nexus4对应设备代码为mako
1 | lunch mk_mako-userdebug |
编译成功
1 | ### make completed successfully (38:05 (mm:ss)) #### |
编译结果
MK71.1-mako-201704110629-UNOFFICIAL.zip为刷机包
1 | root@AndroidDev:~/mokee/out/target/product/mako# ls |
下载ROM
1 | scp root@your server ip:/root/mokee/out/target/product/mako/MK71.1-mako-201704110629-UNOFFICIAL.zip /Users/Downloads/rom |
1 | MK71.1-mako-201704110629-UNOFFICIAL.zip 100% 335MB 79.7KB/s 1:11:39 |
Tips:在Vultr中给当前机器添加一个Snapshots(快照),Vultr提供了免费保存Snapshots的服务,至此VPS可以暂且Destroy了,下次需要时直接使用Snapshots布署。
硬件模块
如GPS模块、WIFI模块、屏幕、基带芯片等。处于待机状态时,各模块耗电量极少。一旦唤醒屏幕,各模块便开始继续工作。
移动数据
典型的 3G 无线电网络有三种能量状态:
WakeLock
在Android系统中,为了节省电量资源,系统会通过休眠,关闭硬件等操作来降低功耗。例如在锁屏时,CPU会进入睡眠状态,后台任务停止运行,网络请求被中止。很多应用为了继续工作,使用PowerManager.WakeLock保持CPU运行,使CPU一直处于运行状态,大大增加了耗电量。
Battery Historian
Battery Historian是Google提供的开源的电量分析工具,目前仅支持Android 5.0及以上的设备。
安装步骤参见官方文档:Battery Historian
在获取手机电量统计数据之前需清空历史数据并开启详细日志:
1 | adb shell dumpsys batterystats --reset |
1 | adb shell dumpsys batterystats --enable full-wake-history |
获取电量统计数据,注意Android7.0及以上版本获取方式有所不同
1 | #Android7.0+ |
1 | #Android6.0- |
上传数据并分析,需要注意的是Battery Historian需要科学上网才能提交分析。
横坐标代表时间范围,纵坐标参数说明请参考Android性能专项测试之battery-historian。
由下图可知,CPU一直处于运行状态,消耗了大量电量。移动数据请求频繁,无线电波持续工作,导致电量一直被消耗。
在App Selection中选择要分析的应用,在App Stats中Device estimated power use可得知APP耗电量为**8.14%**。
经过排查,最终定位在日志上报及注册设备ID的请求过于频繁,并做以下优化。
*降低网络请求频率,集中处理网络请求,可通过Job Scheduler来实现调度。
*检查WakeLock唤醒锁,避免不必要唤醒操作。
*将数据上报等非重要性工作,在WIFI下或接入电源时执行。
在同一时间范围,对比可得可见CPU运行不再频繁,App耗电量下降至**2.62%**。
去年的这个时候和一帮同事在海上世界跨年,挥别了2015,迎来了2016,一起抢新年红包。各自默默许下新年的愿望,又相继消失在人海里。也许是狂欢过后的落寞,依稀记得地铁上还给某人打了一个电话。
曾经热爱的骑行运动在这一年里也逐渐荒废。年初约了两好友骑过一次车,去了一些没有去过的地方,聊了一些没有聊过的话题,喜欢这种在路上的感觉,走走停停,去探索去发现,总能让人觉得身心愉悦。本打算趁国庆长假去环一次青海湖,重温环湖岁月,和好友再三商量之后还是决定放弃,不适合的天气加上没有充分作好准备迫使我们只能推迟计划。
春节回了趟老家,走亲访友,不亦乐乎。众多亲戚欢聚一堂,其乐融融。约上三五好友,扯天扯地,相聚甚欢,同窗的时光仿佛是在昨天。也只有在那座城市,我才会感觉到自由、感觉到亲切,温暖与幸福。童年、青春、爱情,一一层叠起来,堆彻成回忆的高度。
这年的除夕是在武汉亲戚家过的,两家子人,守在一起看春晚,热闹非凡,这样的场景还是第一次。在武汉约见了一些初中同学,感慨良多。时光匆匆,物是人非,各自的生活已是千差万别,对彼此的记忆也只停留在年少之时。
作为一个不怎么喝酒的人,这一年里喝过的酒远大于过去的总和。第一次喝醉酒,是公司两周年的庆典活动上,很难受又很兴奋。作为一个不怎么喝酒的人,2017我将继续保持不喝酒。
逐渐开始意识到健康的重要些,长期缺乏运动,身体质素大不如以前。大南山拓展,超越了自己体力的极限,背着登山包,翻山越岭,徒步几十公里,累到极点,回来后休息一周才恢复。年中的时候,下定决心办了一张健身卡。坚持了半年,体能提高了不少,当然也长了一点肌肉。
身边的朋友在这一年一一脱单,还见证了一个朋友求婚。喜欢过的人都相继结婚,唯有祝愿。
一年来同时间段入职的同事都相继离职,让人深感惋惜。不断加入的新同事,也增添了许多新面孔。我所在的客户端团队虽少,工作上大家都彼此默契地配合,工作之余也能一起愉快地玩耍。月复一月的迭代着版本,看似重复的工作,却每次都有不同的挑战。值得骄傲的是,在空闲之时,也做了许多提升工作效率的事。
不再盲目的追求新技术,开始关注应用的架构。努力提高代码可读性、可维护性。逐步深入去研究感兴趣的技术以及开源项目。
在Android中常用VideoView实现简单的视频播放功能,另一种方法是使用MediaPlayer + SurfaceView来实现,本质上VideoView对它俩进行了封装,使开发者不用关心其细节,降低开发难度。
写到这里,相信大家心里已经有了实现思路,想要实现带视频播放功能的引导页无非就是在ViewPager中每个页面添加VideoView,滑动到相应页面播放则对应的视频。
那么教程到此就可以结束了!!!
但是这么做会有一个问题,每次打开引导页播放在视频前会先黑屏一下,并且ViewPager滑动时两个页面之间会有明显的黑缝隙。作为一个有追求的开发者这显然是不能接受的。
TextureView又是个什么东东的呢?我们知道SurfaceView为了提高工作效率,它的工作方式是创建一个置于应用窗口之后的新窗口,SurfaceView的内容不在应用窗口上,所以不能使用变换(平移、缩放、旋转等)。也难以放在ListView或者ScrollView中,不能使用UI控件的一些特性比如View.setAlpha()。
作为SurfaceView的兄弟,TextureView终于在Android 4.0来救场了。
TextureView并没有创建新的窗口,可以像使用普通View一样执行变换操作,使用TextureView务必开启硬件加速。
科学证明使用MediaPlayer与TextureView能确保达到最佳效果。
视频文件
视频文件来源于闲鱼App
1 | //将视频文件放至在assets/guide目录中 |
GuideActivity
MediaPlayer的setOnPreparedListener()方法可以设置一个监听器,在视频预处理完成之后产生回调,这里我们可以将视频默认设置到第一帧,从而避免黑屏。
1 | public class GuideActivity extends AppCompatActivity { |
guide.xml
1 | <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
guide_one.xml、guide_two.xml、guide_three.xml、guide_four.xml
1 |
|
page.xml
1 |
|
page_now.xml
1 |
|
dimens.xml
1 | <dimen name="guide_big_font_size">25.7dp</dimen> |
本教程效果
闲鱼App效果
Groovy是一门动态语言,同Java一样运行在JVM上,Groovy的语法与Java很相似,两者可以混合编程,而同样的任务使用相比Java所需的代码更少。Gradle选择Groovy来作为构建语言,是因为它给Java开发人员提供了迄今为止最大的透明度。
详细的语法介绍参见官方文档,本文只作简单介绍。
1 | //定义变量 |
1 | //单引号 |
1 | //Lists |
1 | //闭包语法格式 |
Gradle可以说是一个框架,定义了流程和规则,而且具体的编译工作则由插件来完成。比如常见的Java插件、Android插件。每个插件中又定义了各种task。
**projects (项目) **
我们的Android Application、Android Library、Java Library都是单个的Project,可以说每个待编译的工程都是一个Project。
以某项目为例,该项目总共有几个Project呢?答案是3个!
分别是app、scrollanimationlibrary、social_sdk_library_project。从目录结构我们可以看出每个Project对应一个build.gradle文件,该文件定义了Project的编译规则。根目录下build.gradle用于配置子Project,settings.gradle文件声明包括多少个子Project,整体上这就是一个MultiProject。
1 | ├── app |
使用projects
命令查看project
1 | ------------------------------------------------------------ |
task (任务)
一个task代表更细化的构建,可能是编译一些 classes, 创建一个 JAR, 生成 javadoc, 或者生成某个目录的压缩文件。
查看Project包含的任务可以使用task
命令,查看具体某个Project的任务使用project-name:task
命令,如app:task
,执行某个task使用task-name
,如assemble
。
1 | Parallel execution with configuration on demand is an incubating feature. |
创建任务
1 | //普通写法 |
从API的角度来理解,Gradle有三种对象,每种对象对应不同的脚本文件。
./gradlew task
。settings.gradle
1 | //指定包含的子project |
MultiProject build.gradle
1 | //全局配置 |
Project build.gradle
1 | //Android 插件 |
指定.so库
1 | android{ |
配置apk签名
1 | android{ |
构建不同类型的版本
通过添加applicationIdSuffix ".debug"
修改包名,可以使内测版与正式版同时安装在手机上。
1 | android { |
多渠道打包
1 | //在AndroidManifest.xml里配置PlaceHolder |
引用其它gradle配置
1 | apply from: '../tinker-patch.gradle' |
扩展属性
1 | //root project build.gradle配置ext属性 |
BuildConfig
1 | //gradle 中定义BuildConfig字段 |
Android Wear是Android操作系统的一个分支版本,专为智能手表等可穿戴式电脑设备所设计,由Google主导开发。
我们身处一个最好的时代,也是最坏的时代,智能硬件的普及,极大改善了人们的生活体验,以前只有在科幻电影中才可能出现的场景,也逐渐变为现实进入人类的生活。
在MOTO 360刚推出之时,刷新了我对手表的认识,Android Wear使得手表有了更多的可实现的功能,使得手表成为手机功能的延伸,某些功能的操作甚至不再需要手机,比如消息的阅读与回复,查看天气,导航、打车等等。
Android Wear项目同时包含mobile、wear两部分,这里同时需要勾选Phone and Tablet、Wear,Minimum SDK这里都选API21。
wear部分默认选Add No Activity
由于我的Android Wear手表为圆形,这里就以圆形表盘为例
表盘需要WAKE_LOCK
权限,在mobile和wear Module的manifest文件中声明权限。
1 | <manifest> |
1 | public class MyWatchFaceService extends CanvasWatchFaceService { |
1 | <service |
详细代码见smartisan_watchface
assembleDebug
命令生成debug包adb connect
命令连接手表进行调试adb instll
命令将表盘安装至手表assembleRelease
命令生成release包adb instll
命令将表盘安装至手表在Android开发的世界中,依赖管理是每个开发人员不得不面对的问题,无论是外部的开源类库依赖,还是项目内部的模块间的依赖,都需要有效的管理。在Android中不仅可以依赖module
、so
、jar
还可以依赖aar
。既可引用远程仓库jcenter()
、mavenCentral()
、也可以引用私有仓库、本地仓库。使用Maven可以有效地解决依赖管理。
使用方式
1 | dependencies { |
包结构
POM文件,包含maven包的相关信息
1 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
Maven仓库用于存放Maven包,可以是本地的,远程的、私有的、公开的。
1 | repositories { |
下载nexus,选择nexus-3.0.1-01-mac.tgz
解压文件,进入nexus-3.0.1-01/bin
目录,输入./nexus start
运行服务,nexus默认监听端口8081。
浏览器中访问http://your ip:8081
nexus管理员主界面,默认帐号admin
,密码admin123
仓库类型
hosted,本地仓库,通常我们会部署自己的构件到这一类型的仓库。比如公司的第二方库。
proxy,代理仓库,它们被用来代理远程的公共仓库,如maven中央仓库。
group,仓库组,用来合并多个hosted/proxy仓库,当你的项目希望在多个repository使用资源时就不需要多次引用了,只需要引用一个group即可。
使用方法参考chrisbanes/gradle-mvn-push 插件
在项目根目录新建gradle.properties
文件
使用如下配置
1 | # 项目名称 |
根目录下新建gradle-mvn-push.gradle
文件
使用如下配置
1 | apply plugin: 'maven' |
在build.gradle
中使用插件
1 | apply from: '../gradle-mvn-push.gradle' |
执行gradle task进行上传操作
1 | $ ./gradlew clean build uploadArchives |
1 | maven { url "http://172.16.1.19:8081/repository/maven-releases/" } |
1 | dependencies { |
我叫陈龙飞,是一名Android开发者。喜欢骑行,梦想着有机会骑行川藏,环一次青海湖和海南岛。业余健身爱好者,用崔健的话说“一周跑步三次加一次游泳”。酷爱美剧,《权力的游戏》《行尸走肉》期待更新中。
明亮的环境,宽阔的桌面,舒服的椅子。高层,靠窗,面朝大海。
洗澡、睡觉前。这些时候思考得会多点。
Keep健身软件,健康的身体才是一切的根本。
]]>本文参与了「利器社群计划」,发现更多创造者和他们的工具:http://liqi.io/community/
Jenkins是一个开源的持续集成工具,提供软件开发的持续集成服务。
在移动端开发过程中,临近发版时,需要频繁进行集成操作,打包app提交给测试,测试反馈bug,开发修复bug后再重新打包给测试,如此反复操作消耗了大量时间。
而持续集成正是针对这类问题的最佳解决方案,使用持续集成可以自动化实现编译、发布、测试。
Jenkins提供了各种安装程序,这里以Docker为例。
下载Docker GUI工具kitematic
搜索Jenkins获取镜像,并选择create创建容器
选择安装方式
创建用户并登录
首页选择新建,创建一个项目
]]>
面向切面编程(AOP,Aspect-oriented programming) 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
假设有这样一个需求,需要统计所有的点击事件并进行上报。一般情况下会在所有的点击事件中调用事件上报的方法,这使得代码之间的耦合度非常之高,将来修改事件上报的方法,所有调用该方法的地方都得修改。
AOP的目标就是解决这些问题,把这些功能集中起来进行管理。比如上面所说的点击事件上报将使用AOP统一管理。
每个页面点击事件中都需要调用事件上报的方法。
1 | public class AOPActivity extends AppCompatActivity implements View.OnClickListener { |
AspectJ是一套支持AOP的语言,完全兼容Java。
使用AspectJ的两种方法:
Join Points 程序运行时的一些执行点。比如函数调用、函数执行等等。
Pointcuts 一个程序会有很多个JPoints,不是每一个JPonints都是我们所需要的,而选择感兴趣的JPonints就是Pointcuts提供的功能。
Join Points类型与之对应的Pointcuts:
Join point category | Pointcut syntax |
---|---|
Method execution(函数执行) | execution(MethodSignature) |
Method call(函数调用) | call(MethodSignature) |
Constructor execution(构造函数执行) | execution(ConstructorSignature) |
Constructor call(构造函数调用) | call(ConstructorSignature) |
Class initialization(类初始化) | staticinitialization(TypeSignature) |
Filed read access(获取变量) | get(FiledSignature) |
Filed write access(设置变量) | set(FiledSignature) |
Exception handler execution(异常处理) | handler(TypeSignature) |
Object initialization(Object 在构造函数中做得工作) | initialization(ConstructorSignature) |
Object pre-initialization | preinitialization(ConstructorSignature) |
Advice execution | adviceexecution() |
Advice 选择Join Points后要做的事情:
关键词 | 说明 |
---|---|
before() | Join Points执行之前需要做的事情 |
after() | Join Points执行之后需要做的事情 |
after():returning(返回值类型) after():throwing(异常类型) | 假设JPoint是一个函数调用的话,那么函数调用执行完有两种方式退出,一个是正常的return,另外一个是抛异常。注意,after()默认包括returning和throwing两种情况 |
返回值类型 around() | around替代原Join Points,如果要执行原JPoint的话,需要调用proceed |
使用AspectJ Gradle插件gradle-android-plugin-aspectjx
选择切入的目标函数:
访问权限(public/private/protect,以及static/final)属于可选项。如果不设置它们,则默认都会选择。
返回值类型就是普通的函数的返回值类型。如果不限定类型的话,就用*
通配符表示。
包名.函数名用于查找匹配的函数。可以使用通配符,包括*
和..
以及+
号。其中*
号用于匹配除.
号之外的任意字符,而..
则表示任意子package,+
号表示子类。
@Pointcut("execution(* *.onClick(android.view.View)) && args(view)")
表示Join Points类型为execution,目标函数为任意包下返回值为任意类型的名为onClick的函数,其中参数类型为View
@Around("onClickPointcut(view)")
替换原Join Points
1 |
|
使用AOP我们就不用给所有点击事件添加上报点击事件的方法,而是统一使用AOP管理。
]]>1 | /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" |
安装方法一:
1
brew install node
安装方法二:(推荐)
首先安装nvm(node.js版本管理)
1
brew install nvm
安装最新版本node.js
1
2//(nvm alias default node指定全局版本)
nvm install node && nvm alias default node
1 | brew install watchman |
1 | brew install flow |
1 | //(-g指定为全局安装) |
ios/AwesomeProject.xcodeproj
并点击Run按钮index.ios.js
并随便改上几行⌘-R
就可以刷新APP并看到你的最新修改```
$ cd AwesomeProject1 | * ``` |
index.android.js
并随便改上几行1 | adb logcat *:S ReactNative:V ReactNativeJS:V |
设备必须已经root
1.使用adb 执行以下命令,获取su权限
1 | adb shell |
2.修改 /data/local目录权限
1 | chmod 777 data/local |
3.将tcpdump文件push到/data/local目录下
1 | adb push /Users/chenlongfei/Downloads/tcpdump /data/local |
4.修改tcpdump文件权限
1 | chmod 6755 /data/local/tcpdump |
5.执行抓包监听命令,并将pcap文件保存到sdcard目录中
1 | /data/local/tcpdump -p -s 0 -w /sdcard/test.pcap |
6.使用control+z 停止抓包
7.从设备中获取抓包文件
1 | adb pull /sdcard/test.pcap /Users/chenlongfei/Downloads |
至此抓包就结束了,PC上使用Wireshark对文件进行分析。
设备已经越狱
1.在Cydia中安装OpenSSH、tcpdump
2.查看设备IP
3.连接到设备
1 | ssh root@192.168.1.103 |
4.开始抓包
1 | tcpdump -p -s 0 -w /test.pcap |
5.使用pp助手导出pcap文件即可
教程结束
]]>下面介绍不同版本相应的方法:
继承PreferenceActivity重写onCreate方法添加如下代码:
1 |
|
activity_toolbar.xml内容:
1 |
|
这里给出一个更简单的方法:
1 |
|
activity_toolbar.xml内容:
1 |
|
1 | public class SettingsActivity extends ActionBarActivity { |
布局文件:
1 |
|
Preference xml:
1 |
|
好了,收工。
效果图如下: