Skip to content

Commit 6847db2

Browse files
committed
feat: 添加内存泄漏检测和诊断工具
- 实现 VipsPointerManager 以跟踪 VipsImage 指针并检测内存泄漏 - 添加内存诊断页面,实时监控活跃指针 - 包含泄漏检测、统计信息和紧急清理功能 - 增强图像比较 UI,提供并排视图和缩放功能 - 启用可选的堆栈跟踪调试内存问题 - 添加紧急强制释放功能用于内存清理 解决 VipsImage 包装器和示例应用中的潜在内存泄漏问题。 Signed-off-by: Caijinglong <[email protected]>
1 parent a6601fb commit 6847db2

File tree

9 files changed

+787
-18
lines changed

9 files changed

+787
-18
lines changed

libvips_ffi/example/android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ plugins {
77

88
android {
99
namespace = "top.kikt.libvips_example"
10-
compileSdk = flutter.compileSdkVersion
10+
compileSdk = 35
1111
ndkVersion = flutter.ndkVersion
1212

1313
compileOptions {

libvips_ffi/example/lib/main.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:libvips_ffi/libvips_ffi.dart';
33

44
import 'pages/all_tests_page.dart';
55
import 'pages/benchmark_page.dart';
6+
import 'pages/memory_diagnostics_page.dart';
67

78
void main() {
89
runApp(const MyApp());
@@ -137,6 +138,18 @@ class _HomePageState extends State<HomePage> {
137138
MaterialPageRoute(builder: (_) => const BenchmarkPage()),
138139
),
139140
),
141+
const SizedBox(height: 12),
142+
143+
_buildNavButton(
144+
context,
145+
title: 'Memory Diagnostics',
146+
subtitle: 'Monitor pointer usage and detect leaks',
147+
icon: Icons.memory,
148+
onTap: () => Navigator.push(
149+
context,
150+
MaterialPageRoute(builder: (_) => const MemoryDiagnosticsPage()),
151+
),
152+
),
140153

141154
const Spacer(),
142155

libvips_ffi/example/lib/pages/all_tests_page.dart

Lines changed: 180 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -351,18 +351,144 @@ class _AllTestsPageState extends State<AllTestsPage> {
351351
fontSize: 12,
352352
),
353353
),
354-
if (_processedImageData != null) ...[
354+
if (_processedImageData != null &&
355+
_selectedImagePath != null) ...[
355356
const SizedBox(height: 16),
356-
const Text(
357-
'Processed Image:',
358-
style: TextStyle(fontWeight: FontWeight.bold),
357+
// Side-by-side comparison
358+
Container(
359+
decoration: BoxDecoration(
360+
color: Colors.grey.shade100,
361+
borderRadius: BorderRadius.circular(12),
362+
),
363+
padding: const EdgeInsets.all(12),
364+
child: Column(
365+
children: [
366+
// Labels row
367+
Row(
368+
children: [
369+
Expanded(
370+
child: Container(
371+
padding: const EdgeInsets.symmetric(
372+
vertical: 6),
373+
decoration: BoxDecoration(
374+
color: Colors.blue.shade100,
375+
borderRadius:
376+
BorderRadius.circular(6),
377+
),
378+
child: const Text(
379+
'Original / 原图',
380+
textAlign: TextAlign.center,
381+
style: TextStyle(
382+
fontWeight: FontWeight.bold,
383+
fontSize: 12,
384+
),
385+
),
386+
),
387+
),
388+
const SizedBox(width: 8),
389+
Expanded(
390+
child: Container(
391+
padding: const EdgeInsets.symmetric(
392+
vertical: 6),
393+
decoration: BoxDecoration(
394+
color: Colors.green.shade100,
395+
borderRadius:
396+
BorderRadius.circular(6),
397+
),
398+
child: const Text(
399+
'Processed / 处理后',
400+
textAlign: TextAlign.center,
401+
style: TextStyle(
402+
fontWeight: FontWeight.bold,
403+
fontSize: 12,
404+
),
405+
),
406+
),
407+
),
408+
],
409+
),
410+
const SizedBox(height: 8),
411+
// Images row
412+
Row(
413+
crossAxisAlignment: CrossAxisAlignment.start,
414+
children: [
415+
// Original image
416+
Expanded(
417+
child: GestureDetector(
418+
onTap: () => _showFullImage(
419+
context,
420+
'Original / 原图',
421+
Image.file(
422+
File(_selectedImagePath!),
423+
fit: BoxFit.contain,
424+
),
425+
),
426+
child: Container(
427+
decoration: BoxDecoration(
428+
border: Border.all(
429+
color: Colors.blue.shade300,
430+
width: 2,
431+
),
432+
borderRadius:
433+
BorderRadius.circular(8),
434+
),
435+
child: ClipRRect(
436+
borderRadius:
437+
BorderRadius.circular(6),
438+
child: Image.file(
439+
File(_selectedImagePath!),
440+
fit: BoxFit.contain,
441+
),
442+
),
443+
),
444+
),
445+
),
446+
const SizedBox(width: 8),
447+
// Processed image
448+
Expanded(
449+
child: GestureDetector(
450+
onTap: () => _showFullImage(
451+
context,
452+
'Processed / 处理后',
453+
Image.memory(
454+
_processedImageData!,
455+
fit: BoxFit.contain,
456+
),
457+
),
458+
child: Container(
459+
decoration: BoxDecoration(
460+
border: Border.all(
461+
color: Colors.green.shade300,
462+
width: 2,
463+
),
464+
borderRadius:
465+
BorderRadius.circular(8),
466+
),
467+
child: ClipRRect(
468+
borderRadius:
469+
BorderRadius.circular(6),
470+
child: Image.memory(
471+
_processedImageData!,
472+
fit: BoxFit.contain,
473+
),
474+
),
475+
),
476+
),
477+
),
478+
],
479+
),
480+
],
481+
),
359482
),
360-
const SizedBox(height: 8),
361-
ClipRRect(
362-
borderRadius: BorderRadius.circular(8),
363-
child: Image.memory(
364-
_processedImageData!,
365-
fit: BoxFit.contain,
483+
const SizedBox(height: 12),
484+
// Tap to view full size hint
485+
Center(
486+
child: Text(
487+
'Tap images to view full size / 点击图片查看大图',
488+
style: TextStyle(
489+
fontSize: 11,
490+
color: Colors.grey.shade600,
491+
),
366492
),
367493
),
368494
],
@@ -387,4 +513,48 @@ class _AllTestsPageState extends State<AllTestsPage> {
387513
),
388514
);
389515
}
516+
517+
void _showFullImage(BuildContext context, String title, Widget image) {
518+
showDialog(
519+
context: context,
520+
builder: (context) => Dialog(
521+
backgroundColor: Colors.black,
522+
insetPadding: const EdgeInsets.all(16),
523+
child: Column(
524+
mainAxisSize: MainAxisSize.min,
525+
children: [
526+
// Header
527+
Container(
528+
padding: const EdgeInsets.all(12),
529+
color: Colors.black87,
530+
child: Row(
531+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
532+
children: [
533+
Text(
534+
title,
535+
style: const TextStyle(
536+
color: Colors.white,
537+
fontWeight: FontWeight.bold,
538+
),
539+
),
540+
IconButton(
541+
icon: const Icon(Icons.close, color: Colors.white),
542+
onPressed: () => Navigator.pop(context),
543+
),
544+
],
545+
),
546+
),
547+
// Image with InteractiveViewer for zoom/pan
548+
Flexible(
549+
child: InteractiveViewer(
550+
minScale: 0.5,
551+
maxScale: 4.0,
552+
child: image,
553+
),
554+
),
555+
],
556+
),
557+
),
558+
);
559+
}
390560
}

0 commit comments

Comments
 (0)