Android Permissions Troubleshooting Guide
This guide focuses on location permission-related issues specific to the Android platform.
Common Issues
Issue: Permission Appears Not Granted Immediately After Request
Symptoms:
⚠️ expo-gaode-map: Foreground location permission not granted
{
"coarseLocation": false,
"fineLocation": false,
"granted": false,
"isPermanentlyDenied": false,
"shouldShowRationale": false
}User clearly taps "Allow" button, but the console still shows permission not granted.
Root Cause:
On Android, there's a brief asynchronous delay in permission state updates after the user taps "Allow" on the permission dialog. If you check the permission status immediately after requesting it, you might get the old state (not granted).
Solution:
This issue has been fixed in v1.x.x of this library. The fix implements a polling mechanism:
- Call the system permission request API
- Wait 500ms before starting to poll the permission status
- Check every 100ms, up to 5 seconds (50 attempts)
- Return the final result once permission is granted or timeout is reached
Core implementation (Kotlin):
AsyncFunction("requestLocationPermission") { promise: expo.modules.kotlin.Promise ->
val activity = appContext.currentActivity
if (activity == null) {
promise.reject("NO_ACTIVITY", "Activity not available", null)
return@AsyncFunction
}
// Request permission
PermissionHelper.requestForegroundLocationPermission(activity, 1001)
// Use WeakReference to avoid memory leaks
val contextRef = java.lang.ref.WeakReference(appContext.reactContext)
val handler = android.os.Handler(android.os.Looper.getMainLooper())
var attempts = 0
val maxAttempts = 50 // 5 second timeout
val checkPermission = object : Runnable {
override fun run() {
val context = contextRef.get()
if (context == null) {
promise.reject("CONTEXT_LOST", "Context was garbage collected", null)
return
}
val status = PermissionHelper.checkForegroundLocationPermission(context)
// Return result if granted or max attempts reached
if (status.granted || attempts >= maxAttempts) {
handler.removeCallbacks(this)
promise.resolve(mapOf(
"granted" to status.granted,
"fineLocation" to status.fineLocation,
"coarseLocation" to status.coarseLocation,
"shouldShowRationale" to status.shouldShowRationale,
"isPermanentlyDenied" to status.isPermanentlyDenied
))
} else {
attempts++
handler.postDelayed(this, 100)
}
}
}
// Wait 500ms before starting to poll, giving time for dialog to appear
handler.postDelayed(checkPermission, 500)
}Best Practices
1. Correct Permission Request Flow
import { ExpoGaodeMapModule } from 'expo-gaode-map';
async function requestLocationPermission() {
try {
// 1. Check current permission status
const status = await ExpoGaodeMapModule.checkLocationPermission();
if (status.granted) {
console.log('Permission already granted');
return true;
}
// 2. Request permission if not granted
const result = await ExpoGaodeMapModule.requestLocationPermission();
if (result.granted) {
console.log('User granted permission');
return true;
} else {
console.log('User denied permission');
return false;
}
} catch (error) {
console.error('Permission request failed:', error);
return false;
}
}2. Handle Permanently Denied Permissions
async function handlePermissionDenied() {
const status = await ExpoGaodeMapModule.checkLocationPermission();
if (status.isPermanentlyDenied) {
Alert.alert(
'Location Permission Required',
'Please manually enable location permission in Settings to use map features',
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Open Settings',
onPress: () => ExpoGaodeMapModule.openAppSettings()
}
]
);
}
}3. Check Permissions on App Startup
useEffect(() => {
const init = async () => {
try {
// Check location permission
const status = await ExpoGaodeMapModule.checkLocationPermission();
if (!status.granted) {
// Request permission
const result = await ExpoGaodeMapModule.requestLocationPermission();
if (!result.granted) {
// Permission not granted, use default location
setInitialPosition({
target: { latitude: 39.9, longitude: 116.4 },
zoom: 16
});
return;
}
}
// Configure location options
ExpoGaodeMapModule.setLocatingWithReGeocode(true);
ExpoGaodeMapModule.setInterval(5000);
// Get current location
const location = await ExpoGaodeMapModule.getCurrentLocation();
setLocation(location);
setInitialPosition({
target: { latitude: location.latitude, longitude: location.longitude },
zoom: 16
});
} catch (error) {
console.error('Initialization failed:', error);
}
};
init();
}, []);Android 14+ Special Notes
Android 14 introduced more granular location permission controls:
- Precise Location (FINE_LOCATION): Get precise GPS location
- Approximate Location (COARSE_LOCATION): Get approximate network location
- One-time: Allow only for this session
This library automatically handles these permissions without additional configuration.
Common Error Codes
| Error Code | Description | Solution |
|---|---|---|
NO_ACTIVITY | Activity unavailable | Ensure called within React Native lifecycle |
CONTEXT_LOST | Context garbage collected | Usually a memory issue, check app memory usage |
Debugging Tips
1. Enable Verbose Logging
Filter by ExpoGaodeMap tag in Android Logcat for detailed logs:
adb logcat | grep ExpoGaodeMap2. Manually Reset Permissions
To reset permissions during testing:
adb shell pm reset-permissionsOr uninstall the app and reinstall from Settings.
3. Check Permission Status
Use this code to print detailed permission information:
const status = await ExpoGaodeMapModule.checkLocationPermission();
console.log('Permission details:', {
granted: status.granted,
fineLocation: status.fineLocation,
coarseLocation: status.coarseLocation,
isPermanentlyDenied: status.isPermanentlyDenied,
shouldShowRationale: status.shouldShowRationale
});Version History
- v1.x.x: Fixed issue where permission appears not granted immediately after request
- Implemented polling mechanism
- Delayed initial check to 500ms
- Increased maximum polling time to 5 seconds