2021年三季度国内生产总值(GDP)初步核算结果

   根据有关基础资料和国民经济核算方法,我国2021年三季度国内生产总值(以下简称GDP)初步核算主要结果如下: 表1  2021年三季度GDP初步核算数据    现价总量(亿元) 比上年同期增长(%) 3季度 1-3季度 3季度 1-3季度 GDP 290964 823131 4.9 9.8 *产业 23028 51430 7.1 7.4 第二产业 113786 320940 3.6 10.6 第三产业 154150 450761 5.4 9.5           农林牧渔业 24007 53758 7.1 7.4 工业 93034 267640 4.9 12.0 #制造业 77373 225896 4.6 12.5 建筑业 21202 54538 -1.8 4.3 批发和零售业 27972 78867 7.6 13.5 交通运输、仓储和邮政业 12753 35457 5.9 15.3 住宿和餐饮业 4830 13058 5.7 19.4 金融业 23398 69035 4.0 4.5 房地产业 18775 58517 -1.6 8.2 信息传输、软件和信息技术服务业 10488 33675 17.1 19.3 租赁和商务服务业 8663 24127 5.8 6.5 其他行业 45841 134458 5.2 6.7 注: 1.总量按现价计算,增速按不变价计算; 2.三次产业分类依据国家统计局2018年修订的《三次产业划分规定》; 3.行业分类采用《国民经济行业分类(GB/T 4754-2017)》; 4.本表GDP总量数据中,有的不等于各产业(行业)之和,是由于数值修约误差所致,未作机械调整。  表2  GDP同比增长速度  单位:% 年份 1季度 2季度 3季度 4季度 2016 6.9 6.8 6.8 6.9 2017 7.0 7.0 6.9 6.8 2018 6.9 6.9 6.7 6.5 2019 6.3 6.0 5.9 5.8 2020 -6.8 3.2 4.9 6.5 2021 18.3 7.9 4.9   注:同比增长速度为与上年同期对比的增长速度。  表3  GDP环比增长速度  单位:% 年份 1季度 2季度 3季度 4季度 2016 1.4 2.0 1.7 1.6 2017 1.8 1.7 1.6 1.7 2018 1.8 1.6 1.3 1.6 2019 1.6 1.2 1.3 1.6 2020 -9.5 10.7 2.9 3.2 2021 0.2 1.2 0.2   注:环比增长速度为经季节调整后与上一季度对比的增长速度。    其他相关核算结果详见国家统计局数据库(http://data.stats.gov.cn)。   附件: 中国GDP季度核算说明   1.季度GDP核算概述   1.1 基本概念   GDP是一个国家所有常住单位在一定时期内生产活动的*终成果。GDP是国民经济核算的核心指标,也是衡量一个国家经济状况和发展水平的重要指标。   GDP核算有三种方法,即生产法、收入法和支出法,三种方法从不同的角度反映国民经济生产活动成果。生产法是从生产过程中创造的货物和服务价值中,剔除生产过程中投入的中间货物和服务价值,得到增加值的一种方法。国民经济各行业生产法增加值计算公式如下:增加值=总产出-中间投入。将国民经济各行业生产法增加值相加,得到生产法国内生产总值。收入法是从生产过程形成收入的角度,对生产活动成果进行核算。按照这种计算方法,增加值由劳动者报酬、生产税净额、固定资产折旧和营业盈余四个部分组成。计算公式为:增加值=劳动者报酬+生产税净额+固定资产折旧+营业盈余。国民经济各行业收入法增加值之和等于收入法国内生产总值。支出法是从生产活动成果*终使用的角度计算国内生产总值的一种方法。*终使用包括*终消费支出、资本形成总额及货物和服务净出口三部分。   国家统计局发布的季度GDP是以生产法为基础核算的结果。   1.2 核算范围   1.2.1 生产范围   GDP核算的生产范围包括以下四个部分:*,生产者提供或准备提供给其他单位的货物或服务的生产;第二,生产者用于自身*终消费或固定资本形成的所有货物的自给性生产;第三,生产者为了自身*终消费或固定资本形成而进行的知识载体产品的自给性生产,但不包括住户部门所从事的类似的活动;第四,自有住房提供的住房服务,以及雇佣有酬家庭服务人员提供的家庭和个人服务的自给性生产。生产范围不包括没有报酬的家庭和个人服务、没有单位控制的自然活动(如野生的、未经培育的森林、野果或野浆果的自然生长,公海中鱼类数量的自然增长)等。   1.2.2 生产活动主体范围   GDP生产活动主体范围包括了中国经济领土范围内具有经济利益中心的所有常住单位。本报告中的季度GDP数据是由国家统计局负责核算的全国数据,未包括香港、澳门特别行政区和台湾省的地区生产总值数据。   1.3 核算单位   GDP核算主要以法人单位作为核算单位,在核算中依据法人单位从事的主要活动将其划分到不同的行业,分别计算各个行业的增加值,再将各行业增加值汇总得到GDP。   1.4 核算频率   核算频率为季度。中国从1992年1季度开始到2015年2季度,采用累计核算方式核算季度GDP,即分别计算各年1季度,1-2季度,1-3季度和1-4季度的GDP数据,1-4季度GDP初步核算即为年度GDP初步核算。从2015年3季度开始改为分季核算方式,即分别计算各年1季度,2季度,3季度和4季度的GDP数据,累计数据通过当季数据相加得到。   从2011年1季度开始,国家统计局正式对外发布各季GDP环比增长速度。   1.5 法律依据和制度规定   GDP核算严格遵守《中华人民共和国统计法》的规定。目前,中国GDP是按照《中国国民经济核算体系(2016)》的要求进行测算的,该体系采纳了联合国《国民账户体系(2008)》的基本核算原则、内容和方法。   1.6 保密性   依照《中华人民共和国统计法》*章第九条的规定,统计机构和统计人员对在统计工作中知悉的国家秘密、商业秘密和个人信息,应当予以保密。   国民经济核算人员在进行GDP核算时对所使用的未经公开的专业统计数据和行政记录数据严格保密,在GDP核算数据发布前对当期GDP数据也严格保密。   1.7 用户需求   季度GDP数据的国内用户主要是政府部门、研究机构、大学、行业协会、媒体以及社会公众。此外,国家统计局定期向联合国、国际货币基金组织、经济合作与发展组织、亚洲开发银行等国际组织提供中国季度GDP数据。   2.季度GDP核算方法   2.1 分类体系   在季度GDP核算中,行业划分依据中国国民经济行业分类标准和三次产业划分标准,并采用两种分类方式。   *种分类是国民经济行业分类,采用国家标准管理部门2017年颁布的《国民经济行业分类(GB/T 4754-2017)》。在实际核算中采用两级分类。   *级分类以国民经济行业分类中的门类为基础,分为农、林、牧、渔业,工业,建筑业,批发和零售业,交通运输、仓储和邮政业,住宿和餐饮业,金融业,房地产业,信息传输、软件和信息技术服务业,租赁和商务服务业,其他行业等11个行业。其中工业包含采矿业,制造业,电力、热力、燃气及水生产和供应业3个门类行业;其他行业包含科学研究和技术服务业,水利、环境和公共设施管理业,居民服务、修理和其他服务业,教育,卫生和社会工作,文化、体育和娱乐业,公共管理、社会保障和社会组织等7个门类行业。   第二级分类在*级分类的基础上,将国民经济行业分类中的一部分门类细化为行业大类。   第二种分类是三次产业分类,依据国家统计局2018年修订的《三次产业划分规定》,分为*产业、第二产业和第三产业。*产业是指农、林、牧、渔业(不含农、林、牧、渔专业及辅助性活动);第二产业是指采矿业(不含开采专业及辅助性活动),制造业(不含金属制品、机械和设备修理业),电力、热力、燃气及水生产和供应业,建筑业;第三产业即服务业,是指除*产业、第二产业以外的其他行业(剔除国际组织)。   2.2 资料来源   在季度GDP核算时,将所有可以在核算时获得的、适用的经济统计调查数据都用于GDP核算。资料来源主要包括两部分:   一是国家统计调查资料,指由国家统计系统实施的统计调查获得的各种统计资料,如农林牧渔业、工业、建筑业、批发和零售业、住宿和餐饮业、房地产业、规模以上服务业等统计调查资料、人口与劳动工资统计资料、价格统计资料等。   二是行政管理部门的行政记录资料,主要包括:财政部、人民银行、税务总局、银保监会、证监会等行政管理部门的相关数据,例如人民银行的金融机构本外币信贷收支情况、税务总局分行业的税收资料等。   2.3 核算方法   2.3.1 现价增加值核算方法   根据资料来源情况,季度现价增加值核算主要采用增加值率法、相关价值量指标推算法以及利用不变价推算现价等方法。   2.3.1.1 相关价值量指标速度推算法   相关价值量指标速度推算法是利用相关价值量指标的现价增长速度推算现价增加值的增长速度,然后用上年同期现价增加值乘以推算出的现价增加值增长速度得出当期现价增加值,计算公式为:   现价增加值=上年同期现价增加值×(1+现价增加值增长速度)   其中,现价增加值增长速度,根据本期相关价值量指标现价增长速度,以及以前年度现价增加值增长速度和相关价值量指标的现价增长速度之间的数量关系确定。   2.3.1.2 利用不变价推算现价方法   先利用物量指数外推法求得本期不变价增加值,再根据相关价格指数推算现价增加值。计算公式为:   现价增加值=不变价增加值×价格指数   2.3.2 不变价增加值核算方法   不变价增加值是把按当期价格计算的增加值换算成按某个固定期(基期)价格计算的价值,从而剔除价格变化因素的影响,以使不同时期的价值可以比较。不变价增加值采用固定基期方法计算,目前每5年更换一次基期,2021年至2025年不变价增加值的基期是2020年。   季度不变价增加值核算主要采用价格指数缩减法和相关物量指数外推法。   2.3.2.1 价格指数缩减法   利用相关价格指数直接缩减现价增加值,计算不变价增加值,计算公式为:   某行业不变价增加值=该行业现价增加值÷价格指数   2.3.2.2 物量指数外推法   利用相关物量指标的增长速度推算不变价增加值的增长速度,然后用上年同期不变价增加值和推算出的不变价增加值增长速度计算得出当期不变价增加值,计算公式为:   某行业不变价增加值=该行业上年同期不变价增加值 ×(1+该行业不变价增加值增长速度)   其中,不变价增加值增长速度根据本期相关物量指标增长速度,以及以前年度不变价增加值增长速度和相关物量指标的增长速度之间的数量关系确定。   2.4 季节调整   GDP环比增长速度是季度增加值与上一个季度增加值数据对比的结果。在测算时,须剔除季节性因素对时间序列的影响,利用国家统计局版季节调整软件(NBS-SA)对时间序列进行季节调整。NBS-SA是在目前国际上比较常用的季节调整软件的基础上,考虑了中国特有的季节因素研制而成的。该软件添加了处理中国特有的季节因素的新模块,有效剔除了中国特有的季节因素,包括春节、端午、中秋等移动假日因素、周工作天数从原来的6天制到5天制转变的因素、假期变动及调休带来的变化因素等。   3.季度GDP数据修订   3.1 修订的必要性   季度GDP初步核算对时效性要求很强,一般在季后15天左右公布,这时,GDP核算所需要的基础资料不能全部获得,因此季度GDP初步核算利用专业统计进度资料和相关指标推算得到。之后,随着可以获得的基础资料不断增加和完善,会利用更加完整的基础资料,例如,专业统计年报、行业财务资料和财政决算资料对GDP数据进行修订,使其更加准确地反映经济发展实际情况。   3.2 修订程序   按照国家统计局*新改革的GDP核算和数据发布制度规定,中国季度GDP核算分为初步核算和*终核实两个步骤。通常,年度GDP*终核实后,要对季度数据进行修订,称为常规修订;在开展全国经济普查,发现对GDP数据有较大影响的新的基础资料,或计算方法及分类标准发生变化后而对年度GDP历史数据进行修订后,也要对季度GDP历史数据进行相应修订,称为全面修订。   3.3 修订方法   3.3.1 当季数据的修订   中国目前对季度GDP数据修订的方法是比例衔接法,即利用年度基准值与年内四个季度汇总数的差率调整季度数据的方法。比例衔接法的基本做法是:首先对国民经济各行业现价和不变价增加值分别进行衔接,GDP和三次产业增加值是衔接后的行业增加值的加总。不变价GDP和不变价三次产业增加值的衔接方法与现价相同。   3.3.2 环比数据的修订   由于季节调整的对象是时间序列数据,因此,当时间序列中任何一个季度数据发生变化时,都会影响季节调整的结果;在时间序列中加入*新的一个季度的数据,也会使以前季度的环比数据或多或少地发生变化,这是模型自动修正的结果。根据季节调整原理,一般情况下,离*新数据时间较近的时期,数据受影响较大;离*新数据时间较远的时期,数据受影响较小。为便于用户使用,在发布当期环比数据的同时,会通过国家统计局网站发布修订后的以前季度的环比数据。   4.季度GDP数据质量评估   4.1 对基础数据的评估   对于GDP核算所使用的各专业统计数据和行政记录数据,有关部门都会对其质量进行检验,确保数据合理反映经济发展实际情况。当GDP核算部门得到这些基础数据后,会再次对数据的完整性和准确性进行检验,确保这些数据符合GDP核算的概念和要求。   4.2 对核算方法的评估   在GDP核算中,GDP核算部门会根据不断发展的中国经济实际情况,依据不断完善的国民经济核算标准,对中国的季度GDP核算方法进行修订,以确保核算方法的合理性。   4.3 对核算结果的评估   在得到季度GDP核算结果后,要对GDP各构成项目数据、GDP数据与相关专业、部门统计数据以及宏观数据的协调性进行检验,保证GDP数据和其他主要数据的相互协调和匹配。正在建立以国民经济核算为核心框架,对各专业和部门基础统计数据进行评估的制度。   4.4 数据的可比性   《中国国民经济核算体系(2016)》采纳了联合国《国民账户体系(2008)》的基本核算原则、内容和方法,因而GDP数据具有国际可比性。   在开展全国经济普查或计算方法及分类标准发生变化后对季度GDP历史数据进行了修订,因此1992年1季度以来的季度GDP时间序列具有可比性。   5.季度GDP数据发布   5.1 发布时间   季度GDP初步核算数一般于季后15日左右发布,季度GDP*终核实数一般于隔年1月份发布。对于主要统计指标的发布,国家统计局会在年初发布的《主要统计信息发布日程表》中说明发布日期,GDP数据将按规定日程发布。   5.2 发布方式   季度GDP初步核算数在季度国民经济运行情况新闻发布会、国家统计局网站(www.stats.gov.cn)、《中国经济景气月报》上公布;季度GDP*终核实数在国家统计数据库(http://data.stats.gov.cn/)、《中国经济景气月报》上公布。对于1992年1季度以来的季度GDP数据时间序列,可以通过国家统计数据库(http://data.stats.gov.cn/)进行查询。 

使用ViewPager的setCurrentItem方法

修改ViewPager调用setCurrentItem时,滑屏的速度 ,解决滑动之间切换动画难看

 在使用ViewPager的过程中,有需要直接跳转到某一个页面的情况,这个时候就需要用到ViewPager的setCurrentItem方法了,它的意思是跳转到ViewPager的指定页面,但在使用这个方法的时候有个问题,跳转的时候有滑动效果,当需要从当前页面跳转到其它页面时,跳转页面跨度过大、或者ViewPager每个页面的视觉效果相差较大时,通过这种方式实现ViewPager跳转显得很不美观,怎么办呢,我们可以去掉在使用ViewPager的setCurrentItem方法时的滑屏速度,具体实现如下:

一、自定义一个Scroll类,用于控制ViewPager滑动速度:
import Android.view.animation.Interpolator;
import android.widget.Scroller;

public class FixedSpeedScroller extends Scroller {
private int mDuration = 0;

    public FixedSpeedScroller(Context context) {
super(context);
}

    public FixedSpeedScroller(Context context, Interpolator interpolator) {
super(context, interpolator);
}

    public FixedSpeedScroller(Context context, Interpolator interpolator, boolean flywheel) {
super(context, interpolator, flywheel);
}


@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
super.startScroll(startX, startY, dx, dy, mDuration);
}

@Override
public void startScroll(int startX, int startY, int dx, int dy) {
super.startScroll(startX, startY, dx, dy, mDuration);
}
}

二、在初始化ViewPager时,对ViewPager作如下设置:

* 设置ViewPager的滑动速度
*
* */
private void setViewPagerScrollSpeed( ){
try {
Field mScroller = null;
mScroller = ViewPager.class.getDeclaredField(“mScroller”);
mScroller.setAccessible(true);
FixedSpeedScroller scroller = new FixedSpeedScroller( mViewPager.getContext( ) );
mScroller.set( mViewPager, scroller);
}catch(NoSuchFieldException e){

}catch (IllegalArgumentException e){

}catch (IllegalAccessException e){

}
}

iOS逆向—-Mac m1芯片检测

由于Apple更新了m1芯片的Mac,导致iOS程序可以在Mac上直接运行,因此需要判断一下我们的App是跑在了Mac上还是跑在了iPhone上。
因为m1芯片的Mac上只能运行iOS14及以上系统(当然未来说不定有大能会移植低版本iOS系统到Mac上),因此我们只需要判断iOS14之后的系统即可:

var isMac = false
if #available(iOS 14.0, *) {
isMac = ProcessInfo.processInfo.isiOSAppOnMac
}
print(“\(isMac ? “App on Mac” : “App not on Mac”)!”)

当然,还可以直接获取设备型号:

 

#import <sys/utsname.h>
+ (void)deviceModel {
struct utsname systemInfo;
uname(&systemInfo);
NSString *deviceModel = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
NSLog(@”%@”, deviceModel);
}

“MacBookAir10,1”: “MacBook Air (M1, 2020)”,
“MacBookPro17,1”: “MacBook Pro (13-inch, M1, 2020)”,
“Macmini9,1”: “Mac mini (M1, 2020)”,

iOS逆向—-绕过越狱检测

当然有手动绕过越狱检测的办法,比如自己利用Fishhook或者OC的runtime机制去替换越狱检测的函数,甚至可以使用frida来Hook函数的返回值。这些都是比较稳妥的方法,本篇文章主要介绍的是自动绕过检测的工具xCon。

xCon会在iOS10.3以上系统的设备上导致Cydia崩溃,因此强烈建议先安装Filza后再安装xCon,避免Cydia崩溃后无法移除xCon的尴尬局面
iOS11之后的手机绕过越狱检测看这里
xCon可以从Cydia中安装,是目前为止*强大的越狱检测工具。由n00neimp0rtant与Lunatik共同开发,它据说patch了目前所知的所有越狱检测方法(也有不能patch的应用)。估计是由于影响太大,目前已不开放源码了。

安装方法:
首先在Cydia中添加源http://xcon.crazy.net
然后进入添加的源中选择xCon下载安装,安装完之后需要重启SpringBoard。

安装xCon后,会有两个文件xCon.dylib与xCon.plist出现在设备/Library/MobileSubstrate/DynamicLibraries目录下
进入/Library/MobileSubstrate/DynamicLibraries目录下,查看文件,发现xCon.dylib和xCon.plist

Skys-iPhone:/Library/MobileSubstrate/DynamicLibraries root# ls
MobileSafety.dylib* patcyh.dylib@ xCon.dylib*
MobileSafety.plist patcyh.plist xCon.plist

xCon.plist
该文件为过滤文件,标识在调用com.apple.UIKit时加载xCon.dylib
查看文件内容如下

Skys-iPhone:/Library/MobileSubstrate/DynamicLibraries root# cat xCon.plist
{
Filter = {
Bundles = (com.apple.UIKit);
};
}
Skys-iPhone:/Library/MobileSubstrate/DynamicLibraries root#

 

xCon.dylib
首先我们利用scp命令将文件拷贝到Mac上,然后使用otool工具将该文件的text section反汇编出来从而了解程序的具体逻辑:

➜ ~ scp -P2222 root@127.0.0.1:/Library/MobileSubstrate/DynamicLibraries/xCon.dylib ~/Desktop
root@127.0.0.1’s password:
xCon.dylib 100% 467KB 6.5MB/s 00:00
➜ ~ cd Desktop
➜ Desktop otool -tV xCon.dylib >xContextsection
➜ Desktop

可以根据文件中的函数名,同时结合该工具的原理以及越狱检测的一些常用手段来猜其逻辑,例如越狱检测方法中的文件系统检查,会根据特定的文件路径名来匹配,我们可以使用strings查看文件中的内容,看看会有哪些文件路径名。

➜ Desktop strings xCon.dylib
ipv4
ipv6
Not Found
%s [%s]
CFBundleIdentifier
CFBundleShortVersionString
__TEXT
__text
/System/Library/Lockdown/Services.plist
/var/mobile/Library/Caches/com.apple.mobile.installation.plist
/private/
/dev/
/bin/
/var/mobile/Library/Preferences
com.apple.
v16@?0@”NSString”4@”NSString”8^c12
/etc/fstab
/private/etc/fstab
/var/lib/xcon/fstab
/var/mobile/Applications/
/var/mobile/Containers/Data/Application/
_ptrace
/Applications
/Library/Activator
/Library/Flipswitch
/Library/Ringtones
/Library/Wallpaper
/Library/Switchs
/usr/libexec
/usr/lib/pam
/usr/arm-apple-darwin9
/usr/include
/usr/share
/usr/lib/python2.5
/var/lib/dpkg/info
/var/lib/xcon/paths-deny
……

通过分析,xCon会绕过以下越狱检测方法
(1)根据是否存在特定的越狱文件,及特定文件的权限是否发生变化来判断设备是否越狱
(2)根据沙箱完整性检测设备是否越狱
(3)根据文件系统的分区是否发生变化来检测设备是否越狱
(4)根据是否安装ssh来判断设备是否越狱

iOS逆向—-iOS12之后的静态砸壳

关于静态砸壳其实之前已经介绍过一个工具Clutch,但是由于年久失修,导致其在iOS11之后变得不可用,动不动就kill -9。于是*近又开始研究了一下,发现了一款新的静态砸壳工具flexdecrypt

首先连接到越狱的iPhone上,然后使用wget命令下载*新的deb(wget自行安装):

iPhone:/tmp root# wget https://github.com/JohnCoates/flexdecrypt/releases/download/1.1/flexdecrypt.deb

然后直接安装:

iPhone:/tmp root# dpkg -i flexdecrypt.deb
Selecting previously unselected package flexdecrypt.
(Reading database … 3858 files and directories currently installed.)
Preparing to unpack flexdecrypt.deb …
Unpacking flexdecrypt (1.1) …
Setting up flexdecrypt (1.1) …

安装完之后就可以使用了:

iPhone:/tmp root# flexdecrypt
Error: Missing expected argument ‘<file>’

OVERVIEW: A tool for decrypting apps and Mach-O binaries. Based on the Flex 3
jailbreak app’s source code.

USAGE: flex-decrypt <subcommand>

OPTIONS:
–version Show the version.
-h, –help Show help information.

SUBCOMMANDS:
file (default) Decrypt file.

See ‘flex-decrypt help <subcommand>’ for detailed help.

看描述,flexdecrypt应该不如clutchname智能,因此只能先cd到要砸壳的app目录下,然后再进行砸壳,具体如何找App的路径可以参考之前的博客,这里以砸Quantumult为例:

iPhone:/tmp root# cd /var/containers/Bundle/Application/
iPhone:/var/containers/Bundle/Application/ root# cd 6B8B93D5-DB46-4AAE-A264-F1C93A689B65
iPhone:/var/containers/Bundle/Application/6B8B93D5-DB46-4AAE-A264-F1C93A689B65 root# cd Quantumult.app/
iPhone:/var/containers/Bundle/Application/6B8B93D5-DB46-4AAE-A264-F1C93A689B65/Quantumult.app root# flexdecrypt Quantumult
Wrote decrypted image to /tmp/Quantumult

可以看到静态砸壳的速度是很快的,基本秒出,但是只有一行输出,告诉了你脱壳文件的位置,使用scp命令将文件取出:

➜ ~ scp -P 2222 root@127.0.0.1:”/tmp/Quantumult” ~/Desktop
root@127.0.0.1’s password:
Quantumult 100% 3235KB 34.9MB/s 00:00
➜ ~ cd Desktop
➜ Desktop otool -l Quantumult| grep crypt
cryptoff 16384
cryptsize 2703360
cryptid 0

可以看出已经成功脱壳。

IOS砸壳研究

1.https://github.com/BishopFox/bfdecrypt

Utility to decrypt App Store apps on jailbroken iOS 11.x

2.https://github.com/ivRodriguezCA/decrypt-ios-apps-script

Python script to SSH into your jailbroken device, decrypt an iOS App and transfer it to your local machine
This will decrypt the Instagram app and transfer it from your device to your ~/Desktop:

python ios_ssh.py –lport 2222 -p s3cr37 –app “Instagram”
This will give you an interactive shell to communicate with your device over USB:

python ios_ssh.py –lport 2222 -p s3cr37 -i

The missing guide to debug third party apps on iOS 12
如何调试第三方APP:https://medium.com/@felipejfc/the-ultimate-guide-for-live-debugging-apps-on-jailbroken-ios-12-4c5b48adf2fb

frida的cycript:https://github.com/nowsecure/frida-cycript

iOS应用砸壳

一、砸壳概述及其原理
APP上架会经历 APP->AppStore->源码 加密过程。

应用加密:开发者向AppStore提交应用,都将由苹果官方进行加密处理,以确保用户使用的APP都是经过审核过的,加密后的APP,开发人员无法通过Hopper等反编译应用,也无法使用class-dump。

iOS应用运行原理:应用在磁盘中是加密状态,由于CPU运行不会识别加密文件,因此在启动应用前需要在内核中对加密应用文件进行解密,提取可执行文件才可放入内存中运行。示意图如下:

%title插图%num

逆向需要对加密的二进制文件进行解密才可以做静态分析,解密过程即为砸壳。砸壳有两种:
1、静态砸壳
在已经掌握和了解加密应用的加密算法和逻辑后在不运行加密应用程序的前提下将壳应用程序进行解密处理,静态砸壳方法难度大,并且加密方发现应用被破解后就可能会改用更高级的复杂加密算法。
2、动态砸壳
在运行进程内存中的可执行程序映像入手,将内存中的内容进行转储处理来实现砸壳。该方法相对简单,并且不用关心应用所使用的加密技术。

二、手机越狱
使用爱思助手或pp助手均可以对手机越狱,注意越狱时对应的系统版本号,根据自己系统版本号选择。
越狱后手机上会安装Cydia软件,在软件中选择搜索搜索OpenSSH安装,安装完成后便可以与Mac端通过ssh建立连接。
1、通过工具越狱后会出现h3lix应用:

%title插图%num

2、点击运行h3lix,再点击按钮运行,将会下载安装Cydia应用:

%title插图%num

3、点击Cydia应用查看是否安装OpenSSL,如未安装则搜索安装,如下:

%title插图%num

通过以上1、2、3步骤之后Mac终端就可以和越狱手机进行ssh连接了。命令如下:

ssh root@192.168.1.168 -p 22
1
默认密码:alpine
修改密码执行passwd 输入新密码:yahibo
进入手机中操作与Linux命令相同
注意:手机重启后Cydia应用会失效,需要重新执行第2步重新安装。

三、砸壳工具Clutch使用(动态砸壳)
注意:会遇到砸壳失败,要确定是否在App Store下载,如果不是重新下载。
Clutch需要使用iOS8.0以上的越狱手机应用。
下载链接:https://github.com/KJCracks/Clutch/releases
1、下载后将Clutch-2.0.4放到越狱手机的usr/bin目录下(密码默认为alphine)

scp Clutch-2.0.4 root@192.168.1.168:/usr/bin/
1
2、ssh连接苹果设备,命令如下:

ssh root@192.168.1.168
1
3、进入usr/bin目给Clutch-2.0.4赋予可执行权限:

chmod +x Clutch-2.0.4
1
4、执行Chmod命令查看命令参数:

%title插图%num

Clutch-2.0.4
1

-b 只转储二进制文件
-d 转储应用的.ipa文件即砸壳,砸壳后获取.ipa文件的路径,将.ipa拉到本地即可
-i 获取安装的应用

5、列举安装的应用:

Clutch-2.0.4 -i
1%title插图%num

6、开始砸壳,选择编号2对微信进行砸壳:

Clutch-2.0.4 -d 2
1
运行如下:

%title插图%num

*后生成一个砸壳后的包路径:
/private/var/mobile/Documents/Dumped/com.tencent.xin-iOS9.0-(Clutch-2.0.4).ipa
将Dumped文件下载到Mac中:

scp -r root@192.168.1.168:/private/var/mobile/Documents/Dumped ipas/
1
7、将获取到的.ipa修改为wxtest.zip并解压,进入wxtest文件中的Payload中,执行命令:

class-dump -H WeChat.app -o apph
1
结束后apph中为微信对应的所有头文件信息。如图:

apph中为微信对应的所有头文件信息。如图:

%title插图%num

Interpolator的用法

Interpolator的用法

Interpolator这个东西很难进行翻译,直译过来的话是补间器的意思,它的主要作用是可以控制动画的变化速率,比如去实现一种非线性运动的动画效果。那么什么叫做非线性运动的动画效果呢?就是说动画改变的速率不是一成不变的,像加速运动以及减速运动都属于非线性运动。

 

不过Interpolator并不是属性动画中新增的技术,实际上从Android 1.0版本开始就一直存在Interpolator接口了,而之前的补间动画当然也是支持这个功能的。只不过在属性动画中新增了一个TimeInterpolator接口,这个接口是用于兼容之前的Interpolator的,这使得所有过去的Interpolator实现类都可以直接拿过来放到属性动画当中使用,那么我们来看一下现在TimeInterpolator接口的所有实现类,如下图所示:

 

%title插图%num

 

可以看到,TimeInterpolator接口已经有非常多的实现类了,这些都是Android系统内置好的并且我们可以直接使用的Interpolator。每个Interpolator都有它各自的实现效果,比如说AccelerateInterpolator就是一个加速运动的Interpolator,而DecelerateInterpolator就是一个减速运动的Interpolator。

 

我觉得细心的朋友应该早已经发现了,在前面两篇文章当中我们所学到的所有属性动画,其实都不是在进行一种线程运动。比如说在“上”篇文章中使用ValueAnimator所打印的值如下所示:

 

%title插图%num

 

可以看到,一开始的值变化速度明显比较慢,仅0.0开头的就打印了4次,之后开始加速,*后阶段又开始减速,因此我们可以很明显地看出这一个先加速后减速的Interpolator。

 

那么再来看一下在“中”篇文章中完成的小球移动加变色的功能,如下图所示:

 

%title插图%num

从上图中我们明显可以看出,小球一开始运动速度比较慢,然后逐渐加速,中间的部分运动速度就比较快,接下来开始减速,*后缓缓停住。另外颜色变化也是这种规律,一开始颜色变化的比较慢,中间颜色变化的很快,*后阶段颜色变化的又比较慢。

 

从以上几点我们就可以总结出一个结论了,使用属性动画时,系统默认的Interpolator其实就是一个先加速后减速的Interpolator,对应的实现类就是AccelerateDecelerateInterpolator。

 

当然,我们也可以很轻松地修改这一默认属性,将它替换成任意一个系统内置好的Interpolator。就拿“中”篇文章中的代码来举例吧,MyAnimView中的startAnimation()方法是开启动画效果的入口,这里我们对Point对象的坐标稍做一下修改,让它变成一种垂直掉落的效果,代码如下所示:

  1. private void startAnimation() {  
  2.     Point startPoint = new Point(getWidth() / 2, RADIUS);  
  3.     Point endPoint = new Point(getWidth() / 2, getHeight() – RADIUS);  
  4.     ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);  
  5.     anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
  6.         @Override  
  7.         public void onAnimationUpdate(ValueAnimator animation) {  
  8.             currentPoint = (Point) animation.getAnimatedValue();
  9.             invalidate();
  10.         }
  11.     });
  12.     anim.setDuration(2000);  
  13.     anim.start();
  14. }

这里主要是对Point构造函数中的坐标值进行了一下改动,那么现在小球运动的动画效果应该是从屏幕正中央的顶部掉落到底部。但是现在默认情况下小球的下降速度肯定是先加速后减速的,这不符合物理的常识规律,如果把小球视为一个自由落体的话,那么下降的速度应该是越来越快的。我们怎样才能改变这一默认行为呢?其实很简单,调用Animator的setInterpolator()方法就可以了,这个方法要求传入一个实现TimeInterpolator接口的实例,那么比如说我们想要实现小球下降越来越快的效果,就可以使用AccelerateInterpolator,代码如下所示:

  1. private void startAnimation() {  
  2.     Point startPoint = new Point(getWidth() / 2, RADIUS);  
  3.     Point endPoint = new Point(getWidth() / 2, getHeight() – RADIUS);  
  4.     ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);  
  5.     anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
  6.         @Override  
  7.         public void onAnimationUpdate(ValueAnimator animation) {  
  8.             currentPoint = (Point) animation.getAnimatedValue();
  9.             invalidate();
  10.         }
  11.     });
  12.     anim.setInterpolator(new AccelerateInterpolator(2f));  
  13.     anim.setDuration(2500);  
  14.     anim.start();
  15. }

代码很简单,这里调用了setInterpolator()方法,然后传入了一个AccelerateInterpolator的实例,注意AccelerateInterpolator的构建函数可以接收一个float类型的参数,这个参数是用于控制加速度的。现在运行一下代码,效果如下图所示:

 

%title插图%num

 

OK,效果非常明显,说明我们已经成功替换掉了默认的Interpolator,AccelerateInterpolator确实是生效了。但是现在的动画效果看上去仍然是怪怪的,因为一个小球从很高的地方掉落到地面上直接就静止了,这也是不符合物理规律的,小球撞击到地面之后应该要反弹起来,然后再次落下,接着再反弹起来,又再次落下,以此反复,*后静止。这个功能我们当然可以自己去写,只不过比较复杂,所幸的是,Android系统中已经提供好了这样一种Interpolator,我们只需要简单地替换一下就可以完成上面的描述的效果,代码如下所示:

  1. private void startAnimation() {  
  2.     Point startPoint = new Point(getWidth() / 2, RADIUS);  
  3.     Point endPoint = new Point(getWidth() / 2, getHeight() – RADIUS);  
  4.     ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);  
  5.     anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
  6.         @Override  
  7.         public void onAnimationUpdate(ValueAnimator animation) {  
  8.             currentPoint = (Point) animation.getAnimatedValue();
  9.             invalidate();
  10.         }
  11.     });
  12.     anim.setInterpolator(new BounceInterpolator());  
  13.     anim.setDuration(3000);  
  14.     anim.start();
  15. }

可以看到,我们只是将设置的Interpolator换成了BounceInterpolator的实例,而BounceInterpolator就是一种可以模拟物理规律,实现反复弹起效果的Interpolator。另外还将整体的动画时间稍微延长了一点,因为小球反复弹起需要比之前更长的时间。现在重新运行一下代码,效果如下图所示:

 

%title插图%num

 

OK!效果还是非常不错的。那么这里我们只是选了几个系统实现好的Interpolator,由于内置Interpolator非常多,就不一一进行讲解了,大家可以自己去使用一下其它的几种Interpolator来看一看效果。

 

但是,只会用一下系统提供好的Interpolator,我们显然对自己的要求就太低了,既然是学习属性动画的高级用法,那么自然要将它研究透了。下面我们就来看一下Interpolator的内部实现机制是什么样的,并且来尝试写一个自定义的Interpolator。

 

首先看一下TimeInterpolator的接口定义,代码如下所示:

  1. /** 
  2.  * A time interpolator defines the rate of change of an animation. This allows animations 
  3.  * to have non-linear motion, such as acceleration and deceleration. 
  4.  */  
  5. public interface TimeInterpolator {  
  6.     /** 
  7.      * Maps a value representing the elapsed fraction of an animation to a value that represents 
  8.      * the interpolated fraction. This interpolated value is then multiplied by the change in 
  9.      * value of an animation to derive the animated value at the current elapsed animation time. 
  10.      * 
  11.      * @param input A value between 0 and 1.0 indicating our current point 
  12.      *        in the animation where 0 represents the start and 1.0 represents 
  13.      *        the end 
  14.      * @return The interpolation value. This value can be more than 1.0 for 
  15.      *         interpolators which overshoot their targets, or less than 0 for 
  16.      *         interpolators that undershoot their targets. 
  17.      */  
  18.     float getInterpolation(float input);  
  19. }

OK,接口还是非常简单的,只有一个getInterpolation()方法。大家有兴趣可以通过注释来对这个接口进行详解的了解,这里我就简单解释一下,getInterpolation()方法中接收一个input参数,这个参数的值会随着动画的运行而不断变化,不过它的变化是非常有规律的,就是根据设定的动画时长匀速增加,变化范围是0到1。也就是说当动画一开始的时候input的值是0,到动画结束的时候input的值是1,而中间的值则是随着动画运行的时长在0到1之间变化的。

 

说到这个input的值,我觉得有不少朋友可能会联想到我们在“中”篇文章中使用过的fraction值。那么这里的input和fraction有什么关系或者区别呢?答案很简单,input的值决定了fraction的值。input的值是由系统经过计算后传入到getInterpolation()方法中的,然后我们可以自己实现getInterpolation()方法中的算法,根据input的值来计算出一个返回值,而这个返回值就是fraction了。

 

因此,*简单的情况就是input值和fraction值是相同的,这种情况由于input值是匀速增加的,因而fraction的值也是匀速增加的,所以动画的运动情况也是匀速的。系统中内置的LinearInterpolator就是一种匀速运动的Interpolator,那么我们来看一下它的源码是怎么实现的:

  1. /** 
  2.  * An interpolator where the rate of change is constant 
  3.  */  
  4. @HasNativeInterpolator  
  5. public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {  
  6.     public LinearInterpolator() {  
  7.     }
  8.     public LinearInterpolator(Context context, AttributeSet attrs) {  
  9.     }
  10.     public float getInterpolation(float input) {  
  11.         return input;  
  12.     }
  13.     /** @hide */  
  14.     @Override  
  15.     public long createNativeInterpolator() {  
  16.         return NativeInterpolatorFactoryHelper.createLinearInterpolator();  
  17.     }
  18. }

这里我们只看getInterpolation()方法,这个方法没有任何逻辑,就是把参数中传递的input值直接返回了,因此fraction的值就是等于input的值的,这就是匀速运动的Interpolator的实现方式。

 

当然这是*简单的一种Interpolator的实现了,我们再来看一个稍微复杂一点的。既然现在大家都知道了系统在默认情况下使用的是AccelerateDecelerateInterpolator,那我们就来看一下它的源码吧,如下所示:

  1. /** 
  2.  * An interpolator where the rate of change starts and ends slowly but 
  3.  * accelerates through the middle. 
  4.  *  
  5.  */  
  6. @HasNativeInterpolator  
  7. public class AccelerateDecelerateInterpolator implements Interpolator, NativeInterpolatorFactory {  
  8.     public AccelerateDecelerateInterpolator() {  
  9.     }
  10.     @SuppressWarnings({“UnusedDeclaration”})  
  11.     public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {  
  12.     }
  13.     public float getInterpolation(float input) {  
  14.         return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;  
  15.     }
  16.     /** @hide */  
  17.     @Override  
  18.     public long createNativeInterpolator() {  
  19.         return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();  
  20.     }
  21. }

代码虽然没有变长很多,但是getInterpolation()方法中的逻辑已经明显变复杂了,不再是简单地将参数中的input进行返回,而是进行了一个较为复杂的数学运算。那这里我们来分析一下它的算法实现,可以看到,算法中主要使用了余弦函数,由于input的取值范围是0到1,那么cos函数中的取值范围就是π到2π。而cos(π)的结果是-1,cos(2π)的结果是1,那么这个值再除以2加上0.5之后,getInterpolation()方法*终返回的结果值还是在0到1之间。只不过经过了余弦运算之后,*终的结果不再是匀速增加的了,而是经历了一个先加速后减速的过程。我们可以将这个算法的执行情况通过曲线图的方式绘制出来,结果如下图所示:

 

%title插图%num

 

可以看到,这是一个S型的曲线图,当横坐标从0变化到0.2的时候,纵坐标的变化幅度很小,但是之后就开始明显加速,*后横坐标从0.8变化到1的时候,纵坐标的变化幅度又变得很小。

 

OK,通过分析LinearInterpolator和AccelerateDecelerateInterpolator的源码,我们已经对Interpolator的内部实现机制有了比较清楚的认识了,那么接下来我们就开始尝试编写一个自定义的Interpolator。

 

编写自定义Interpolator*主要的难度都是在于数学计算方面的,由于我数学并不是很好,因此这里也就写一个简单点的Interpolator来给大家演示一下。既然属性动画默认的Interpolator是先加速后减速的一种方式,这里我们就对它进行一个简单的修改,让它变成先减速后加速的方式。新建DecelerateAccelerateInterpolator类,让它实现TimeInterpolator接口,代码如下所示:

  1. public class DecelerateAccelerateInterpolator implements TimeInterpolator{  
  2.     @Override  
  3.     public float getInterpolation(float input) {  
  4.         float result;  
  5.         if (input <= 0.5) {  
  6.             result = (float) (Math.sin(Math.PI * input)) / 2;  
  7.         } else {  
  8.             result = (float) (2 – Math.sin(Math.PI * input)) / 2;  
  9.         }
  10.         return result;  
  11.     }
  12. }

这段代码是使用正弦函数来实现先减速后加速的功能的,因为正弦函数初始弧度的变化值非常大,刚好和余弦函数是相反的,而随着弧度的增加,正弦函数的变化值也会逐渐变小,这样也就实现了减速的效果。当弧度大于π/2之后,整个过程相反了过来,现在正弦函数的弧度变化值非常小,渐渐随着弧度继续增加,变化值越来越大,弧度到π时结束,这样从0过度到π,也就实现了先减速后加速的效果。

 

同样我们可以将这个算法的执行情况通过曲线图的方式绘制出来,结果如下图所示:

 

%title插图%num

 

可以看到,这也是一个S型的曲线图,只不过曲线的方向和刚才是相反的。从上图中我们可以很清楚地看出来,一开始纵坐标的变化幅度很大,然后逐渐变小,横坐标到0.5的时候纵坐标变化幅度趋近于零,之后随着横坐标继续增加纵坐标的变化幅度又开始变大,的确是先减速后加速的效果。

 

那么现在我们将DecelerateAccelerateInterpolator在代码中进行替换,如下所示:

  1. private void startAnimation() {  
  2.     Point startPoint = new Point(getWidth() / 2, RADIUS);  
  3.     Point endPoint = new Point(getWidth() / 2, getHeight() – RADIUS);  
  4.     ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);  
  5.     anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
  6.         @Override  
  7.         public void onAnimationUpdate(ValueAnimator animation) {  
  8.             currentPoint = (Point) animation.getAnimatedValue();
  9.             invalidate();
  10.         }
  11.     });
  12.     anim.setInterpolator(new DecelerateAccelerateInterpolator());  
  13.     anim.setDuration(3000);  
  14.     anim.start();
  15. }

非常简单,就是将DecelerateAccelerateInterpolator的实例传入到setInterpolator()方法当中。重新运行一下代码,效果如下图所示:

 

%title插图%num

 

OK!小球的运动确实是先减速后加速的效果,说明我们自定义的Interpolator已经可以正常工作了。通过这样一个程度的学习,相信大家对属性动画Interpolator的理解和使用都达到了一个比较深刻的层次了。

 

ViewPropertyAnimator的用法

ViewPropertyAnimator其实算不上什么高级技巧,它的用法格外的简单,只不过和前面所学的所有属性动画的知识不同,它并不是在3.0系统当中引入的,而是在3.1系统当中附增的一个新的功能,因此这里我们把它作为整个属性动画系列的收尾部分。

 

我们都知道,属性动画的机制已经不是再针对于View而进行设计的了,而是一种不断地对值进行操作的机制,它可以将值赋值到指定对象的指定属性上。但是,在*大多数情况下,我相信大家主要都还是对View进行动画操作的。Android开发团队也是意识到了这一点,没有为View的动画操作提供一种更加便捷的用法确实是有点太不人性化了,于是在Android 3.1系统当中补充了ViewPropertyAnimator这个机制。

 

那我们先来回顾一下之前的用法吧,比如我们想要让一个TextView从常规状态变成透明状态,就可以这样写:

  1. ObjectAnimator animator = ObjectAnimator.ofFloat(textview, “alpha”, 0f);  
  2. animator.start();

看上去复杂吗?好像也不怎么复杂,但确实也不怎么容易理解。我们要将操作的view、属性、变化的值都一起传入到ObjectAnimator.ofFloat()方法当中,虽然看上去也没写几行代码,但这不太像是我们平时使用的面向对象的思维。

 

那么下面我们就来看一下如何使用ViewPropertyAnimator来实现同样的效果,ViewPropertyAnimator提供了更加易懂、更加面向对象的API,如下所示:

  1. textview.animate().alpha(0f);

果然非常简单!不过textview.animate()这个方法是怎么回事呢?animate()方法就是在Android 3.1系统上新增的一个方法,这个方法的返回值是一个ViewPropertyAnimator对象,也就是说拿到这个对象之后我们就可以调用它的各种方法来实现动画效果了,这里我们调用了alpha()方法并转入0,表示将当前的textview变成透明状态。

 

怎么样?比起使用ObjectAnimator,ViewPropertyAnimator的用法明显更加简单易懂吧。除此之外,ViewPropertyAnimator还可以很轻松地将多个动画组合到一起,比如我们想要让textview运动到500,500这个坐标点上,就可以这样写:

  1. textview.animate().x(500).y(500);  

可以看出,ViewPropertyAnimator是支持连缀用法的,我们想让textview移动到横坐标500这个位置上时调用了x(500)这个方法,然后让textview移动到纵坐标500这个位置上时调用了y(500)这个方法,将所有想要组合的动画通过这种连缀的方式拼接起来,这样全部动画就都会一起被执行。

 

那么怎样去设定动画的运行时长呢?很简单,也是通过连缀的方式设定即可,比如我们想要让动画运行5秒钟,就可以这样写:

  1. textview.animate().x(500).y(500).setDuration(5000);  

除此之外,本篇文章*部分所学的Interpolator技术我们也可以应用在ViewPropertyAnimator上面,如下所示:

  1. textview.animate().x(500).y(500).setDuration(5000)  
  2.         .setInterpolator(new BounceInterpolator());  

用法很简单,同样也是使用连缀的方式。相信大家现在都已经体验出来了,ViewPropertyAnimator其实并没有什么太多的技巧可言,用法基本都是大同小异的,需要用到什么功能就连缀一下,因此更多的用法大家只需要去查阅一下文档,看看还支持哪些功能,有哪些接口可以调用就可以了。

 

那么除了用法之外,关于ViewPropertyAnimator有几个细节还是值得大家注意一下的:

 

  • 整个ViewPropertyAnimator的功能都是建立在View类新增的animate()方法之上的,这个方法会创建并返回一个ViewPropertyAnimator的实例,之后的调用的所有方法,设置的所有属性都是通过这个实例完成的。
  • 大家注意到,在使用ViewPropertyAnimator时,我们自始至终没有调用过start()方法,这是因为新的接口中使用了隐式启动动画的功能,只要我们将动画定义完成之后,动画就会自动启动。并且这个机制对于组合动画也同样有效,只要我们不断地连缀新的方法,那么动画就不会立刻执行,等到所有在ViewPropertyAnimator上设置的方法都执行完毕后,动画就会自动启动。当然如果不想使用这一默认机制的话,我们也可以显式地调用start()方法来启动动画。
  • ViewPropertyAnimator的所有接口都是使用连缀的语法来设计的,每个方法的返回值都是它自身的实例,因此调用完一个方法之后可以直接连缀调用它的另一个方法,这样把所有的功能都串接起来,我们甚至可以仅通过一行代码就完成任意复杂度的动画功能。

 

好的,那么到这里为止,整个Android属性动画完全解析的系列就全部结束了,感谢大家有耐心看到*后。

ViewStub用法

ViewStub用法

在开发应用程序的时候,经常会遇到这样的情况,会在运行时动态根据条件来决定显示哪个View或某个布局。那么*通常的想法就是把可能用到的View都写在上面,先把它们的可见性都设为View.GONE,然后在代码中动态的更改它的可见性。这样的做法的优点是逻辑简单而且控制起来比较灵活。但是它的缺点就是,耗费资源。虽然把View的初始可见View.GONE但是在Inflate布局的时候View仍然会被Inflate,也就是说仍然会创建对象,会被实例化,会被设置属性。也就是说,会耗费内存等资源。

推荐的做法是使用android.view.ViewStub,ViewStub 是一个轻量级的View,它一个看不见的,不占布局位置,占用资源非常小的控件。可以为ViewStub指定一个布局,在Inflate布局的时候,只有 ViewStub会被初始化,然后当ViewStub被设置为可见的时候,或是调用了ViewStub.inflate()的时候,ViewStub所向 的布局就会被Inflate和实例化,然后ViewStub的布局属性都会传给它所指向的布局。这样,就可以使用ViewStub来方便的在运行时,要还 是不要显示某个布局。

但ViewStub也不是万能的,下面总结下ViewStub能做的事儿和什么时候该用ViewStub,什么时候该用可见性的控制。

首先来说说ViewStub的一些特点:

1. ViewStub只能Inflate一次,之后ViewStub对象会被置为空。按句话说,某个被ViewStub指定的布局被Inflate后,就不会够再通过ViewStub来控制它了。

2. ViewStub只能用来Inflate一个布局文件,而不是某个具体的View,当然也可以把View写在某个布局文件中。

基于以上的特点,那么可以考虑使用ViewStub的情况有:

1. 在程序的运行期间,某个布局在Inflate后,就不会有变化,除非重新启动。

因为ViewStub只能Inflate一次,之后会被置空,所以无法指望后面接着使用ViewStub来控制布局。所以当需要在运行时不止一次的显示和 隐藏某个布局,那么ViewStub是做不到的。这时就只能使用View的可见性来控制了。

2. 想要控制显示与隐藏的是一个布局文件,而非某个View。

因为设置给ViewStub的只能是某个布局文件的Id,所以无法让它来控制某个View。

所以,如果想要控制某个View(如Button或TextView)的显示与隐藏,或者想要在运行时不断的显示与隐藏某个布局或View,只能使用View的可见性来控制。

下面来看一个实例

在这个例子中,要显示二种不同的布局,一个是用TextView显示一段文字,另一个则是用ImageView显示一个图片。这二个是在onCreate()时决定是显示哪一个,这里就是应用ViewStub的*佳地点。

先来看看布局,一个是主布局,里面只定义二个ViewStub,一个用来控制TextView一个用来控制ImageView,另外就是一个是为显示文字的做的TextView布局,一个是为ImageView而做的布局:

 

复制代码
    <?xml version="1.0" encoding="utf-8"?>  
    <LinearLayout  
      xmlns:android="http://schemas.android.com/apk/res/android"  
      android:orientation="vertical"  
      android:layout_width="fill_parent"  
      android:layout_height="fill_parent"  
      android:gravity="center_horizontal">  
      <ViewStub   
        android:id="@+id/viewstub_demo_text"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_marginLeft="5dip"  
        android:layout_marginRight="5dip"  
        android:layout_marginTop="10dip"  
        android:layout="@layout/viewstub_demo_text_layout"/>  
      <ViewStub   
        android:id="@+id/viewstub_demo_image"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_marginLeft="5dip"  
        android:layout_marginRight="5dip"  
        android:layout="@layout/viewstub_demo_image_layout"/>  
    </LinearLayout>
复制代码

 

为TextView的布局:

 

复制代码
    <?xml version="1.0" encoding="utf-8"?>  
    <LinearLayout  
      xmlns:android="http://schemas.android.com/apk/res/android"  
      android:orientation="vertical"  
      android:layout_width="wrap_content"  
      android:layout_height="wrap_content">  
        <TextView  
            android:id="@+id/viewstub_demo_textview"  
            android:layout_width="fill_parent"  
            android:layout_height="wrap_content"  
            android:background="#aa664411"  
            android:textSize="16sp"/>  
    </LinearLayout>
复制代码

 

为ImageView的布局:

 

复制代码
    <?xml version="1.0" encoding="utf-8"?>  
    <LinearLayout  
      xmlns:android="http://schemas.android.com/apk/res/android"  
      android:orientation="vertical"  
      android:layout_width="wrap_content"  
      android:layout_height="wrap_content">  
        <ImageView  
            android:id="@+id/viewstub_demo_imageview"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"/>  
    </LinearLayout>
复制代码

 

下面来看代码,决定来显示哪一个,只需要找到相应的ViewStub然后调用其infalte()就可以获得相应想要的布局:

 

复制代码
    package com.effective;  
      
    import android.app.Activity;  
    import android.os.Bundle;  
    import android.view.ViewStub;  
    import android.widget.ImageView;  
    import android.widget.TextView;  
      
    public class ViewStubDemoActivity extends Activity {  
        @Override  
        public void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.viewstub_demo_activity);  
            if ((((int) (Math.random() * 100)) & 0x01) == 0) {  
                // to show text  
                // all you have to do is inflate the ViewStub for textview  
                ViewStub stub = (ViewStub) findViewById(R.id.viewstub_demo_text);  
                stub.inflate();  
                TextView text = (TextView) findViewById(R.id.viewstub_demo_textview);  
                text.setText("The tree of liberty must be refreshed from time to time" +  
                        " with the blood of patroits and tyrants! Freedom is nothing but " +  
                        "a chance to be better!");  
            } else {  
                // to show image  
                // all you have to do is inflate the ViewStub for imageview  
                ViewStub stub = (ViewStub) findViewById(R.id.viewstub_demo_image);  
                stub.inflate(); 
                ImageView image = (ImageView) findViewById(R.id.viewstub_demo_imageview);  
                image.setImageResource(R.drawable.happy_running_dog);  
            }  
        }  
    }
复制代码

 

运行结果:

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

使用的时候的注意事项:

1. 某些布局属性要加在ViewStub而不是实际的布局上面,才会起作用,比如上面用的android:layout_margin*系列属性,如果加在 TextView上面,则不会起作用,需要放在它的ViewStub上面才会起作用。而ViewStub的属性在inflate()后会都传给相应的布 局。

 

1.ViewStub之所以常称之为“延迟化加载”,是因为在教多数情况下,程序 无需显示ViewStub所指向的布局文件,只有在特定的某些较少条件下,此时ViewStub所指向的布局文件才需要被inflate,且此布局文件直 接将当前ViewStub替换掉,具体是通过viewStub.infalte()或 viewStub.setVisibility(View.VISIBLE)来完成;

2.正确把握住ViewStub的应用场景非常重要,正如如1中所描述需求场景下,使用ViewStub可以优化布局;

3.对ViewStub的inflate操作只能进行一次,因为inflate的 时候是将其指向的布局文件解析inflate并替换掉当前ViewStub本身(由此体现出了ViewStub“占位符”性质),一旦替换后,此时原来的 布局文件中就没有ViewStub控件了,因此,如果多次对ViewStub进行infalte,会出现错误信息:ViewStub must have a non-null ViewGroup viewParent。

4.3中所讲到的ViewStub指向的布局文件解析inflate并替换掉当前 ViewStub本身,并不是完全意义上的替换(与include标签还不太一样),替换时,布局文件的layout params是以ViewStub为准,其他布局属性是以布局文件自身为准。

5.ViewStub本身是不可见的,对 ViewStub setVisibility(..)与其他控件不一样,ViewStub的setVisibility 成View.VISIBLE或INVISIBLE如果是首次使用,都会自动inflate其指向的布局文件,并替换ViewStub本身,再次使用则是相 当于对其指向的布局文件设置可见性。

 

 

viewStub中加载layout的代码是

  1. public View inflate() {
  2.     final ViewParent viewParent = getParent();// 获取当前view的父view,用于获取需要加载的layout的index  
  3.     if (viewParent != null && viewParent instanceof ViewGroup) {
  4.         if (mLayoutResource != 0) {
  5.             final ViewGroup parent = (ViewGroup) viewParent;  
  6.             final LayoutInflater factory;
  7.             if (mInflater != null) {
  8.                 factory = mInflater;  
  9.             } else {
  10.                 factory = LayoutInflater.from(mContext);  
  11.             }
  12.             final View view = factory.<strong><span style=”font-size:14px;color:#ff0000;”>inflate</span></strong>(mLayoutResource, parent, false);// 获取需要加载的layout  
  13.             if (mInflatedId != NO_ID) {
  14.                 view.setId(mInflatedId);
  15.             }
  16.             final int index = parent.indexOfChild(this);  
  17.             parent.removeViewInLayout(this);// 删除之前加载的view
  18.             final ViewGroup.LayoutParams layoutParams = getLayoutParams();  
  19.             if (layoutParams != null) {
  20.                 parent.addView(view, index, layoutParams);
  21.             } else {
  22.                 parent.<strong><span style=“font-size:14px;color:#ff0000;”>addView</span></strong>(view, index);// 添加view  
  23.             }
  24.             mInflatedViewRef = new WeakReference<View>(view);  
  25.             if (mInflateListener != null) {
  26.                 mInflateListener.onInflate(this, view);
  27.             }
  28.             return view;
  29.         } else {
  30.             throw new IllegalArgumentException(“ViewStub must have a valid layoutResource”);
  31.         }
  32.     } else {
  33.         throw new IllegalStateException(“ViewStub must have a non-null ViewGroup viewParent”);
  34.     }
  35. }

 

Volley(一 )—— 框架简介

Volley(一 )—— 框架简介

一、引言

虽然网上已经有很多大神、高手都写过了类似的帖子,但作为新人,必须要走模仿的道路,再考虑超越,因此学习大神的笔记,记录自己的理解,是一个菜鸟走向成功的必经之路啊。如签名所言,记录自己摸爬滚打的经历,享受不悔的青春。废话不多说,言归正传。

二、Volley是什么?

Volley是 Google 推出的 Android 异步网络请求框架和图片加载框架。

三、Volley的主要特性

(1). 提供对网络请求自动调控的管理功能RequestQueue请求队列
(2).提供对网络请求进行缓存的功能,数据可以二级缓存,图片可以三级缓存,缓存有效时间默认使用请求响应头内CacheControl设置的缓存有效时间,也就是缓存有效时间由服务端动态确定
(3). 提供强大的请求取消功能
(4). 提供简便的图片加载工具ImageLoader&NetworkImageView

(5).提供强大的自定制功能,可以自己按照自己的实际需求定制request<T>子类

四、volley的总体设计图

%title插图%num

上图源自网络。如上图所示,volley主要是通过两种Diapatch Thread不断从RequestQueue中取出请求,根据是否已缓存调用Cache或Network这两类数据获取接口之一,从内存缓存或是服务器取 得请求的数据,然后交由ResponseDelivery去做结果分发及回调处理。

五、volley中的一些概念

  • Volley:Volley 对外暴露的 API,通过 newRequestQueue(…) 函数新建并启动一个请求队列RequestQueue。
  • Request:表示一个请求的抽象类。StringRequest、JsonRequest、ImageRequest都是它的子类,表示某种类型的请求。
  • RequestQueue:表示请求队列,里面包含一个CacheDispatcher(用于处理走缓存请求的调度线程)、 NetworkDispatcher数组(用于处理走网络请求的调度线程),一个ResponseDelivery(返回结果分发接口),通过 start() 函数启动时会启动CacheDispatcher和NetworkDispatchers。
  • CacheDispatcher:一个线程,用于调度处理走缓存的请求。启动后会不断从缓存请求队列中取请求处理,队列为空则等待,请求 处理结束则将结果传递给ResponseDelivery去执行后续处理。当结果未缓存过、缓存失效或缓存需要刷新的情况下,该请求都需要重新进入 NetworkDispatcher去调度处理。
  • NetworkDispatcher:一个线程,用于调度处理走网络的请求。启动后会不断从网络请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理,并判断结果是否要进行缓存。
  • ResponseDelivery:返回结果分发接口,目前只有基于ExecutorDelivery的在入参 handler 对应线程内进行分发。
  • HttpStack:处理 Http 请求,返回请求结果。目前 Volley 中有基于 HttpURLConnection 的HurlStack和 基于 Apache HttpClient 的HttpClientStack。
  • Network:调用HttpStack处理请求,并将结果转换为可被ResponseDelivery处理的NetworkResponse。
  • Cache:缓存请求结果,Volley 默认使用的是基于 sdcard 的DiskBasedCache。NetworkDispatcher得到请求结果后判断是否需要存储在 Cache,CacheDispatcher会从 Cache 中取缓存结果。

六、volley请求处理流程图

%title插图%num

上图源自网络。