bookworm-smart-assistant/skills/mobile-expert/SKILL.md

10 KiB
Raw Blame History

name description allowed-tools maturity last-reviewed
mobile-expert 移动端开发专家。当用户需要 React Native、Flutter、Expo 跨平台开发, iOS Swift/SwiftUI、Android Kotlin/Jetpack Compose 原生开发, App 性能优化、应用上架 App Store/Google Play Android 设备控制、ADB 操作、设备截图、UI 自动化测试、 应用安装/卸载/启动、模拟器操作、手势模拟、屏幕元素检测, 或说 "移动端"、"App开发"、"跨平台"、"ADB"、"设备截图" 时使用此技能。 Read, Glob, Grep, Edit, Write, Bash, mcp__mobile stable 2026-03-01

移动端开发专家 (Mobile Developer Expert)

Output Style: 本技能使用内联输出规范

资深移动端开发工程师,精通 React Native、Flutter 和原生开发。

触发关键词

  • 跨平台: React Native, Flutter, Expo, 跨平台, Ionic
  • 原生开发: iOS, Android, Swift, Kotlin, 原生开发
  • 应用开发: App开发, 移动端, 手机应用, APP
  • 发布相关: 应用上架, App Store, Google Play, 签名
  • 性能相关: 移动性能, 启动优化, 内存优化
  • 设备控制: ADB, adb devices, 设备截图, 模拟器, 真机调试
  • MCP 操作: mobile MCP, 设备列表, 应用安装, UI自动化, 手势模拟, 屏幕元素

路由消歧: Android + ADB/设备截图/模拟器/UI自动化 → mobile-expert; Playwright/Selenium + 测试 → tester-expert; 浏览器自动化 → browser-automation-expert

技术栈

跨平台框架 (2024-2025)

  • React Native 0.74+: 新架构 (Fabric/TurboModules)
  • Expo 51: 托管工作流和开发工具
  • Flutter 3.24+: Dart 3.5+, Impeller 渲染引擎

原生开发

  • iOS: Swift 5.9+, SwiftUI, UIKit
  • Android: Kotlin 1.9+, Jetpack Compose

状态管理

  • React Native: Zustand, Jotai, Redux Toolkit
  • Flutter: Riverpod, Bloc, Provider

React Native 项目结构

myapp/
├── src/
│   ├── components/          # 通用组件
│   │   ├── ui/
│   │   └── layout/
│   ├── screens/             # 页面
│   ├── navigation/          # 导航配置
│   ├── hooks/               # 自定义 Hooks
│   ├── services/            # API 服务
│   ├── store/               # 状态管理
│   ├── utils/               # 工具函数
│   └── types/               # TypeScript 类型
├── android/
├── ios/
├── App.tsx
└── package.json

React Native 组件示例

// src/components/ui/Button.tsx
import React from 'react';
import { TouchableOpacity, Text, ActivityIndicator } from 'react-native';

interface ButtonProps {
  title: string;
  onPress: () => void;
  variant?: 'primary' | 'secondary' | 'outline';
  loading?: boolean;
  disabled?: boolean;
}

export const Button: React.FC<ButtonProps> = ({
  title,
  onPress,
  variant = 'primary',
  loading = false,
  disabled = false,
}) => {
  const getStyle = () => {
    const base = { borderRadius: 8, padding: 12, alignItems: 'center' };
    const variants = {
      primary: { backgroundColor: '#007AFF' },
      secondary: { backgroundColor: '#5856D6' },
      outline: { borderWidth: 2, borderColor: '#007AFF' },
    };
    return { ...base, ...variants[variant] };
  };

  return (
    <TouchableOpacity
      style={getStyle()}
      onPress={onPress}
      disabled={disabled || loading}
      activeOpacity={0.7}
    >
      {loading ? (
        <ActivityIndicator color="#FFF" />
      ) : (
        <Text style={{ color: variant === 'outline' ? '#007AFF' : '#FFF' }}>
          {title}
        </Text>
      )}
    </TouchableOpacity>
  );
};

Navigation 配置

// src/navigation/RootNavigator.tsx
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

const Stack = createNativeStackNavigator();
const Tab = createBottomTabNavigator();

function MainTabs() {
  return (
    <Tab.Navigator screenOptions={{ headerShown: false }}>
      <Tab.Screen name="Home" component={HomeScreen} />
      <Tab.Screen name="Profile" component={ProfileScreen} />
    </Tab.Navigator>
  );
}

export function RootNavigator() {
  const { user } = useAuth();
  
  return (
    <NavigationContainer>
      <Stack.Navigator screenOptions={{ headerShown: false }}>
        {!user ? (
          <Stack.Screen name="Login" component={LoginScreen} />
        ) : (
          <Stack.Screen name="Main" component={MainTabs} />
        )}
      </Stack.Navigator>
    </NavigationContainer>
  );
}

Flutter 项目结构

myapp/
├── lib/
│   ├── main.dart
│   ├── core/
│   │   ├── theme/
│   │   └── network/
│   ├── features/
│   │   ├── auth/
│   │   ├── home/
│   │   └── profile/
│   └── shared/
│       ├── widgets/
│       └── services/
├── android/
├── ios/
└── pubspec.yaml

Flutter 组件示例

// lib/shared/widgets/custom_button.dart
import 'package:flutter/material.dart';

enum ButtonVariant { primary, secondary, outline }

class CustomButton extends StatelessWidget {
  final String text;
  final VoidCallback? onPressed;
  final ButtonVariant variant;
  final bool isLoading;

  const CustomButton({
    Key? key,
    required this.text,
    this.onPressed,
    this.variant = ButtonVariant.primary,
    this.isLoading = false,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: isLoading ? null : onPressed,
      style: ElevatedButton.styleFrom(
        backgroundColor: _getBackgroundColor(),
        padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
      ),
      child: isLoading
          ? const SizedBox(
              width: 20,
              height: 20,
              child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white),
            )
          : Text(text, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
    );
  }

  Color _getBackgroundColor() {
    switch (variant) {
      case ButtonVariant.primary:
        return const Color(0xFF007AFF);
      case ButtonVariant.secondary:
        return const Color(0xFF5856D6);
      case ButtonVariant.outline:
        return Colors.transparent;
    }
  }
}

性能优化

React Native

// 1. 使用 FlatList 而不是 ScrollView + map
<FlatList
  data={data}
  renderItem={renderItem}
  keyExtractor={(item) => item.id}
  removeClippedSubviews={true}
  maxToRenderPerBatch={10}
  windowSize={10}
  getItemLayout={(data, index) => ({
    length: ITEM_HEIGHT,
    offset: ITEM_HEIGHT * index,
    index,
  })}
/>

// 2. 使用 useCallback 和 useMemo
const handlePress = useCallback(() => {
  doSomething(id);
}, [id]);

const sortedData = useMemo(() => 
  data.sort((a, b) => a.name.localeCompare(b.name)),
  [data]
);

Flutter

// 1. 使用 const 构造函数
const Text('Hello');

// 2. 使用 ListView.builder
ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) => ListTile(title: Text(items[index])),
);

// 3. 使用 RepaintBoundary 隔离重绘
RepaintBoundary(child: ExpensiveWidget());

应用上架

iOS App Store

# 构建归档
xcodebuild -workspace MyApp.xcworkspace \
  -scheme MyApp -configuration Release \
  -archivePath MyApp.xcarchive archive

# 导出 IPA
xcodebuild -exportArchive \
  -archivePath MyApp.xcarchive \
  -exportPath ./build \
  -exportOptionsPlist ExportOptions.plist

Android Google Play

# 生成签名 AAB (推荐)
cd android
./gradlew bundleRelease

# 输出: android/app/build/outputs/bundle/release/app-release.aab

Android MCP 工具 (mcp__mobile)

通过 @mobilenext/mobile-mcp 提供 Android 真机/模拟器设备控制能力。

工具速查 (5 类 20 个)

# 设备管理
mobile_list_available_devices:  列出已连接设备 (ADB)
mobile_get_screen_size:         获取屏幕分辨率
mobile_get_orientation:         获取屏幕方向
mobile_set_orientation:         设置屏幕方向 (portrait/landscape)

# 应用管理
mobile_list_apps:               列出已安装应用
mobile_launch_app:              启动应用 (包名)
mobile_terminate_app:           终止应用
mobile_install_app:             安装 APK
mobile_uninstall_app:           卸载应用

# 屏幕操作
mobile_take_screenshot:         设备截图 (返回 base64)
mobile_save_screenshot:         保存截图到文件
mobile_list_elements_on_screen: 获取屏幕 UI 元素树

# 触摸手势
mobile_click_on_screen_at_coordinates:        点击坐标
mobile_double_tap_on_screen:                  双击
mobile_long_press_on_screen_at_coordinates:   长按坐标
mobile_swipe_on_screen:                       滑动 (方向/坐标)

# 输入
mobile_type_keys:       输入文本
mobile_press_button:    按下物理/虚拟按键 (home/back/enter)
mobile_open_url:        在设备上打开 URL

典型工作流

工作流 1: 应用安装与验证

mobile_list_available_devices → mobile_install_app(apkPath)
→ mobile_launch_app(bundleId) → mobile_take_screenshot → 验证 UI

工作流 2: UI 自动化操作

mobile_launch_app → mobile_list_elements_on_screen → 分析元素坐标
→ mobile_click_on_screen_at_coordinates → mobile_type_keys → mobile_take_screenshot

工作流 3: 多设备对比测试

mobile_list_available_devices → 遍历设备
→ mobile_launch_app → mobile_take_screenshot → 对比截图差异

框架选择决策树 (含 MCP)

需要移动端操作?
├── 需要控制真机/模拟器设备?
│   └── → Android MCP (mcp__mobile)
├── 需要开发跨平台 App
│   └── → React Native / Flutter (代码编写)
├── 需要移动 Web 页面自动化?
│   └── → Playwright + 设备模拟 (mcp__playwright)
└── 不确定?
    └── → 先确认是设备操作还是代码开发

输出规范

  • 使用中文回复和注释
  • 代码完整可运行
  • 说明平台差异
  • 提供性能优化建议
  • 注明官方文档链接

禁止事项

  • 不要忽略内存泄漏
  • 不要在主线程做耗时操作
  • 不要硬编码配置
  • 不要忽略权限处理
  • 不要使用已废弃的 API