BLACK_OX

The palest ink is better than the best memory


  • 首页

  • 标签

  • 分类

  • 归档

  • 关于

  • 搜索

flutter 1.17共享engine

发表于 2020-06-03 | 分类于 flutter | 评论数: | 阅读次数:
本文字数: 30k | 阅读时长 ≈ 28 分钟

前言

flutter 升级到 1.17之后,app ios 线上遇到一个crash ,通过官方的 符号表文件 flutter.dsym 还原出堆栈如下

1
2
3
4
5
6
0 auto fml::internal::CopyableLambda<flutter::Shell::OnPlatformViewCreated(std::__1::unique_ptr<flutter::Surface, std::__1::default_delete<flutter::Surface> >)::$_8>::operator()<>() const (in Flutter) (make_copyable.h:24)
1 auto fml::internal::CopyableLambda<flutter::Shell::OnPlatformViewCreated(std::__1::unique_ptr<flutter::Surface, std::__1::default_delete<flutter::Surface> >)::$_8>::operator()<>() const (in Flutter) (make_copyable.h:24)
2 fml::MessageLoopImpl::FlushTasks(fml::FlushType) (in Flutter) (message_loop_impl.cc:129)
3 fml::MessageLoopDarwin::OnTimerFire(__CFRunLoopTimer*, fml::MessageLoopDarwin*) (in Flutter) (message_loop_darwin.mm:76)
9 fml::MessageLoopDarwin::Run() (in Flutter) (message_loop_darwin.mm:47)
10 void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, fml::Thread::Thread(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)::$_0> >(void*) (in Flutter) (thread:352)

这里还只能看到crash在engine的c++代码中,具体原因未知

阅读全文 »

flutter 1.17升级

发表于 2020-05-21 | 分类于 flutter | 评论数: | 阅读次数:
本文字数: 16k | 阅读时长 ≈ 15 分钟

升级

最近官方发布了flutter 稳定版本1.17.0 ,记录下升级1.17 ios上碰到的的问题

App产物

在 1.12.13 的时候,为了支持模拟器运行,会进行 debug 产物 跟 release 产物的merge (lipo create …)
debug 产物 x86 、release 产物 arm64 arm7

升级到1.17.0 之后 ,merge报错

IMAGE

lipo 查看下

IMAGE

发现针对模拟器的debug产物 含有arm64

Debug Flutter tool源码, build 里面进行了两次createStubAppFramework(iphone && simulator)
然后做了merge,实际上environment参数里面 iosArchs只有 arch x86,所以问题出在这里

DebugUniveralFramework

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
@override
Future<void> build(Environment environment) async {
// Generate a trivial App.framework.
final Set<DarwinArch> iosArchs = environment.defines[kIosArchs]
?.split(' ')
?.map(getIOSArchForName)
?.toSet()
?? <DarwinArch>{DarwinArch.arm64};
final File iphoneFile = environment.buildDir.childFile('iphone_framework');
final File simulatorFile = environment.buildDir.childFile('simulator_framework');
final File lipoOutputFile = environment.buildDir.childFile('App');
final RunResult iphoneResult = await createStubAppFramework(
iphoneFile,
SdkType.iPhone,
// Only include 32bit if it is contained in the active architectures.
include32Bit: iosArchs.contains(DarwinArch.armv7)
);
final RunResult simulatorResult = await createStubAppFramework(
simulatorFile,
SdkType.iPhoneSimulator,
);
if (iphoneResult.exitCode != 0 || simulatorResult.exitCode != 0) {
throw Exception('Failed to create App.framework.');
}
final List<String> lipoCommand = <String>[
'xcrun',
'lipo',
'-create',
iphoneFile.path,
simulatorFile.path,
'-output',
lipoOutputFile.path
];
final RunResult lipoResult = await processUtils.run(
lipoCommand,
);

if (lipoResult.exitCode != 0) {
throw Exception('Failed to create App.framework.');
}
}

解决办法 可以通过archs 判断下具体执行

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
@override
Future<void> build(Environment environment) async {
// Generate a trivial App.framework.
final Set<DarwinArch> iosArchs = environment.defines[kIosArchs]
?.split(' ')
?.map(getIOSArchForName)
?.toSet()
?? <DarwinArch>{DarwinArch.arm64};
final File iphoneFile = environment.buildDir.childFile('iphone_framework');
final File simulatorFile = environment.buildDir.childFile('simulator_framework');
final File lipoOutputFile = environment.buildDir.childFile('App');

RunResult iphoneResult;

if(iosArchs.contains(DarwinArch.arm64) || iosArchs.contains(DarwinArch.armv7)) {
iphoneResult = await createStubAppFramework(
iphoneFile,
SdkType.iPhone,
// Only include 32bit if it is contained in the active architectures.
include32Bit: iosArchs.contains(DarwinArch.armv7)
);
if (iphoneResult.exitCode != 0) {
throw Exception('(iphoneResult)Failed to create App.framework.');
}
}

RunResult simulatorResult;

if(iosArchs.contains(DarwinArch.x86_64)) {
simulatorResult = await createStubAppFramework(
simulatorFile,
SdkType.iPhoneSimulator,
);
if (simulatorResult.exitCode != 0) {
throw Exception('(simulatorResult)Failed to create App.framework.');
}
}

if(simulatorResult == null) {
iphoneFile.copySync(lipoOutputFile.path);
return;
}

if(iphoneResult == null) {
simulatorFile.copySync(lipoOutputFile.path);
return;
}

final List<String> lipoCommand = <String>[
'xcrun',
'lipo',
'-create',
iphoneFile.path,
simulatorFile.path,
'-output',
lipoOutputFile.path
];
final RunResult lipoResult = await processUtils.run(
lipoCommand,
);

if (lipoResult.exitCode != 0) {
throw Exception('Failed to create App.framework.');
}
}

突然想到 既然1.17 对debug 产物做了arm64的支持,那我们收集产物是不是可以不用自己做merge,发现是不可以的
因为除了App.framework,还有plugin native代码生成的pod静态库 libxxx.a。
静态库 是哪里生成的呢?

这里 build_ios.dart -> buildXcodeProject

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
...

final List<String> buildCommands = <String>[
'/usr/bin/env',
'xcrun',
'xcodebuild',
'-configuration', configuration,
];

...

if (buildForDevice) {
buildCommands.addAll(<String>['-sdk', 'iphoneos']);
} else {
buildCommands.addAll(<String>['-sdk', 'iphonesimulator', '-arch', 'x86_64']);
}

...

这里只针对x86做了xcode build,所以还是要自己merge的…

flutter tool debug

发表于 2020-03-13 | 分类于 flutter | 评论数: | 阅读次数:
本文字数: 2.6k | 阅读时长 ≈ 2 分钟

Flutter

flutter 开发过程中,少不了会运行一些 flutter 命令 ,比如 flutter build xxx 、 flutter run 等等
看下 bin/flutter 脚本,背后都是 flutter_tool 在执行各种操作。

1
2
3
4
5
6
7
8

FLUTTER_TOOLS_DIR="$FLUTTER_ROOT/packages/flutter_tools"
SNAPSHOT_PATH="$FLUTTER_ROOT/bin/cache/flutter_tools.snapshot"
STAMP_PATH="$FLUTTER_ROOT/bin/cache/flutter_tools.stamp"
SCRIPT_PATH="$FLUTTER_TOOLS_DIR/bin/flutter_tools.dart"
DART_SDK_PATH="$FLUTTER_ROOT/bin/cache/dart-sdk"

"$DART" --packages="$FLUTTER_TOOLS_DIR/.packages" $FLUTTER_TOOL_ARGS "$SNAPSHOT_PATH" "$@"
阅读全文 »

flutter共享engine

发表于 2020-03-10 | 分类于 flutter | 评论数: | 阅读次数:
本文字数: 6.2k | 阅读时长 ≈ 6 分钟

flutter 共享引擎 问题记录

共享引擎,就是只有一个 flutter engine,每个页面一个 flutterviewcontroller。
flutter页面 切换,引擎会相应的 detach atach

最近 升级 flutter 到 v1.12.13 版本后,贡献引擎遇到的几个问题 记录下

阅读全文 »

flutter engine 定制

发表于 2020-03-05 | 分类于 flutter | 评论数: | 阅读次数:
本文字数: 776 | 阅读时长 ≈ 1 分钟

…

使用Flutter开发的时候最直接接触的并不是 Flutter Engine 而是 Flutter Framework(https://github.com/flutter/flutter)
在flutter framework 的 目录里面 有编译好的engine 产物

简单说就是, 编译引擎 替换 产物文件就好了

路径 flutter_path/bin/cache/artifacts/engine/ios

参考

Flutter Engine定制流程
Flutter Engine 编译指北

flutter eventChannel crash on ios

发表于 2019-10-21 | 分类于 flutter | 评论数: | 阅读次数:
本文字数: 13k | 阅读时长 ≈ 11 分钟

flutter issue

crash

记录一次困扰了很久的 flutter event channel crash

EventChannel dart

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
Stream<dynamic> receiveBroadcastStream([ dynamic arguments ]) {
final MethodChannel methodChannel = MethodChannel(name, codec);
StreamController<dynamic> controller;
controller = StreamController<dynamic>.broadcast(onListen: () async {
defaultBinaryMessenger.setMessageHandler(name, (ByteData reply) async {
if (reply == null) {
controller.close();
} else {
try {
controller.add(codec.decodeEnvelope(reply));
} on PlatformException catch (e) {
controller.addError(e);
}
}
return null;
});
try {
await methodChannel.invokeMethod<void>('listen', arguments);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: ErrorDescription('while activating platform stream on channel $name'),
));
}
}, onCancel: () async {
defaultBinaryMessenger.setMessageHandler(name, null);
try {
await methodChannel.invokeMethod<void>('cancel', arguments);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: ErrorDescription('while de-activating platform stream on channel $name'),
));
}
});
return controller.stream;
}

FlutterChannel.mm

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
static void SetStreamHandlerMessageHandlerOnChannel(NSObject<FlutterStreamHandler>* handler,
NSString* name,
NSObject<FlutterBinaryMessenger>* messenger,
NSObject<FlutterMethodCodec>* codec) {
__block FlutterEventSink currentSink = nil;
FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
FlutterMethodCall* call = [codec decodeMethodCall:message];
if ([call.method isEqual:@"listen"]) {
if (currentSink) {
FlutterError* error = [handler onCancelWithArguments:nil];
if (error)
NSLog(@"Failed to cancel existing stream: %@. %@ (%@)", error.code, error.message,
error.details);
}
currentSink = ^(id event) {
if (event == FlutterEndOfEventStream)
[messenger sendOnChannel:name message:nil];
else if ([event isKindOfClass:[FlutterError class]])
[messenger sendOnChannel:name message:[codec encodeErrorEnvelope:(FlutterError*)event]];
else
[messenger sendOnChannel:name message:[codec encodeSuccessEnvelope:event]];
};
FlutterError* error = [handler onListenWithArguments:call.arguments eventSink:currentSink];
if (error)
callback([codec encodeErrorEnvelope:error]);
else
callback([codec encodeSuccessEnvelope:nil]);
} else if ([call.method isEqual:@"cancel"]) {
if (!currentSink) {
callback(
[codec encodeErrorEnvelope:[FlutterError errorWithCode:@"error"
message:@"No active stream to cancel"
details:nil]]);
return;
}
currentSink = nil;
FlutterError* error = [handler onCancelWithArguments:call.arguments];
if (error)
callback([codec encodeErrorEnvelope:error]);
else
callback([codec encodeSuccessEnvelope:nil]);
} else {
callback(nil);
}
};
[messenger setMessageHandlerOnChannel:name binaryMessageHandler:messageHandler];
}

EventSink

正常结束stream流 eventSink(FlutterEndOfEventStream) ,异常结束stream流 eventSink(FlutterError) 都会回调执行 onCancel

参考

Flutter 与 Native(iOS) 通信原理
深入Flutter技术内幕:Platform Channel设计与实现

flutter ios 13 dark mode

发表于 2019-10-14 | 分类于 flutter | 评论数: | 阅读次数:
本文字数: 9.1k | 阅读时长 ≈ 8 分钟

前言

ios 13 开启 dark model,flutter页面status bar文字一直是白色

flutter issues

1
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark);

设置dark style 并没有用

SystemChrome

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
static void setSystemUIOverlayStyle(SystemUiOverlayStyle style) {
assert(style != null);
if (_pendingStyle != null) {
// The microtask has already been queued; just update the pending value.
_pendingStyle = style;
return;
}
if (style == _latestStyle) {
// Trivial success: no microtask has been queued and the given style is
// already in effect, so no need to queue a microtask.
return;
}
_pendingStyle = style;
scheduleMicrotask(() {
assert(_pendingStyle != null);
if (_pendingStyle != _latestStyle) {
SystemChannels.platform.invokeMethod<void>(
'SystemChrome.setSystemUIOverlayStyle',
_pendingStyle._toMap(),
);
_latestStyle = _pendingStyle;
}
_pendingStyle = null;
});
}

FlutterPlatformPlugin

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
- (void)setSystemChromeSystemUIOverlayStyle:(NSDictionary*)message {
NSString* style = message[@"statusBarBrightness"];
if (style == (id)[NSNull null])
return;

UIStatusBarStyle statusBarStyle;
if ([style isEqualToString:@"Brightness.dark"])
statusBarStyle = UIStatusBarStyleLightContent;
else if ([style isEqualToString:@"Brightness.light"])
statusBarStyle = UIStatusBarStyleDefault;
else
return;

NSNumber* infoValue = [[NSBundle mainBundle]
objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"];
Boolean delegateToViewController = (infoValue == nil || [infoValue boolValue]);

if (delegateToViewController) {
// This notification is respected by the iOS embedder
[[NSNotificationCenter defaultCenter]
postNotificationName:@(kOverlayStyleUpdateNotificationName)
object:nil
userInfo:@{@(kOverlayStyleUpdateNotificationKey) : @(statusBarStyle)}];
} else {
// Note: -[UIApplication setStatusBarStyle] is deprecated in iOS9
// in favor of delegating to the view controller
[[UIApplication sharedApplication] setStatusBarStyle:statusBarStyle];
}
}

engine 源码中 可以看到 没有 UIStatusBarStyleDarkContent

尝试 去掉 info.plist 中的 UIViewControllerBasedStatusBarAppearance

然后 监听 通知

1
2
3
4
5
6
7
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appStatusBar:) name:@"io.flutter.plugin.platform.SystemChromeOverlayNotificationName" object:nil];

- (void)appStatusBar:(id)notification {
if (@available(iOS 13.0, *)) {
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDarkContent;
}
}

mustache之dart

发表于 2019-05-31 | 分类于 flutter | 评论数: | 阅读次数:
本文字数: 17k | 阅读时长 ≈ 16 分钟

前言

Mustache 是一个 logic-less (轻逻辑)模板解析引擎,可以应用在 js、PHP、Python、Perl 等多种编程语言中。这里主要是看dart中的应用。

模板语法很简单 看这里

1
2
3
4
5
6
7
{{keyName}} 
{{#keyName}} {{/keyName}}
{{^keyName}} {{/keyName}}
{{.}}
{{>partials}}
{{{keyName}}}
{{!comments}}

阅读全文 »

jvm调优

发表于 2019-05-23 | 分类于 java | 评论数: | 阅读次数:
本文字数: 1.4k | 阅读时长 ≈ 1 分钟

jps

输出JVM中运行的进程状态信息

1
2
3
4
-q 不输出类名、Jar名和传入main方法的参数
-m 输出传入main方法的参数
-l 输出main类或Jar的全限名
-v 输出传入JVM的参数

jps -mlv
找到java应用的pid

jstack

根据java应用pid ,查看进程中线程堆栈信息

jstack pid

top

找出该进程内最耗费CPU的线程

top -Hp pid

转为十六进制
printf "%x\n" 线程id

输出进程的堆栈信息,然后根据线程ID的十六进制值grep
jstack pid | grep 十六进制线程id

jmap

查看堆内存使用状况
jmap -heap pid

进程内存使用情况dump到文件中 结合MAT工具分析
jmap -dump:format=b,file=dumpFileName pid

jhat

jhat -port 9998 /tmp/dump.dat
localhost:9998 查看内存对象情况 (不如MAT直观)

vmstatu

发表于 2019-05-22 | 评论数: | 阅读次数:
本文字数: 7.4k | 阅读时长 ≈ 7 分钟

vmstatu

vmstat命令是最常见的Linux/Unix监控工具,可以展现给定时间间隔的服务器的状态值,包括服务器的CPU使用率,内存使用,虚拟内存交换情况,IO读写情况。
相比top,可以看到整个机器的CPU,内存,IO的使用情况,而不是单单看到各个进程的CPU使用率和内存使用率(使用场景不一样)。

1
2
3
4
$vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 215220 0 771404 0 0 2 15 0 1 0 0 100 0 0

一般vmstat工具的使用是通过两个数字参数来完成的,第一个参数是采样的时间间隔数,单位是秒,第二个参数是采样的次数

1
2
3
4
5
$vmstat 2 2
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 202924 0 785780 0 0 2 15 0 1 0 0 100 0 0
0 0 0 203032 0 785812 0 0 0 155 748 1382 0 0 100 0 0

第二个参数如果没有,就会一直采集(ctrl+c 结束)

1
2
3
4
5
6
7
8
$vmstat 2
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 194712 0 794728 0 0 2 15 0 1 0 0 100 0 0
0 0 0 194696 0 794768 0 0 0 50 782 1368 0 0 100 0 0
0 0 0 193828 0 794776 0 0 0 108 752 1156 0 0 100 0 0
0 0 0 193952 0 794804 0 0 0 4 601 997 0 0 100 0 0
^C

字段

procs

  • r 等待运行的进程数
  • b 处在非中断睡眠状态的进程数

memory (KB)

  • swpd 虚拟内存使用大小

注意:如果swpd的值不为0,但是SI,SO的值长期为0,这种情况不会影响系统性能。

  • free 空闲的内存
  • buff 用作缓冲的内存大小
  • cache 用作缓存的内存大小

注意:如果cache的值大的时候,说明cache处的文件数多,如果频繁访问到的文件都能被cache处,那么磁盘的读IO bi会非常小。

swap

  • si 从交换区写到内存的大小
  • so 每秒写入交换区的内存大小

内存够用的时候,这2个值都是0,如果这2个值长期大于0时,系统性能会受到影响,磁盘IO和CPU资源都会被消耗。有些朋友看到空闲内存(free)很少的或接近于0时,就认为内存不够用了,不能光看这一点,还要结合si和so,如果free很少,但是si和so也很少(大多时候是0),那么不用担心,系统性能这时不会受到影响的。

io

  • bi 每秒读取的块数
  • bo 每秒写入的块数

注意:随机磁盘读写的时候,这2个值越大(如超出1024k),能看到CPU在IO等待的值也会越大。

system

  • in 每秒中断数,包括时钟中断。
  • cs 每秒上下文切换数。

注意:上面2个值越大,会看到由内核消耗的CPU时间会越大。

cpu

  • us 用户进程执行时间(user time)

注意: us的值比较高时,说明用户进程消耗的CPU时间多,但是如果长期超50%的使用,那么我们就该考虑优化程序算法或者进行加速。

  • sy 系统进程执行时间(system time)

注意:sy的值高时,说明系统内核消耗的CPU资源多,这并不是良性表现,我们应该检查原因。

  • id 空闲时间(包括IO等待时间),中央处理器的空闲时间 。以百分比表示。

  • wa 等待IO时间百分比

注意:wa的值高时,说明IO等待比较严重,这可能由于磁盘大量作随机访问造成,也有可能磁盘出现瓶颈(块操作)。

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
Procs
r: The number of processes waiting for run time.
b: The number of processes in uninterruptible sleep.
Memory
swpd: the amount of virtual memory used.
free: the amount of idle memory.
buff: the amount of memory used as buffers.
cache: the amount of memory used as cache.
inact: the amount of inactive memory. (-a option)
active: the amount of active memory. (-a option)
Swap
si: Amount of memory swapped in from disk (/s).
so: Amount of memory swapped to disk (/s).
IO
bi: Blocks received from a block device (blocks/s).
bo: Blocks sent to a block device (blocks/s).
System
in: The number of interrupts per second, including the clock.
cs: The number of context switches per second.
CPU
These are percentages of total CPU time.
us: Time spent running non-kernel code. (user time, including nice time)
sy: Time spent running kernel code. (system time)
id: Time spent idle. Prior to Linux 2.5.41, this includes IO-wait time.
wa: Time spent waiting for IO. Prior to Linux 2.5.41, included in idle.
st: Time stolen from a virtual machine. Prior to Linux 2.6.11, unknown.
1234
BLACK_OX

BLACK_OX

Developer...
34 日志
10 分类
22 标签
RSS
GitHub E-Mail
© 2023 BLACK_OX | 497k | 7:32
由 Hexo 强力驱动
|
主题 – NexT.Gemini
|