日期: 2021 年 4 月 2 日

Android流量抓包工具–PacketCapture

Android流量抓包

我们在WIFI环境通常相对容易定位问题,可以通过fiddler、charles等工具轻松抓包进而通过接口判断是否接口异常,但是在流量环境下无法使用上述抓包工具进行抓包,这时就需要Android 流量抓包工具帮忙抓包,进而定位问题。

PacketCapture介绍

PacketCapture是一个强大的调试应用,可以捕获Android手机上的任何网络流量,它利用Android系统的* service完成网络请求的捕获。无需root,并可以通过中间人技术抓取https请求。
可以从google play商店搜索下载PacketCapture

安装完成后,打开PacketCapture,进行应用的设置,开始的几个页面点击确定就行,第三个页面需要设置SSL证书,如果需要抓取https协议的话,这一页的设置就点击“Install Certificate”,证书保存一下即可,比较简单。

PacketCapture使用

需要流量抓包时,我们打开PacketCapture,可以看到它的界面比较简单,主要的功能就是选择应用进行抓包,以及抓取手机全部的流量包。下方列表里存放的是每次抓取到的数据。如下图所示。

Screenshot_2018-03-15-20-27-54-76.png

选择应用抓包可以只过滤出指定应用的请求,比较方便。
开始抓包时点击起始按钮,然后操作应用,结束操作后点击PackageCapture页面的停止抓包按钮。此次抓包记录就保存成一条记录,生成以开始抓包的时间命名的记录。
我们选择列表中的一条记录查看其中的内容。如下图所示,可见里面有抓包App的名称、host及端口号,协议类型、请求时间、是否是https、数据包的大小等信息。

再点击内容中的任意一条请求,可以查看这份请求的具体情况,如下图所示。请求里列出了请求体、请求url、请求方式、返回数据、返回值、返回值内容等。可以看到这条请求的全部信息,与fiddler、charles等wifi下抓包结果一致。

另外还可以点击右上角选择将该条请求的request或者response保存到文件中。

结语

PacketCapture轻巧、使用简单,可完成Android流量抓包,在流量情况下通过抓包分析请求情况,或者分析流量情况下的图片压缩情况等,是个不错的流量调试工具。

MBP 安装 mac 系统监测工具 iStats 遇到的问题,并逐一解决

今天搞视频,MBP 呼呼响,突然想装个软件查看一下 CPU 温度,风扇转速什么的。 *简单是用 Ruby 的 iStats,直接指令安装,简单又小巧。

常规操作:

sudo gem install iStats

谁知道我电脑升级到 10.14 以后就很久没搞过 gem 了,估计生锈,报错:

ERROR: Failed to build gem native extension.

再往下看

You might have to install separate package for the ruby development
environment, ruby-dev or ruby-devel for example.

噢,ruby 都没装啊?可能被我清理掉了。。。 直接装 xcode-select

xcode-select --install

Easy 安装成功,毕竟是 xcode,果子自家软件。

再来安装一次 iStats

sudo gem install iStats

又报错,You don’t have write permissions for the /usr/bin directory. 好像是 10.14 提高了安全等级,不允许安装到 /usr/bin 那就指定到其他地方:

sudo gem install iStats -n /usr/local/bin 

安装成功!~ 执行指令 iStats 运行:

cZUX28.png

2 条回复    2021-04-02 12:37:03 +08:00
suntorrent
    1

suntorrent   12 小时 17 分钟前

10.15 Catalina: make failed
make: *** No rule to make target `/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/universal-darwin19/ruby/config.h’, needed by `smc.o’. Stop.
easonl
    2

easonl   5 小时 12 分钟前

@suntorrent 我是 Big Sur,没遇到你这种报错~ 你试试升级以下你的 ruby 和 gem

吐槽 AirPods?

AirPods 2,佩戴舒服无感,左右交替带一天。
开始吐?
开启入耳检测:经常出现被认为拿出耳机,暂停播放,只能手动重新放耳朵里,而且还得来回扭动待耳机检测成功!
关闭入耳检测:不会再有断播,不过耳机双击切换大概率失效!放充电盒后手机外放会出现已连接耳机的状况(音频直接被耳机接管)

什么,用 Hey Siri 切歌?这家伙懒得要死,基本不理我!

31 条回复    2021-04-02 16:43:58 +08:00
silencelixing
    1

silencelixing   8 小时 7 分钟前

我*受不了的是,耳机有电放进充电盒,拿出来没电了
Shook
    2

Shook   7 小时 52 分钟前

我只用来听歌。
zoeleeeeee
    3

zoeleeeeee   7 小时 46 分钟前

完全没遇到过,使用一切正常,也没有误认为是拿出耳机之类的。建议你找苹果客服看看是不是你那个耳机有问题。
ChangQin
    4

ChangQin   7 小时 45 分钟前

@silencelixing 没错!这种情况一般是耳机有电,盒子没电了
Nishino
    5

Nishino   7 小时 45 分钟前

没遇到过,你这估计坏了吧
zoeleeeeee
    6

zoeleeeeee   7 小时 44 分钟前

我也经常用 airpods 唤出 siri,使用两三年了,从没出过问题,pro 也没问题。siri 必须联网才会响应,如果你联网了还是不行,检查一下 siri 设置,不行的话还是建议联系客服
wipbssldo
    7

wipbssldo   7 小时 42 分钟前

「左右交替带一天。」是什么操作
haorrs
    8

haorrs   7 小时 40 分钟前

坚持只带一只耳朵?
dier
    9

dier   7 小时 18 分钟前

我也觉得你应该咨询一下苹果客服,看是不是耳机有问题
dier
    10

dier   7 小时 17 分钟前

@silencelixing
@ChangQin
耳机还有反向给盒子充电的骚操作??

w99wjacky
    11

w99wjacky   7 小时 16 分钟前

入耳检测的问题没遇到过,你这估计坏了
Hey Siri 肯定有用的,先检查下是否开了 Siri
w99wjacky
    12

w99wjacky   7 小时 16 分钟前

推荐先保修
ericwoflskin
    13

ericwoflskin   7 小时 6 分钟前

个体问题,下一个!
NilChan
    14

NilChan   7 小时 3 分钟前 via Android

入耳检测这么坑的吗?
StyxS
    15

StyxS   7 小时 2 分钟前

对于我来说*坑的是连着电脑,手机拿出来看下就被抢走了
电脑蓝牙还连着,但是没声音
cszchen
    16

cszchen   6 小时 50 分钟前

建议看一下佩戴的姿势,耳机屁股朝下,或者朝前角度不要太大,对着下巴就行了
DEVN
    17

DEVN   6 小时 48 分钟前

3 代 完美躲避这些问题 ?
bg7dcw
    18

bg7dcw   6 小时 40 分钟前

hi siri: hey, say (去声) rui(三声)
wezzard
    19

wezzard   6 小时 36 分钟前

重置一下试试?
Leonard
    20

Leonard   6 小时 25 分钟前

@dier #10 不是反向充电,只是盒子没给耳机充电,耳机自己耗电耗光了
ftu
    21

ftu   6 小时 10 分钟前

的确,有利有弊,躺着很容易暂停,遂关闭入耳检测

AirPods 充电盒没电了也不会提醒,这个有待改良,还有 Siri 带着口罩路上走,基本正常音量叫不 Siri 出来

freakJacker
    22

freakJacker   5 小时 31 分钟前

保修的,拿去检测一下。坏了会给你换一个新的
iloveayu
    23

iloveayu   5 小时 17 分钟前

双击的位置比较重要:
%title插图%num
FartNoSound
    24

FartNoSound   5 小时 15 分钟前

对我来说*恶心的还是 iPhone 和 Mac 不能同时开蓝牙,同时连接,一会连这个,一会连那个的。
xujia1998
    25

xujia1998   3 小时 1 分钟前

airpods pro 能打电话吗?我都怀疑我买到残品了,每次打电话对面说我声音小
zc847666533
    26

zc847666533   2 小时 45 分钟前

@xujia1998 确实声音很小,我每次用这个打电话也是,好几次对面都问我离电话很远么
misaka19000
    27

misaka19000   2 小时 36 分钟前

没遇到过。。。我还是并夕夕买的。。。
nobodyknows
    28

nobodyknows   2 小时 19 分钟前

@silencelixing 试过几次,好像放到某些不兼容的无线充电器会放电 – -!
wipbssldo
    29

wipbssldo   1 小时 36 分钟前

@silencelixing 充电盒没电,没办法告诉耳机进入睡眠,耳机一直处于工作状态,耳机很快就没电
AndyZhuAZ
    30

AndyZhuAZ   1 小时 34 分钟前

*近我的 pro 的 Siri 也不好使了,叫起来很难。。。不知道是不是堵了
mantout
    31

mantout   1 小时 7 分钟前

@ChangQin #4 这种情况我遇到过一两次,我猜测的原因是充电仓底部有灰尘之类的东西,导致耳机接触不良,一直处于工作状态,把电放光了。

问一下大家的 iPad Pro 2020 12.9 照片滑动卡么?

具体表现是:

1 、照片缩略图页面上下滑动经常特别卡,严重的时候感觉带有抖动

2 、有的照片点去要一秒左右才从模糊变清楚

3 、每张照片左滑右滑上一张下一张也经常卡

7 条回复    2021-04-02 17:09:04 +08:00
BlackCode
    1

BlackCode   3 小时 43 分钟前 via iPhone

“一秒左右才从模糊变清楚”

是不是开了照片的“优化 iPhone 存储空间”

zach
    2

zach   3 小时 33 分钟前

@BlackCode iCloud 照片是关闭的,现在怀疑是 iOS14.4.2 的原因,刚才试了下 app store 上下滑动也是卡卡的,整个系统都不流畅,照片尤其严重
tedeastside
    3

tedeastside   1 小时 53 分钟前 via iPhone

开了 60hz 吗
paopaosa
    4

paopaosa   1 小时 44 分钟前

重启过吗?
terrychanin
    5

terrychanin   1 小时 39 分钟前

实测没有这种情况,建议重启试试
littlewing
    6

littlewing   1 小时 29 分钟前 via iPhone

先说一下你有几百 G 的照片吧
Ciicing
    7

Ciicing   41 分钟前

@zach 商店我的 2020 11 款也会偶尔卡一下

Mac 上有哪些好用的 PDF 阅读软件啊?

非常讨厌 WPS,除了 WPS 外其他都可以推荐…
20 条回复    2021-04-02 17:43:58 +08:00
CenN
    1

CenN   2 小时 37 分钟前

我觉得预览挺好用的
TimePPT
    2

TimePPT   2 小时 36 分钟前

PDF Expert
66beta
    3

66beta   2 小时 35 分钟前

预览
chrome
EasonC
    4

EasonC   2 小时 34 分钟前 via iPhone

marginnote3
bear2000
    5

bear2000   2 小时 28 分钟前

自带的预览*好用
IgniteWhite
    6

IgniteWhite   1 小时 53 分钟前 via iPhone

楼上推荐的都不错。补充:Skim,这款方便配合 Vim 写 LaTeX 用,能实时更新,反向搜索
Zien
    7

Zien   1 小时 32 分钟前 via iPhone

系统自带的,或者 chrome 的 kami 插件(批注)和划词翻译带的插件( pdf 划词翻译)
littlewing
    8

littlewing   1 小时 24 分钟前 via iPhone

预览.app
sungnix
    9

sungnix   59 分钟前

PDF Guru 挺不错,不编辑的话用免费版足够了。
w2ex2019
    10

w2ex2019   57 分钟前

PDF Expert +1

AllenHua
    11

AllenHua   55 分钟前

Preview.app
ftu
    12

ftu   53 分钟前

一般用 PDF Expert
shoujiaxin
    13

shoujiaxin   44 分钟前 via iPhone

Preview,PDF Expert 太吃内存了
sunkpfly
    14

sunkpfly   38 分钟前

PDF Expert +2
clyecao
    15

clyecao   27 分钟前

chrome(+ kami) + 1
ExplorerLog
    16

ExplorerLog   17 分钟前

firefox
Johnoo
    17

Johnoo   15 分钟前

PDF Expert +10086 ,关键是还能修改矢量 PDF
gxy2825
    18

gxy2825   14 分钟前

PDF Reader Pro
Roykira
    19

Roykira   13 分钟前

*近发现 marginnote3 还不错。
with
    20

with   1 分钟前

PDF Guru +1

iOS开发-NSThread子线程autoreleasepool的问题

前言
对于 NSThread 开启的子线程,我们需要在 main 函数中创建一个autoreleasepool,当我们从其他线程跳转到该线程执行时,对象是如何释放的呢?主线程是由于runloop的循环,在beforeWait时,触发主线程的autoreleasepool的pop和push操作来释放的,而子线程并没有自动添加这些observer,那么如何释放的?

探索
跳转到我们线程执行任务的方法如下,使用了performSelector:系列的方法。

1 OBJC_EXTERN void _objc_autoreleasePoolPrint(void);
2 @implementation GrowingDispatchManager
3
4 + (void)dispatchInGrowingThread:(void (^_Nullable)(void))block {
5     if ([[NSThread currentThread] isEqual:[GrowingThread sharedThread]]) {
6         block();
7     } else {
8         [GrowingDispatchManager performSelector:@selector(dispatchBlock:)
9                                        onThread:[GrowingThread sharedThread]
10                                      withObject:block
11                                   waitUntilDone:NO];
12     }
13 }
14
15 + (void)dispatchBlock:(void (^_Nullable)(void))block {
16
17     NSLog(@”runloop %@”,[NSRunLoop currentRunLoop]);
18 //    _objc_autoreleasePoolPrint();
19     if (block) {
20         block();
21     }
22 //    _objc_autoreleasePoolPrint();
23 }

打印当前runloop,确定是没有像主线程那样添加observer,如下:

1 2021-02-03 23:14:42.112259+0800 Example[5075:57255] runloop <CFRunLoop 0x600000dbdb00 [0x7fff80617cb0]>{wakeup port = 0x9203, stopped = false, ignoreWakeUps = false,
2 current mode = kCFRunLoopDefaultMode,
3 common modes = <CFBasicHash 0x600003fe25b0 [0x7fff80617cb0]>{type = mutable set, count = 1,
4 entries =>
5
6 }
7 ,
8 common mode items = <CFBasicHash 0x600003fe2bb0 [0x7fff80617cb0]>{type = mutable set, count = 1,
9 entries =>
10
11 }
12 ,
13 modes = <CFBasicHash 0x600003fe27f0 [0x7fff80617cb0]>{type = mutable set, count = 1,
14 entries =>
15
16
17 FBasicHash 0x600003fe25e0 [0x7fff80617cb0]>{type = mutable set, count = 2,
18 entries =>
19
20
21 }
22 ,
23
24 entries =>
25 }
26 ,
27
28
29
30 },
31
32 }
33 }

那么如何释放的呢,通过_objc_autoreleasePoolPrint()打印自动释放池堆栈,确定对象没有堆积,是进行释放过了。

常驻线程实现代码如下:

2 // GrowingThread.m
3 // GrowingAnalytics
4
5 #import “GrowingThread.h”
6
7 @interface GrowingThread () {
8     dispatch_group_t _waitGroup;
9 }
10
11 @property (nonatomic, strong, readwrite) NSRunLoop *runLoop;
12
13 @end
14
15 @implementation GrowingThread
16
17 + (instancetype)sharedThread {
18     static GrowingThread *thread;
19     static dispatch_once_t onceToken;
20     dispatch_once(&onceToken, ^{
21         thread = [[GrowingThread alloc] init];
22         thread.name = @”com.growing.thread”;
23         [thread start];
24     });
25     return thread;
26 }
27
28 – (instancetype)init {
29     self = [super init];
30     if (self) {
31         _waitGroup = dispatch_group_create();
32         dispatch_group_enter(_waitGroup);
33     }
34     return self;
35 }
36
37 – (void)main {
38     @autoreleasepool {
39         _runLoop = [NSRunLoop currentRunLoop];
40         dispatch_group_leave(_waitGroup);
41
42         // Add an empty run loop source to prevent runloop from spinning.
43         CFRunLoopSourceContext sourceCtx = {.version = 0,
44                                             .info = NULL,
45                                             .retain = NULL,
46                                             .release = NULL,
47                                             .copyDescription = NULL,
48                                             .equal = NULL,
49                                             .hash = NULL,
50                                             .schedule = NULL,
51                                             .cancel = NULL,
52                                             .perform = NULL};
53         CFRunLoopSourceRef source = CFRunLoopSourceCreate(NULL, 0, &sourceCtx);
54         CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
55         CFRelease(source);
56
57         while ([_runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) {
58         }
59         assert(NO);
60     }
61 }
62
63 – (NSRunLoop *)runLoop;
64 {
65     dispatch_group_wait(_waitGroup, DISPATCH_TIME_FOREVER);
66     return _runLoop;
67 }
68
69 @end

通过断点调试,发现堆栈中确实有调用对应的NSPushAutoreleasePool和NSPopAutoreleasePool

%title插图%num
打印执行时的堆栈:

1 (lldb) bt
2 * thread #9, name = ‘com.growing.thread’, stop reason = breakpoint 2.1
3     frame #0: 0x0000000108cf5c07 GrowingAnalytics`+[GrowingDispatchManager dispatchBlock:](self=GrowingDispatchManager, _cmd=”dispatchBlock:”, block=0x0000000108cfb330) at GrowingDispatchManager.m:46:1
4     frame #1: 0x00007fff25781c30 Foundation`__NSThreadPerformPerform + 259
5     frame #2: 0x00007fff23bd4471 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
6     frame #3: 0x00007fff23bd439c CoreFoundation`__CFRunLoopDoSource0 + 76
7     frame #4: 0x00007fff23bd3b74 CoreFoundation`__CFRunLoopDoSources0 + 180
8     frame #5: 0x00007fff23bce87f CoreFoundation`__CFRunLoopRun + 1263
9     frame #6: 0x00007fff23bce066 CoreFoundation`CFRunLoopRunSpecific + 438
10   * frame #7: 0x00007fff2576b86f Foundation`-[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 211
11     frame #8: 0x0000000108d41012 GrowingAnalytics`-[GrowingThread main](self=0x0000600001285900, _cmd=”main”) at GrowingThread.m:72:16
12     frame #9: 0x00007fff257817a7 Foundation`__NSThread__start__ + 1047
13     frame #10: 0x00007fff52466e65 libsystem_pthread.dylib`_pthread_start + 148
14     frame #11: 0x00007fff5246283b libsystem_pthread.dylib`thread_start + 15

至此,对象为何释放的原因找到了,如果不是runMode:beforeDate:方法自动加上了自动释放池,就内存泄漏了,所以需要在执行的方法dispatchBlock中保险起见,都加上@autoreleasepool才是上策,这里也是忽略了这点,险些bug背锅。。。

iOS开发-逆向注入SDK之iOS越狱

越狱
这里采用使用 iphone 5S, iOS 12.4.9 为例,进行 非完美越狱

非完美越狱:重启手机越狱失效,需要再进行越狱,越狱也不麻烦,点几下就行了,不过不关机就行了哈

i4助手
安装 i4助手(爱思助手) https://www.i4.cn/ 是个很好的软件,如果你的手机变砖了,正好可以使用它刷机。不过被密码锁定的手机不行。

如果你的iphone已经开机就黑,选择合适的固件版本,进行刷机

%title插图%num

这里对其功能不做进一步说明,低版本可以直接完美越狱,我们这里仅使用它来方便访问越狱文件以及安装app。

使用unc0ver越狱并配置
unc0ver 可以方便的快速越狱你的手机,不过是非完美越狱,由于安装的时候,手机还没有越狱,官网的使用方法中,使用 iOS App Signer 的方式可行。

%title插图%num

这里以iOS开发者的角度阐述为以下几步:

安装好你的xcode,配置好环境,登录你的开发者账号。
创建一个Project,选择类型为App,这里叫它 unc0ver。
配置好你的证书,如下

%title插图%num

从钥匙串中导出你对应的p12证书,并且拖出Profile文件。(这里Profile文件部分同学可能不知道拖,见下图)
a. 鼠标移至感叹号,点击

%title插图%num

b. 长按图标,拖置桌面

%title插图%num

从 unc0ver 官网下载 unc0ver_Release_x.x.x.ipa
打开 iOS App Signer,配置ipa,p12以及选择自定义的Profile

%title插图%num

生成新的ipa,通过i4助手进行安装。

%title插图%num

如果需要信任,则设置->通用->描述文件与设备管理中信任即可。
打开 unc0ver,点击 Jailbreak 进行越狱,期间弹出 REBEL 不要管它,叉掉,继续。提示你重启就重启,该允许的允许,然后继续打开 unc0ver 继续,直到 unc0ver 完成越狱。
这个方法需要 开发证书 配置文件 重签名。网上部分安装方法,直接安装的话,会提示 entitlements 不匹配,无法进行下一步了。

越狱好后,会多出Cydia和Substitude,同时 unc0ver 显示 Re-Jailbreak

%title插图%num

安装好unc0ver之后,就需要配置 Cydia 了

配置你的Cydia
点击 Cydia->软件源->编辑->添加 添加源 build.frida.re ,apt.cydiami.com,并进行更新。
搜索 SSH ,选择 OpenSSH 进行安装
搜索 frida ,由于是 5S,我们选择 Frida for pre-A12 devices
搜索 AFC ,安装 AFC2 iOS12系统文件访问
至此,越狱完成,可以访问系统文件了。

%title插图%num

揭秘Context(上下文)

刚刚学android或者js等,都会看见这个频繁的字眼——Context。
意为”上下文“。

本文主要记述,Context到底是什么、如何理解Context、一个APP可以有几个Context、Context能干啥、Context的作用域、获取Context、全局获取Context技巧。

思考:
Java:万物皆对象。Flutter:万物皆组件。
俗语:”没对象吗?自己new一个啊~“
既然大多数情况可以new一个实例,那么,我们在android中的Activity实例怎么获取呢?Activity.instance可以获取activity。既然Activity也大致归属于一个类,那么可不可以用 Activity activity=new Activity(); 呢?安卓不像Java程序一样,随便创建一个类,写个main()方法就能运行,**Android应用模型是基于组件的应用设计模式,组件的运行要有一个完整的Android工程环境。在这个环境下,Activity、Service等系统组件才能正常工作,而这些组件不能采用普通的java对象创建方式,new一下是不能创建实例的,而是要有它们各自的上下文环境,也就是Context.
所以说,Context是维持android各组件能够正常工作的一个核心功能类。

what ‘s Context:
(本图为沙拉查词给出的中文翻译)
%title插图%num
有点晦涩难懂。但在程序中,我们可理解为当前对象在程序中所处的一个环境,一个与系统交互的过程。 比如QQ和你们自己的女朋友聊天时(没有grilfriend的可自己跳过举例),此时的context是指的聊天界面以及相关的数据请求与传输,Context在加载资源、启动Activity、获取系统服务、创建View等操作都要参与。
所以,一个Activity就是一个Context(getActivity()==getContext),一个Service也是一个Context。Android把场景抽象为Context类,用户和操作系统的每一次交互都是一个场景,比如:打电话、发短信等,都有activity,还有一些我们肉眼看不见的后台服务。一个应用程序可以认为是一个工作环境,用户在这个环境中切换到不同的场景,这就像服务员,客户可能是外卖小哥、也可能是农民工等,这些就是不同的场景,而服务员就是一个应用程序。

How to understand the ‘Context’:
Context理解为”上下文“/”场景“,可能还是很抽象。那么我们可以做一个比喻:
一个APP是仙剑奇侠传3电视剧,Activity、Service、BroadcastReceiver、ContentProvider这四大组件就是电视剧的主角。它们是导演(系统)一开始就确定好试镜成功的人。换言之, 不是我们每个人都能被导演认可的。有了演员,就要有镜头啊,这个镜头便是(Context)。通过镜头,我们才能看见帅气 的胡歌。演员们都是在镜头(Context环境)下表演的。那么Button这些组件子类型就是配角,它们没有那么重要,随便一个组件都能参与演出(即随便new 一个实例),但是它们也需要参与镜头,不然一部戏只有主角多没意思,魔尊重楼还是要的,魔尊也要露面(工作在Context环境下),所以可以用代码new Button();或者xml布局定义一个button。

打开AndroidStudio,输入Context,然后ctrl+鼠标左键追朔其源码(看源码一般都先看注释便于理解):import android.content.Context;

%title插图%num
看注释,TMD,是English,那么笔者这里就用小学生英语水平来翻译一哈哈:
Context提供了关于应用环境全局信息的接口。它是一个abstract类,它的执行被Android系统提供,允许获取以应用为特征的资源和类型,是一个统领一些资源APP环境变量等的上下文。通过它可以获取应用程序的资源和类(包括应用级别操作,如启动Activity,发广播,接收intent等)。abstract会有它的实现类。在源码中,我们可以通过AndroidStudio去查看它的子类,得到以下关系:
它有2个具体实现子类:ContextImpl、ContextWrapper。

其中,ContextWrapper类,只是一个包装类,其构造函数中必须包含一个Context引用,同时它提供了attachBaseContext()用于给ContextWrapper对象中指定真正的Context对象,调用它的方法都会被转向其所包含的真正的Context对象。
ContextThemeWrapper类其内部包含了与主题相关的接口。主题就是清单文件中android:theme为Application或Activity元素指定的主题。(Activity才需要主题,Serviceu不需要,因为服务是没有界面的后台场景,所以服务直接继承ContextWrapper。Application同理。)而Contextlmpl类则是真正实现了Context中的所有函数,应用程序中所调用的各种Context类的方法,其实现均来自这个类。
换言之:Context的2个实现子类分工的,其中ContextImpl是Context的具体是实现类,而ContextWrapper则是Context的包装类。Activity、Application、Service都继承自ContextWrapper(Activity继承自ContextWrapper的子类ContextThemeWrapper),但它们的初始化过程中都会创建ContextImpl对象,由ContextImpl实现Context中的方法。
How much has Context in a App:
关键在于对COntext的理解。从上面提到的实现子类可以看出,在APP中,Context的具体实现子类是Acitivity、Service、Applicaiton。所以Context’s number=Activity’s number + Service’s number+1(1个APP只有一个Application)。为啥不是4大组件,上面不是说四大组件也是主角吗?看看BroadcastReceiver和ContentProvider的源码可以知道它们并不是Context的子类,它们持有的Context都是其他地方传递过去的(比如我们发送广播intent中的context就是外部传递过来的),所以不计数它们。

Context’s method:
Context哪里会用到它。刚开始了解Android的时候不知道它是个啥玩意儿,但是久了发现有些地方就不得不传这个参数。
比如Toast、启动Activity、启动Service、发送广播、操作数据库等等都需要传Context参数,具体例子就不说了。详细可以看后文将提到的如何获取它。

Context’s 作用域:
不是随便获取一个Context实例就可以的,它的使用有一些规则和限制。因为Context的具体实例是由ContextImpl类去实现的,因此,Activity、Service、Application3种类型的Context都是等价的。但是,需要注意的是,,有些场景,比如启动Activity、弹出Dialog等。为了安全,Android不允许Activity或者Dialog凭空出现,一个Activity的启动肯定是由另一个Activity负责的,也就是以此形成的返回栈(具体可以看看任主席的《Android开发艺术探索》)而Dialog则必须是在一个Activity上弹出(系统Alert类型的Dialog除外),这种情况下, 我们只能用Activity类型的Context,否则报错。

%title插图%num

Activity继承自ContextThemeWrapper,而Application和Service继承ContextWrapper,所以ContextThemeWrapper在ContextWrapper的基础上作了一些操作,使得Activity更加厉害。
关于表格中提到的Application和Service不推荐的2种情况:

如果用ApplicationContext去启动一个LaunchMode为standard的Activity的时候会报错:androud,util.AndroidRuntimeException:Calling startActivity from outside of an Activity context require the FLAG_ACTIVITY_NEW_TASK flag。Is this really what you want?
翻译一下,并了解这个FLAG的都知道,此时的非Activity类型的Context并没有所谓的返回栈,因此带启动的Activity就找不到栈。它还给我们明确之处了FLAG的解决办法,这样启动的时候就为它创建一个新的任务栈,而此时Activity是以Single Task模式启动的。所以这种用Application Context启动Activity的方式不推荐,Service同理。
在Application和Service中去layout inflate也是合法的,但是会使用系统默认的主题样式,如果自定义了某些样式可能不会被使用,所以也不推荐。
注:和UI相关的,都应该使用Activity Context来处理。其他的一些操作,Service、Activity、Application等实例都是可以的。同时要注意Context的引用持有,防止内存泄漏。可在被销毁的时候,置Context为null。

How to get the ‘Context’:
常用4种方法获取Context对象:

View.getContext():返回当前View对象的Context对象。通常是当前正在展示的Activity对象。
Activity,getApplicationContext()[后文会详细介绍这个方法]:获取当前Activity所在应用进程的Context对象,通常我们使用Context对象时,要优先考虑这个全局的进程Context。
ContextWrapper.getBaseContext():用来获取一个ContextWrapper进行装饰之前的Context。实际开发很少用,也不建议使用。
Activity.this:返回当前Activity的实例,如果的UI控件需要使用Activity作为Context对象,但默认的Toast实际上使用的ApplicationContext也可以。
实现View.OnClick监听方法中,写Toast,不要用this,因为this,在onClick(View view)指的是view对象而不是Activity实例,所以在这个方法中,应该使用”当前的Activity名.this“,这是入门者比较容易混淆的地方。
getApplication()和getApplicationContext():
获取当前Application对象用getApplicationContext.但是getApplication又是什么。
我们可以自己写代码打印一下:
Application app=(Application)getApplication();
Log.e(TAG,”getApplication is “+app);
Context context=getApplicationContext();
Log.e(TAG,”getApplicationContext is “+ context);

运行后看logcat,效果图就不贴了(电脑卡)。从打印结果可以看出它们2个的内存地址是相同的,即它们是同一个对象。 因为Application本来就是一个Context,那么这里获取的getApplicationContext()自然也是Application本身的实例了。那这2个相同方法存在的意义是啥?(双胞胎?)实际上这2个方法在作用域上有比较大的区别。 getApplication()一看就知道是用来获取Application实例的(道理可以联想getActivity())。但getApplication()只有在Activity和Service中才能调用的到。 对于比如BroadcastReceiver等中也想要获取Application实例,这时就需要getApplicationContext()方法。

//继承BroadcastReceiver并重写onReceive()方法
@Override
public void onReceive(Context context.Intent intent){
Application app=(Application)context.getApplicationContext();
}

内存泄漏之Context:
我们经常会遇到内存泄漏,比如Activity销毁了,但是Context还持有该Activity的引用,造成了内存泄漏。(经常遇到)

2种典型的错误引用方式:

错误的单例模式:
public class Singleton{
private static Singleton instancel
private Context context;
private Singleton(Context context){
this.context=context;
}
public static Singleton getInstance(Context context){
if(instance == null ){
instance=new Singleton(context);
}
return instance;
}
}

熟悉单例模式的都知道,这是一个非线程安全的单例模式,instance作为静态对象,其生命周期要长于普通的对象(单例直到APP退出后台才销毁),其中也包含了Activity。比如Activity A去getInstance()得到instance对象,传入this,常驻内存的Singleton保存了我们传入的A对象,并一直持有,即使Activity被销毁掉,但因为它的引用还存在于一个Singleton中,就不可能被GC掉,这样就导致了内存泄漏。比如典型的数据库操作,存储数据,需要重复的去索取数据,用单例保持数据和拿到Activity持有context引用,因为单例可以看作是上帝,它帮我们保存数据。所以即使Activity被finish掉,还有它的引用在Singleton中。

View持有Activity引用:
public class MainActivity extend Activity{
private static Drawable mDrawable;
@Override
protected void onCreate(Bundle saveInstanceState){
super.onCreate();
setContentView(R.layout.activity_main);
ImageView imageview=new ImageView(this);//通过代码动态的创建组件,而不是传统的xml配置组件,这里的ImageView持有当前Activity的引用。
mDrawable=getResources().getDrawable(R.drawable.ic_launcher);
imageview.setImageDrawable(mDrawable);
}
}

上述代码中,有一个static的Drawable对象。当ImageView设置这个Drawable的时候,ImageView保存了这个mDrawable的引用,而ImageView初始化的时候又传入了this,此处的this是指MainActivity的context。因为被static修饰的mDrawable是常驻内存的(比类还要早加载)。MainActivity是它的间接引用了,当MainActivity被销毁的时候,也不能被GC掉,就造成了内存泄漏。

How to get the context in the whole :
大量的地方都需要使用Context,我们常常会因为不知道怎么得到这个Context而苦恼。那么,全局获取Context无疑是*好的解决方案。
很多时候,我们也不是经常为得不到Context而发愁,毕竟我们很多的操作都是在活动中进行的,而活动本身就是一个Context对象。但APP架构复杂后,很多逻辑代码都脱离了Activity类,此时又需要使用Context,所以我们需要采取全局获取Context的方法。
举例, 我们平常经常会写网络工具类,比如下面的这些代码:

public calss HttpUtil{
public static void sendHttpRequest(final String address,final HttpCallbackListener listener){
new Thread(new Runnable()){
@Override
public void run(){
HttpURLConnection connection=null;
try{
URL url =new URL(address);
connection=(HttpURLConnection)url.openConnection();
connection.setRequestMethod(“GET”);
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
connection.setDoInput(true);
connection.setDoOutput(true);
InputStream in =connection.getInputStream();
BufferedReader reader=new BufferedReader(new InputStreamReader(in));
StringBuilder response=new StringBuilder();
String line;
while((line=reader.readLine())!=nulll){
response.append(line);
}
if(listener!=null){
//回调onFinish()
listener.onFinish(response.toString);
}
}catch(Execption e){
if(listener!=null){
//回调onError()
listener.onError(e);
}
}finally{
if(connection!=null){
connection.disconnect();
}
}
}}.start();
}
}

上述代码中使用sendHttpRequest()方法来发送HTTP请求显然没问题。并且还可以在回调方法中处理服务器返回的数据。但是这个方法还可以被优化。当检测不到网络存在的时候就给用户一个Toast,并不再执行后面的代码。问题来了,Toast需要一个Context参数,但是在本来没有可以传递的Context对象。。。
一般思路:在方法中添加一个COntext参数:

public static void sendHttpRequest(final String address,final HttpCallbackListener listener,final Context context){
if(!isNetWorkAvailable()){
Toast.makeText(context,……);
……
}
……

看似可以,但是有点甩锅。我们将获取Context的任务转移到了sendHttpRequest()方法的调用方。至于调用方能不能得到COntext对象就不是我们要考虑的问题了。

甩锅不一定是通用的解决方案。于是这里介绍哈如何获取全局Context的步骤:,通过它在项目的任何地方都能轻松的获取到Context。:

Android提供了一个Application类,每当APP启动的时候,系统就会自动将这个类进行初始化。我们可以定制一个自己的Application类,以便管理程序内一些全局的状态信息,比如说全局Context。
定制一个自己的Application并不复杂,首先, 需要创建一个MyApplication类继承自系统的Application:
public calss MyApplication extends Application{
private static Context context;
@Overrride
public void onCreate(){
context=getApplicationContext();
}
public static Context getContext(){
return context;
}
}

代码很简单,容易理解。重写了父类的onCreate()方法,并通过调用getApplicationContext()方法得到一个应用程序级别的Context,然后又提供了一个静态的getContext()方法,在这里将刚才获取到的COntext进行返回。

接下来,我们需要告诉系统,当程序启动的时候应该初始化MyApplication类,而不是系统默认的Application类。这一步需要在清单文件里面实现,找到清单文件的标签下进行指定就可以了:

注意:这里一定要加上完整的包名,不然系统将无法找到这个类。

以上就是实现了一种全局获取Context的机制,在这个项目的任何地方使用Context,只需要调用MyApplication.getContext()就可以了。
关于自定义Application和LitePal配置冲突的问题:
自定义需要在清单文件写出android.name=”……”。而为了让LitePal可以正常工作,也需要在清单文件下,配置:

android:name=”org.litepal.LitePalApplication”

道理也是一样的,这样配置后,LitePal就能在内部自动获取到Context了。
问题:当都已经配置过自定义的Application怎么办?岂不是和LitePalApplication冲突了?
解答:任何一个项目都只能配置一个Application. 对于这种情况,LitePalApplication给出了很简单的解决方案,在自定义的Application中去调用LitePal的初始化方法就可以了:

public calss MyApplication extends Application{
private static Context context;
@Overrride
public void onCreate(){
context=getApplicationContext();
LitePalApplication.initialize(context);
}
public static Context getContext(){
return context;
}
}

这种写法就相当于我们把全局Context对象通过参数传递给了LitePal,效果和在清单文件配置LitePalApplication是一样的。

总结,如何在程序中正确的使用Context:
一般Context造成的内存泄漏,几乎都是当Context销毁的时候,因为被引用导致销毁失败。而Application的Context对象可以简单的理解为伴随着进程存在的(它的生命周期也很长,毕竟APP加载的时候先加载Application,我们可以自定义Application然后继承系统的Application)。
正确使用:

当Applicatin的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context;
不要让生命周期长于Activity的对象持有Activity的引用。
尽量不要在Activity中使用非静态内部类。非静态内部类会隐式持有外部类实例的引用。如果使用静态内部类,将外部实例引用作为弱引用持有。
获取全局context的另一种思路:
ActivityThread是主进程的入口,它的currentApplication返回值是application.

import android.app.Application;

import java.lang.reflect.InvocationTargetException;

/**
* 这种方式获取全局的Application 是一种拓展思路。
*

* 对于组件化项目,不可能把项目实际的Application下沉到Base,而且各个module也不需要知道Application真实名字
*

* 这种一次反射就能获取全局Application对象的方式相比于在Application#OnCreate保存一份的方式显示更加通用了
*/
public class AppGlobals {
private static Application sApplication;

public static Application getApplication() {
if (sApplication == null) {
try {
sApplication = (Application) Class.forName(“android.app.ActivityThread”)
.getMethod(“currentApplication”)
.invoke(null, (Object[]) null);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return sApplication;
}
}

关于Context(上下文)的理解

一直听到上下文一说,一直没弄清楚到底是啥意思,今天总结一下,不知道对不对

感觉对Context这个词翻译的不太好,不应该叫上下文,应该直接就叫“环境”,不过都这么叫,就叫上下文好了

所谓的上下文就是指语境,每一段程序都有很多的外部变量。只有想Add这种简单的函数才是没有外部变量的。一旦写的一段程序中有了外部变量,这段程序就是不完整的,不能独立运行,要想让他运行,就必须把所有的外部变量的值一个一个的全部传进去,这些值的集合就叫上下文。

说的通俗一点就是一段程序的执行需要依赖于外部的一些环境(外部变量等等),如果没有这些外部环境,这段程序是运行不起来的。

今天在segmentfault社区看见一则回答,生动的解释了什么是程序的上下文:
%title插图%num
可能上面的例子有点绕,但是多看一下就明白了,其实说白了,程序上下文可以理解为context实例中的全局变量,你给它什么样的值,它就呈现对应的值或者状态

子程序之于程序,进程之于操作系统,甚至app的一屏之于app,都是一个道理

当程序执行了一部分,要跳转到其他的地方,而跳转到的地方需要之前程序的一些结果(包括但不限于外部变量,外部对象等等)。

app点击一个按钮进入一个新的界面,也要保存你是在那个屏幕跳过来的等等信息,以便你点击返回的时候能正确跳回

上面这些都是上下文的典型例子,所以把“上下文”理解为环境就可以了。(而且上下文虽然是上下文,但是程序里面一般都只有上文而已,只是叫的好听叫上下文。进程中断在操作系统中是有上有下的)。

所以说,通俗一点理解就是,当程序从一个位置调到另一个位置的时候,这个时候就叫上下文的切换(因为他要保存现场,各种的压栈,出栈等等),进程之间切换也叫上下文切换,因为也要保存现场,以便切换回之前的线程

以我自己的认识水平来说,在C或者C++中,context一般就是一个结构体,用来存储一些关键信息,比如切换上下文时,要保存切换之前的状态和数据,这需要一个结构体来承担,然后将contex中的状态和数据重新赋值为新的,这样就切换了,等运行完了之后,又要切换回来,那么之前保存的那些状态和数据又要重新启用了,就是这么回事。

我想之所以在多线程网络编程中,现在采用的多是one loop per thread + thread poll,而不是采用一个socket一个线程,就是因为在切换线程(也就是切换上下文)的时候,需要保留大量的现场数据,而在重新切回到该线程时,又要恢复这个保存的数据(即保存的环境),保存/恢复都是需要花费cpu大量的资源和时间的,所以不如one loop per thread + thread poll的模式,当然one loop per thread + thread poll还有其他的好处,这只是其中一个好处,不需要大量的切换上下文,占用cpu大量的资源。

关于Android Context 你必须知道的一切

1、Context概念
其实一直想写一篇关于Context的文章,但是又怕技术不如而误人子弟,于是参考了些资料,今天准备整理下写出来,如有不足,请指出,参考资料会在醒目地方标明。

Context,相信不管是*天开发Android,还是开发Android的各种老鸟,对于Context的使用一定不陌生~~你在加载资源、启动一个新的Activity、获取系统服务、获取内部文件(夹)路径、创建View操作时等都需要Context的参与,可见Context的常见性。大家可能会问到底什么是Context,Context字面意思上下文,或者叫做场景,也就是用户与操作系统操作的一个过程,比如你打电话,场景包括电话程序对应的界面,以及隐藏在背后的数据;

但是在程序的角度Context又是什么呢?在程序的角度,我们可以有比较权威的答案,Context是个抽象类,我们可以直接通过看其类结构来说明答案:
%title插图%num

可以看到Activity、Service、Application都是Context的子类;

也就是说,Android系统的角度来理解:Context是一个场景,代表与操作系统的交互的一种过程。从程序的角度上来理解:Context是个抽象类,而Activity、Service、Application等都是该类的一个实现。

在仔细看一下上图:Activity、Service、Application都是继承自ContextWrapper,而ContextWrapper内部会包含一个base context,由这个base context去实现了*大多数的方法。

先扯这么多,有能力了会从别的角度去审视Context,加油~

2、Context与ApplicationContext
看了标题,千万不要被误解,ApplicationContext并没有这个类,其实更应该叫做:Activity与Application在作为Context时的区别。嗯,的确是这样的,大家在需要Context的时候,如果是在Activity中,大多直接传个this,当在匿名内部类的时候,因为this不能用,需要写XXXActivity.this,很多哥们会偷懒,直接就来个getApplicationContext。那么大家有没有想过,XXXActivity.this和getApplicationContext的区别呢?

XXXActivity和getApplicationContext返回的肯定不是一个对象,一个是当前Activity的实例,一个是项目的Application的实例。既然区别这么明显,那么各自的使用场景肯定不同,乱使用可能会带来一些问题。

下面开始介绍在使用Context时,需要注意的问题。

3、引用的保持
大家在编写一些类时,例如工具类,可能会编写成单例的方式,这些工具类大多需要去访问资源,也就说需要Context的参与。

在这样的情况下,就需要注意Context的引用问题。

例如以下的写法:

package com.mooc.shader.roundimageview;

import android.content.Context;

public class CustomManager
{
private static CustomManager sInstance;
private Context mContext;

private CustomManager(Context context)
{
this.mContext = context;
}

public static synchronized CustomManager getInstance(Context context)
{
if (sInstance == null)
{
sInstance = new CustomManager(context);
}
return sInstance;
}

//some methods
private void someOtherMethodNeedContext()
{

}
}

对于上述的单例,大家应该都不陌生(请别计较getInstance的效率问题),内部保持了一个Context的引用;
这么写是没有问题的,问题在于,这个Context哪来的我们不能确定,很大的可能性,你在某个Activity里面为了方便,直接传了个this;这样问题就来了,我们的这个类中的sInstance是一个static且强引用的,在其内部引用了一个Activity作为Context,也就是说,我们的这个Activity只要我们的项目活着,就没有办法进行内存回收。而我们的Activity的生命周期肯定没这么长,所以造成了内存泄漏。

那么,我们如何才能避免这样的问题呢?

有人会说,我们可以软引用,嗯,软引用,假如被回收了,你不怕NullPointException么。

把上述代码做下修改:

public static synchronized CustomManager getInstance(Context context)
{
if (sInstance == null)
{
sInstance = new CustomManager(context.getApplicationContext());
}
return sInstance;
}

这样,我们就解决了内存泄漏的问题,因为我们引用的是一个ApplicationContext,它的生命周期和我们的单例对象一致。
这样的话,可能有人会说,早说嘛,那我们以后都这么用不就行了,很遗憾的说,不行。上面我们已经说过,Context和Application Context的区别是很大的,也就是说,他们的应用场景(你也可以认为是能力)是不同的,并非所有Activity为Context的场景,Application Context都能搞定。

下面就开始介绍各种Context的应用场景。

4、Context的应用场景

%title插图%num

大家注意看到有一些NO上添加了一些数字,其实这些从能力上来说是YES,但是为什么说是NO呢?下面一个一个解释:

数字1:启动Activity在这些类中是可以的,但是需要创建一个新的task。一般情况不推荐。

数字2:在这些类中去layout inflate是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。

数字3:在receiver为null时允许,在4.2或以上的版本中,用于获取黏性广播的当前值。(可以无视)

注:ContentProvider、BroadcastReceiver之所以在上述表格中,是因为在其内部方法中都有一个context用于使用。

好了,这里我们看下表格,重点看Activity和Application,可以看到,和UI相关的方法基本都不建议或者不可使用Application,并且,前三个操作基本不可能在Application中出现。实际上,只要把握住一点,凡是跟UI相关的,都应该使用Activity做为Context来处理;其他的一些操作,Service,Activity,Application等实例都可以,当然了,注意Context引用的持有,防止内存泄漏。

5、总结
好了,到此,Context的分析基本完成了,希望大家在以后的使用过程中,能够稍微考虑下,这里使用Activity合适吗?会不会造成内存泄漏?这里传入Application work吗?

由于参考内容过多,本文改为译文咯~~

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