离线地图
expo-gaode-map 支持离线地图功能,允许用户下载城市地图数据以便在无网络环境下使用。
重要提示
离线地图功能需要使用 3D 地图 SDK,2D SDK 不支持离线地图。
Android 权限要求
离线地图功能需要以下权限(Config Plugin 会自动添加):
xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />如果使用 Config Plugin,这些权限会自动配置。如果手动配置,请确保在 AndroidManifest.xml 中添加这些权限。
功能特性
- ✅ 城市地图下载 - 支持下载全国各城市的离线地图数据
- ✅ 下载管理 - 支持暂停、恢复、取消下载
- ✅ 实时进度 - 实时监听下载进度和状态
- ✅ 存储管理 - 查看存储使用情况,支持删除和清空
- ✅ 自动更新 - 检查并更新离线地图数据
- ✅ 网络控制 - 可限制仅 WiFi 下载
快速开始
1. 获取可用城市列表
tsx
import { ExpoGaodeMapOfflineModule } from 'expo-gaode-map';
const cities = await ExpoGaodeMapOfflineModule.getAvailableCities();
cities.forEach(city => {
console.log(`${city.cityName}: ${city.size / 1024 / 1024} MB`);
});2. 开始下载
tsx
// 开始下载北京地图(仅 WiFi)
await ExpoGaodeMapOfflineModule.startDownload({
cityCode: '110000',
allowCellular: false, // 仅 WiFi 下载
});3. 监听下载进度
tsx
import { useEffect } from 'react';
useEffect(() => {
// 监听下载进度
const progressSub = ExpoGaodeMapOfflineModule.addDownloadProgressListener((event) => {
console.log(`${event.cityName}: ${event.progress}%`);
});
// 监听下载完成
const completeSub = ExpoGaodeMapOfflineModule.addDownloadCompleteListener((event) => {
console.log(`${event.cityName} 下载完成!`);
});
// 监听下载错误
const errorSub = ExpoGaodeMapOfflineModule.addDownloadErrorListener((event) => {
console.error(`${event.cityName} 下载失败: ${event.error}`);
});
// 清理监听器
return () => {
progressSub.remove();
completeSub.remove();
errorSub.remove();
};
}, []);完整示例
这是一个包含下载管理、进度显示、状态更新的完整示例:
tsx
import React, { useState, useEffect } from 'react';
import { View, Text, FlatList, TouchableOpacity, Alert } from 'react-native';
import { ExpoGaodeMapOfflineModule, type OfflineMapInfo } from 'expo-gaode-map';
export default function OfflineMapScreen() {
const [cities, setCities] = useState<OfflineMapInfo[]>([]);
const [downloadedCities, setDownloadedCities] = useState<string[]>([]);
const [progress, setProgress] = useState<Record<string, number>>({});
const [downloading, setDownloading] = useState<string | null>(null);
// 加载城市列表
useEffect(() => {
loadCities();
}, []);
const loadCities = async () => {
const [available, downloaded] = await Promise.all([
ExpoGaodeMapOfflineModule.getAvailableCities(),
ExpoGaodeMapOfflineModule.getDownloadedMaps(),
]);
setCities(available.slice(0, 20)); // 显示前20个城市
setDownloadedCities(downloaded.map(c => c.cityCode));
};
// 监听下载事件
useEffect(() => {
const progressSub = ExpoGaodeMapOfflineModule.addDownloadProgressListener((event) => {
setProgress(prev => ({
...prev,
[event.cityCode]: event.progress,
}));
});
const completeSub = ExpoGaodeMapOfflineModule.addDownloadCompleteListener((event) => {
Alert.alert('下载完成', `${event.cityName} 离线地图已下载完成`);
setDownloading(null);
loadCities();
});
const errorSub = ExpoGaodeMapOfflineModule.addDownloadErrorListener((event) => {
Alert.alert('下载失败', `${event.cityName}: ${event.error}`);
setDownloading(null);
});
return () => {
progressSub.remove();
completeSub.remove();
errorSub.remove();
};
}, []);
// 开始下载
const handleDownload = async (city: OfflineMapInfo) => {
setDownloading(city.cityCode);
try {
await ExpoGaodeMapOfflineModule.startDownload({
cityCode: city.cityCode,
allowCellular: false, // 仅 WiFi
});
} catch (error) {
console.error('下载失败:', error);
Alert.alert('错误', '开始下载失败');
setDownloading(null);
}
};
// 暂停下载
const handlePause = async (cityCode: string) => {
try {
await ExpoGaodeMapOfflineModule.pauseDownload(cityCode);
setDownloading(null);
} catch (error) {
console.error('暂停失败:', error);
}
};
// 删除地图
const handleDelete = async (city: OfflineMapInfo) => {
Alert.alert(
'确认删除',
`确定要删除 ${city.cityName} 的离线地图吗?`,
[
{ text: '取消', style: 'cancel' },
{
text: '删除',
style: 'destructive',
onPress: async () => {
await ExpoGaodeMapOfflineModule.deleteMap(city.cityCode);
loadCities();
},
},
]
);
};
// 渲染城市项
const renderCity = ({ item }: { item: OfflineMapInfo }) => {
const isDownloaded = downloadedCities.includes(item.cityCode);
const isDownloading = downloading === item.cityCode;
const currentProgress = progress[item.cityCode] || 0;
return (
<View style={{ padding: 16, borderBottomWidth: 1, borderBottomColor: '#eee' }}>
<Text style={{ fontSize: 18, fontWeight: 'bold' }}>{item.cityName}</Text>
<Text style={{ color: '#666', marginTop: 4 }}>
{(item.size / 1024 / 1024).toFixed(1)} MB
</Text>
{isDownloading && (
<View style={{ marginTop: 8 }}>
<View style={{ height: 8, backgroundColor: '#eee', borderRadius: 4 }}>
<View
style={{
height: '100%',
width: `${currentProgress}%`,
backgroundColor: '#4CAF50',
borderRadius: 4,
}}
/>
</View>
<Text style={{ marginTop: 4, color: '#666' }}>{currentProgress}%</Text>
</View>
)}
<View style={{ flexDirection: 'row', marginTop: 8 }}>
{!isDownloaded && !isDownloading && (
<TouchableOpacity
onPress={() => handleDownload(item)}
style={{
backgroundColor: '#2196F3',
padding: 8,
borderRadius: 4,
}}
>
<Text style={{ color: 'white' }}>下载</Text>
</TouchableOpacity>
)}
{isDownloading && (
<TouchableOpacity
onPress={() => handlePause(item.cityCode)}
style={{
backgroundColor: '#FF9800',
padding: 8,
borderRadius: 4,
}}
>
<Text style={{ color: 'white' }}>暂停</Text>
</TouchableOpacity>
)}
{isDownloaded && (
<TouchableOpacity
onPress={() => handleDelete(item)}
style={{
backgroundColor: '#F44336',
padding: 8,
borderRadius: 4,
}}
>
<Text style={{ color: 'white' }}>删除</Text>
</TouchableOpacity>
)}
</View>
</View>
);
};
return (
<View style={{ flex: 1 }}>
<View style={{ padding: 16, backgroundColor: '#f5f5f5' }}>
<Text style={{ fontSize: 20, fontWeight: 'bold' }}>离线地图管理</Text>
<Text style={{ marginTop: 4, color: '#666' }}>
已下载: {downloadedCities.length} 个城市
</Text>
</View>
<FlatList
data={cities}
keyExtractor={item => item.cityCode}
renderItem={renderCity}
/>
</View>
);
}API 参考
ExpoGaodeMapOfflineModule
方法
getAvailableCities()
获取所有可下载的城市列表。
tsx
const cities = await ExpoGaodeMapOfflineModule.getAvailableCities();返回值: Promise<OfflineMapInfo[]>
startDownload(options)
开始下载离线地图。
tsx
await ExpoGaodeMapOfflineModule.startDownload({
cityCode: '110000',
allowCellular: false,
});参数:
cityCode: 城市代码allowCellular: 是否允许移动网络下载(默认false)
pauseDownload(cityCode)
暂停下载。
tsx
await ExpoGaodeMapOfflineModule.pauseDownload('110000');resumeDownload(cityCode)
恢复下载。
tsx
await ExpoGaodeMapOfflineModule.resumeDownload('110000');deleteMap(cityCode)
删除离线地图。
tsx
await ExpoGaodeMapOfflineModule.deleteMap('110000');clearAll()
清除所有离线地图。
tsx
await ExpoGaodeMapOfflineModule.clearAll();getDownloadedMaps()
获取已下载的地图列表。
tsx
const downloaded = await ExpoGaodeMapOfflineModule.getDownloadedMaps();返回值: Promise<OfflineMapInfo[]>
getStorageInfo()
获取存储信息。
tsx
const storage = await ExpoGaodeMapOfflineModule.getStorageInfo();
console.log('已用空间:', storage.usedSpace / 1024 / 1024, 'MB');
console.log('可用空间:', storage.availableSpace / 1024 / 1024, 'MB');checkUpdate(cityCode)
检查地图是否有更新。
tsx
const hasUpdate = await ExpoGaodeMapOfflineModule.checkUpdate('110000');
if (hasUpdate) {
await ExpoGaodeMapOfflineModule.updateMap('110000');
}updateMap(cityCode)
更新离线地图。
tsx
await ExpoGaodeMapOfflineModule.updateMap('110000');事件监听
addDownloadProgressListener(callback)
监听下载进度。
tsx
const subscription = ExpoGaodeMapOfflineModule.addDownloadProgressListener((event) => {
console.log(`${event.cityName}: ${event.progress}%`);
});
// 清理监听器
subscription.remove();事件对象:
tsx
{
cityCode: string;
cityName: string;
progress: number; // 0-100
}addDownloadCompleteListener(callback)
监听下载完成。
tsx
const subscription = ExpoGaodeMapOfflineModule.addDownloadCompleteListener((event) => {
console.log(`${event.cityName} 下载完成`);
});addDownloadErrorListener(callback)
监听下载错误。
tsx
const subscription = ExpoGaodeMapOfflineModule.addDownloadErrorListener((event) => {
console.error(`${event.cityName} 错误: ${event.error}`);
});addDownloadPausedListener(callback)
监听下载暂停。
tsx
const subscription = ExpoGaodeMapOfflineModule.addDownloadPausedListener((event) => {
console.log(`${event.cityName} 已暂停`);
});addDownloadCancelledListener(callback)
监听下载取消。
tsx
const subscription = ExpoGaodeMapOfflineModule.addDownloadCancelledListener((event) => {
console.log(`${event.cityName} 已取消`);
});类型定义
tsx
interface OfflineMapInfo {
cityCode: string; // 城市代码
cityName: string; // 城市名称
size: number; // 地图大小(字节)
status: OfflineMapStatus;
}
type OfflineMapStatus =
| 'NOT_DOWNLOADED' // 未下载
| 'DOWNLOADING' // 下载中
| 'PAUSED' // 已暂停
| 'DOWNLOADED' // 已下载
| 'ERROR'; // 错误
interface StorageInfo {
usedSpace: number; // 已用空间(字节)
availableSpace: number; // 可用空间(字节)
totalSpace: number; // 总空间(字节)
}最佳实践
1. 检查存储空间
下载前检查可用空间:
tsx
const handleDownload = async (city: OfflineMapInfo) => {
const storage = await ExpoGaodeMapOfflineModule.getStorageInfo();
if (storage.availableSpace < city.size) {
Alert.alert('存储空间不足', '请释放存储空间后重试');
return;
}
await ExpoGaodeMapOfflineModule.startDownload({
cityCode: city.cityCode,
allowCellular: false,
});
};2. 错误处理
所有异步操作都应包含错误处理:
tsx
try {
await ExpoGaodeMapOfflineModule.startDownload({ cityCode });
} catch (error) {
console.error('下载失败:', error);
Alert.alert('错误', '开始下载失败,请重试');
}3. 监听器清理
使用 useEffect 的清理函数:
tsx
useEffect(() => {
const progressSub = ExpoGaodeMapOfflineModule.addDownloadProgressListener(handler);
return () => {
progressSub.remove(); // 清理监听器
};
}, []);4. 网络控制
建议默认仅 WiFi 下载,保护用户流量:
tsx
await ExpoGaodeMapOfflineModule.startDownload({
cityCode: city.cityCode,
allowCellular: false, // ✅ 推荐
});5. 自动更新检查
定期检查地图更新:
tsx
const checkUpdates = async () => {
const downloaded = await ExpoGaodeMapOfflineModule.getDownloadedMaps();
for (const city of downloaded) {
const hasUpdate = await ExpoGaodeMapOfflineModule.checkUpdate(city.cityCode);
if (hasUpdate) {
// 提示用户更新
}
}
};注意事项
- SDK 要求: 离线地图功能仅支持 3D 地图 SDK
- Android 权限: 需要网络权限和存储权限(Config Plugin 自动添加):
INTERNET- 下载地图数据ACCESS_NETWORK_STATE- 检查网络状态WRITE_EXTERNAL_STORAGE- 存储离线地图
- 存储空间: 每个城市地图通常需要 50-150 MB
- 下载时间: 取决于网络速度和地图大小
- 自动加载: 下载完成后 SDK 会自动使用离线数据
- 数据更新: 建议定期检查并更新离线地图
常见问题
离线地图在哪里存储?
离线地图数据存储在应用的私有目录中,卸载应用时会被删除。
如何知道地图正在使用离线数据?
SDK 会自动检测并使用离线数据,无需额外配置。在无网络环境下,地图会自动切换到离线模式。
可以同时下载多个城市吗?
技术上可以,但建议一次只下载一个城市,以便更好地管理下载进度和错误处理。
下载中断后如何恢复?
使用 resumeDownload() 方法恢复下载,SDK 会从中断的地方继续下载。
tsx
await ExpoGaodeMapOfflineModule.resumeDownload(cityCode);如何删除所有离线地图?
使用 clearAll() 方法:
tsx
await ExpoGaodeMapOfflineModule.clearAll();