AndroidManifest.xml
1. Giới thiệu
AndroidManifest.xml là file cấu hình bắt buộc cho mọi ứng dụng Android. File này cung cấp thông tin thiết yếu về ứng dụng cho hệ thống Android, bao gồm:
- Package name: Định danh duy nhất của ứng dụng
- Components: Các Activity, Service, BroadcastReceiver, ContentProvider
- Permissions: Quyền truy cập tài nguyên hệ thống
- Hardware/Software features: Yêu cầu phần cứng/phần mềm
- API levels: Phiên bản Android được hỗ trợ
Vị trí file:
app/src/main/AndroidManifest.xml
2. Cấu trúc cơ bản
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- Permissions - Quyền truy cập -->
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApp"
tools:targetApi="31">
<activity android:name=".MainActivity" ... />
</application>
</manifest>Giải thích các namespace:
| Namespace | Mục đích |
|---|---|
xmlns:android | Chứa các thuộc tính Android chuẩn |
xmlns:tools | Chứa các thuộc tính chỉ dùng cho build time, không đi vào APK |
3. Permissions (Quyền truy cập)
3.1 Phân loại Permissions
| Loại | Mô tả | Ví dụ |
|---|---|---|
| Normal | Cấp tự động, không cần hỏi user | INTERNET, VIBRATE |
| Dangerous | Phải xin quyền runtime từ Android 6.0+ | CAMERA, LOCATION |
| Signature | Chỉ cấp cho apps có cùng signature | BIND_DEVICE_ADMIN |
3.2 Internet Permission
<uses-permission android:name="android.permission.INTERNET" />Khi nào cần?
- Gọi API REST/GraphQL
- Tải ảnh từ internet (Glide, Coil, Picasso)
- WebView load URL
- Firebase, Analytics, Ads
- Real-time messaging (WebSocket)
Ví dụ thực tế: App tin tức cần load danh sách bài viết từ server.
3.3 Camera Permission
<uses-permission android:name="android.permission.CAMERA" />Khi nào cần?
- Chụp ảnh/quay video trực tiếp trong app
- Quét QR code/barcode
- AR (Augmented Reality) features
- Video call
Ví dụ thực tế: App ngân hàng cần quét mã QR để thanh toán.
⚠️ Lưu ý: Đây là Dangerous permission - cần xin quyền runtime từ Android 6.0+
// Request permission at runtime
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
arrayOf(Manifest.permission.CAMERA), REQUEST_CODE)
}3.4 Location Permission
<!-- Vị trí chính xác (GPS) - độ chính xác cao -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Vị trí xấp xỉ (Network) - tiết kiệm pin -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- Foreground location (Android 10+) -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<!-- Background location (Android 10+) - cần giải thích rõ cho user -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />Sự khác biệt:
| Permission | Độ chính xác | Use case |
|---|---|---|
FINE_LOCATION | ~1-10m (GPS) | Bản đồ điều hướng, giao nhận |
COARSE_LOCATION | ~100m (Network) | Thời tiết, tin tức địa phương |
BACKGROUND_LOCATION | Cả 2 | Fitness tracking, geo-fencing |
Ví dụ thực tế:
- Grab/Gojek: Cần
FINE_LOCATION+BACKGROUND_LOCATIONđể tracking realtime - Weather app: Chỉ cần
COARSE_LOCATIONđể lấy thời tiết thành phố
3.5 Storage Permission
<!-- Đọc file từ bộ nhớ -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<!-- Ghi file vào bộ nhớ -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" />
<!-- Android 13+ (API 33): Media permissions chi tiết -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />Thay đổi qua các phiên bản Android:
| Android | Cách tiếp cận Storage |
|---|---|
| ≤ 9 (API 28) | READ/WRITE_EXTERNAL_STORAGE |
| 10 (API 29) | Scoped Storage + requestLegacyExternalStorage |
| 11-12 (API 30-32) | Scoped Storage bắt buộc |
| 13+ (API 33) | READ_MEDIA_* permissions |
Ví dụ thực tế: App gallery cần đọc ảnh từ thư viện máy.
// Android 13+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requestPermissions(arrayOf(Manifest.permission.READ_MEDIA_IMAGES))
} else {
requestPermissions(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE))
}3.6 Notification Permission (Android 13+)
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />Khi nào cần?
- Push notifications
- Local notifications (nhắc nhở, alarm)
- Download progress notifications
- Music playback controls
Lưu ý: Từ Android 13, notifications cũng cần xin quyền runtime!
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}3.7 Các Permission thường dùng khác
<!-- Rung điện thoại -->
<uses-permission android:name="android.permission.VIBRATE" />
<!-- Giữ màn hình sáng -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- Nhận sự kiện boot completed -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- Bluetooth -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- Android 12+ -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <!-- Android 12+ -->
<!-- Đọc contacts -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<!-- Gọi điện -->
<uses-permission android:name="android.permission.CALL_PHONE" />
<!-- Đọc trạng thái điện thoại -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- Ghi âm -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- Biometric (vân tay, face) -->
<uses-permission android:name="android.permission.USE_BIOMETRIC" />4. Application Tag
<application
android:name=".MyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApp"
android:networkSecurityConfig="@xml/network_security_config"
android:usesCleartextTraffic="false"
android:largeHeap="false">
<!-- Components here -->
</application>Giải thích chi tiết từng thuộc tính:
| Thuộc tính | Mô tả | Ví dụ khi cần |
|---|---|---|
android:name | Class Application tùy chỉnh | Khởi tạo DI (Hilt/Koin), Firebase, Timber |
android:allowBackup | Cho phép backup data lên Google Drive | false cho apps banking/sensitive |
android:icon | Icon hiển thị trên launcher | Mọi app |
android:roundIcon | Icon tròn cho các launcher hỗ trợ | Pixel, Samsung One UI |
android:label | Tên app hiển thị | Mọi app |
android:theme | Theme mặc định cho app | Material Theme |
android:supportsRtl | Hỗ trợ ngôn ngữ Right-to-Left | Arabic, Hebrew |
android:networkSecurityConfig | Cấu hình bảo mật mạng | Debug với localhost |
android:usesCleartextTraffic | Cho phép HTTP không mã hóa | false cho production |
android:largeHeap | Yêu cầu heap memory lớn hơn | Xử lý ảnh/video lớn |
Ví dụ Custom Application class:
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// Khởi tạo các libraries
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
// Firebase
FirebaseApp.initializeApp(this)
// Crash reporting
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(!BuildConfig.DEBUG)
}
}5. Activity
5.1 Main Activity (Launcher)
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.MyApp"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>Giải thích Intent Filter:
action.MAIN: Đây là entry point của appcategory.LAUNCHER: Hiển thị icon trên launcher/app drawer
5.2 Các thuộc tính Activity quan trọng
| Thuộc tính | Giá trị | Mô tả | Khi nào dùng |
|---|---|---|---|
exported | true/false | Cho phép components khác khởi động | true cho launcher, deep link |
launchMode | standard | Tạo instance mới mỗi lần | Default |
singleTop | Dùng lại nếu đang ở top stack | Notification click | |
singleTask | Chỉ 1 instance trong task | Main screen | |
singleInstance | Instance riêng biệt | Incoming call screen | |
screenOrientation | portrait | Chỉ dọc | Form nhập liệu |
landscape | Chỉ ngang | Game, video player | |
fullSensor | Theo sensor | Gallery viewer | |
windowSoftInputMode | adjustResize | Resize layout khi keyboard xuất hiện | Chat input |
adjustPan | Pan lên để hiện input | Login form | |
configChanges | orientation|screenSize | Không recreate khi config change | Video player |
5.3 Activity thường (Internal)
<activity
android:name=".DetailActivity"
android:exported="false"
android:parentActivityName=".MainActivity"
android:theme="@style/Theme.MyApp">
<!-- Enable Up navigation automatically -->
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity" />
</activity>Khi nào dùng exported="false"?
- Activity chỉ được mở từ trong app
- Không cần deep link hay external access
- Bảo mật: ngăn apps khác khởi động activity này
5.4 Deep Link Activity
<activity
android:name=".ProductActivity"
android:exported="true">
<!-- App Links (HTTPS - verified) -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="www.mystore.com"
android:pathPrefix="/product" />
</intent-filter>
<!-- Custom Scheme (không cần verify) -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="myapp"
android:host="product" />
</intent-filter>
</activity>Các loại Deep Link:
| Loại | Scheme | Verify | Ví dụ URL |
|---|---|---|---|
| App Links | https | Cần verify domain | https://mystore.com/product/123 |
| Deep Links | http/https | Không | http://mystore.com/product/123 |
| Custom Scheme | Custom | Không | myapp://product/123 |
Ví dụ thực tế:
- E-commerce:
https://shopee.vn/product/123→ mở app Shopee - Email verification:
myapp://verify?token=abc→ mở màn hình verify
5.5 Xử lý Deep Link trong Activity:
class ProductActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Handle deep link
handleIntent(intent)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
intent?.let { handleIntent(it) }
}
private fun handleIntent(intent: Intent) {
val data: Uri? = intent.data
data?.let {
val productId = it.lastPathSegment // "123" from /product/123
loadProduct(productId)
}
}
}6. Service
6.1 Background Service
<service
android:name=".DownloadService"
android:exported="false"
android:enabled="true" />Khi nào cần?
- Download/upload files trong background
- Sync data với server
- Các tác vụ không cần UI
6.2 Foreground Service
<service
android:name=".MusicPlayerService"
android:foregroundServiceType="mediaPlayback"
android:exported="false" />
<service
android:name=".LocationTrackingService"
android:foregroundServiceType="location"
android:exported="false" />
<service
android:name=".UploadService"
android:foregroundServiceType="dataSync"
android:exported="false" />Foreground Service Types (Android 10+):
| Type | Permission cần thêm | Ví dụ |
|---|---|---|
camera | FOREGROUND_SERVICE_CAMERA | Video recording |
connectedDevice | FOREGROUND_SERVICE_CONNECTED_DEVICE | Bluetooth device |
dataSync | FOREGROUND_SERVICE_DATA_SYNC | Cloud sync |
health | FOREGROUND_SERVICE_HEALTH | Fitness tracking |
location | FOREGROUND_SERVICE_LOCATION | GPS tracking |
mediaPlayback | FOREGROUND_SERVICE_MEDIA_PLAYBACK | Music player |
mediaProjection | FOREGROUND_SERVICE_MEDIA_PROJECTION | Screen recording |
microphone | FOREGROUND_SERVICE_MICROPHONE | Voice recording |
phoneCall | FOREGROUND_SERVICE_PHONE_CALL | VoIP |
remoteMessaging | FOREGROUND_SERVICE_REMOTE_MESSAGING | Push notifications |
shortService | - | Quick tasks < 3 mins |
specialUse | FOREGROUND_SERVICE_SPECIAL_USE | Other cases |
Ví dụ thực tế - Music Player:
class MusicPlayerService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = createNotification()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(NOTIFICATION_ID, notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK)
} else {
startForeground(NOTIFICATION_ID, notification)
}
// Play music...
return START_STICKY
}
}6.3 Bound Service
<service
android:name=".MessengerService"
android:exported="false">
<intent-filter>
<action android:name="com.example.messenger.BIND" />
</intent-filter>
</service>Khi nào dùng?
- Cần giao tiếp hai chiều giữa Activity và Service
- Multiple clients bind to same service
- AIDL cho inter-process communication
7. Broadcast Receiver
7.1 Static Registration (Manifest)
<receiver
android:name=".BootCompletedReceiver"
android:exported="true"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>Khi nào dùng Static?
- Nhận broadcasts khi app không chạy
- System broadcasts:
BOOT_COMPLETED,PACKAGE_ADDED - Cần guaranteed delivery
7.2 Các System Broadcasts phổ biến
<!-- Khi device boot xong -->
<receiver android:name=".BootReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- Khi timezone/time thay đổi -->
<receiver android:name=".TimeChangeReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
<action android:name="android.intent.action.TIME_SET" />
</intent-filter>
</receiver>
<!-- Khi battery low -->
<receiver android:name=".BatteryReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BATTERY_LOW" />
<action android:name="android.intent.action.BATTERY_OKAY" />
</intent-filter>
</receiver>
<!-- Khi network thay đổi (deprecated, dùng NetworkCallback) -->
<receiver android:name=".NetworkReceiver" android:exported="true">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>Lưu ý Android 8.0+: Nhiều implicit broadcasts bị hạn chế. Nên dùng:
- Dynamic registration (trong code)
- JobScheduler / WorkManager
7.3 Dynamic Registration (Trong Code)
class MainActivity : AppCompatActivity() {
private val screenReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
Intent.ACTION_SCREEN_ON -> handleScreenOn()
Intent.ACTION_SCREEN_OFF -> handleScreenOff()
}
}
}
override fun onResume() {
super.onResume()
val filter = IntentFilter().apply {
addAction(Intent.ACTION_SCREEN_ON)
addAction(Intent.ACTION_SCREEN_OFF)
}
registerReceiver(screenReceiver, filter)
}
override fun onPause() {
super.onPause()
unregisterReceiver(screenReceiver)
}
}8. Content Provider
8.1 FileProvider (Chia sẻ files)
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>File res/xml/file_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!-- Internal app storage: /data/data/com.example/files/ -->
<files-path name="internal_files" path="." />
<!-- Internal cache: /data/data/com.example/cache/ -->
<cache-path name="cache" path="." />
<!-- External files: /storage/emulated/0/Android/data/com.example/files/ -->
<external-files-path name="external_files" path="." />
<!-- External cache -->
<external-cache-path name="external_cache" path="." />
<!-- External storage root (deprecated) -->
<external-path name="external" path="." />
<!-- Specific subdirectory -->
<files-path name="images" path="images/" />
</paths>Ví dụ thực tế - Share image:
fun shareImage(context: Context, imageFile: File) {
val uri = FileProvider.getUriForFile(
context,
"${context.packageName}.fileprovider",
imageFile
)
val shareIntent = Intent(Intent.ACTION_SEND).apply {
type = "image/*"
putExtra(Intent.EXTRA_STREAM, uri)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
context.startActivity(Intent.createChooser(shareIntent, "Share via"))
}8.2 Custom Content Provider
<provider
android:name=".data.NotesProvider"
android:authorities="com.example.app.provider"
android:exported="true"
android:permission="com.example.app.READ_NOTES">
<path-permission
android:pathPrefix="/notes"
android:readPermission="com.example.app.READ_NOTES"
android:writePermission="com.example.app.WRITE_NOTES" />
</provider>Khi nào cần Custom Provider?
- Chia sẻ data với apps khác
- Sync data với system (Contacts, Calendar)
- Search integration
9. Hardware Features
<!-- Camera bắt buộc - app sẽ không hiện trên devices không có camera -->
<uses-feature
android:name="android.hardware.camera"
android:required="true" />
<!-- Camera tùy chọn - app vẫn hiện nhưng có thể kiểm tra runtime -->
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<!-- GPS -->
<uses-feature
android:name="android.hardware.location.gps"
android:required="false" />
<!-- Bluetooth -->
<uses-feature
android:name="android.hardware.bluetooth"
android:required="false" />
<!-- NFC -->
<uses-feature
android:name="android.hardware.nfc"
android:required="false" />
<!-- Fingerprint -->
<uses-feature
android:name="android.hardware.fingerprint"
android:required="false" />
<!-- Telephony (cho phone apps) -->
<uses-feature
android:name="android.hardware.telephony"
android:required="false" />
<!-- Microphone -->
<uses-feature
android:name="android.hardware.microphone"
android:required="true" />Kiểm tra feature runtime:
fun hasCameraFeature(context: Context): Boolean {
return context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
}
fun hasNfcFeature(context: Context): Boolean {
return context.packageManager.hasSystemFeature(PackageManager.FEATURE_NFC)
}Ví dụ thực tế:
- QR Scanner app:
camerarequired = true - E-commerce app:
camerarequired = false (có thể dùng để scan QR nhưng không bắt buộc)
10. Screen Configurations
<supports-screens
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="true"
android:anyDensity="true"
android:resizeable="true" />Ý nghĩa:
| Thuộc tính | Screen size | Ví dụ device |
|---|---|---|
smallScreens | < 3” | Smartwatch (hiếm) |
normalScreens | 3” - 5” | Hầu hết phones |
largeScreens | 5” - 7” | Tablets nhỏ |
xlargeScreens | 7”+ | Tablets lớn |
Compatible screens (giới hạn devices):
<compatible-screens>
<!-- Chỉ hỗ trợ normal screens với hdpi, xhdpi, xxhdpi -->
<screen android:screenSize="normal" android:screenDensity="hdpi" />
<screen android:screenSize="normal" android:screenDensity="xhdpi" />
<screen android:screenSize="normal" android:screenDensity="xxhdpi" />
</compatible-screens>11. Meta-data
11.1 Google Services
<application>
<!-- Google Maps API Key -->
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${MAPS_API_KEY}" />
<!-- Google Play Services version check -->
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
</application>11.2 Firebase
<application>
<!-- Default notification icon -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_notification" />
<!-- Default notification color -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/colorAccent" />
<!-- Default notification channel -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="default_channel" />
</application>11.3 Facebook SDK
<application>
<meta-data
android:name="com.facebook.sdk.ApplicationId"
android:value="@string/facebook_app_id" />
<meta-data
android:name="com.facebook.sdk.ClientToken"
android:value="@string/facebook_client_token" />
</application>11.4 AdMob
<application>
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-xxxxxxxx~xxxxxxxx" />
</application>12. Network Security Config
Tạo file res/xml/network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<!-- Base config cho tất cả domains -->
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
<!-- Cho phép cleartext cho localhost (development) -->
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">localhost</domain>
<domain includeSubdomains="true">10.0.2.2</domain>
<domain includeSubdomains="true">192.168.1.0</domain>
</domain-config>
<!-- Debug-only: trust user-installed certificates -->
<debug-overrides>
<trust-anchors>
<certificates src="user" />
<certificates src="system" />
</trust-anchors>
</debug-overrides>
</network-security-config>Khai báo trong manifest:
<application
android:networkSecurityConfig="@xml/network_security_config">Khi nào cần?
| Scenario | Config |
|---|---|
| Development với localhost | cleartextTrafficPermitted="true" cho localhost |
| Production | cleartextTrafficPermitted="false" (chỉ HTTPS) |
| Certificate pinning | Thêm <pin-set> |
| Charles Proxy debugging | debug-overrides với user certificates |
Certificate Pinning:
<domain-config>
<domain includeSubdomains="true">api.myapp.com</domain>
<pin-set expiration="2025-12-31">
<pin digest="SHA-256">base64EncodedPublicKeyHash1=</pin>
<pin digest="SHA-256">base64EncodedPublicKeyHash2=</pin>
</pin-set>
</domain-config>13. Queries (Android 11+)
Từ Android 11, cần khai báo package visibility:
<queries>
<!-- Specific packages -->
<package android:name="com.facebook.katana" />
<package android:name="com.twitter.android" />
<!-- Apps that handle specific intents -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
<!-- Apps that handle sharing -->
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="image/*" />
</intent>
<!-- Apps that can take photos -->
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
</queries>Khi nào cần?
- Kiểm tra app khác có được cài không (isPackageInstalled)
- Mở app khác (startActivity với package name)
- Share content tới specific apps
14. Ví dụ Manifest hoàn chỉnh
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<!-- Features -->
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<!-- Package visibility (Android 11+) -->
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
</queries>
<application
android:name=".MyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApp"
android:networkSecurityConfig="@xml/network_security_config"
tools:targetApi="34">
<!-- Main Activity -->
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/Theme.MyApp.Splash">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- Deep Links -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="myapp.com" />
</intent-filter>
</activity>
<!-- Other Activities -->
<activity
android:name=".DetailActivity"
android:exported="false"
android:parentActivityName=".MainActivity" />
<!-- Services -->
<service
android:name=".LocationService"
android:foregroundServiceType="location"
android:exported="false" />
<!-- Receivers -->
<receiver
android:name=".BootReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- Providers -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<!-- Meta-data -->
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${MAPS_API_KEY}" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_notification" />
</application>
</manifest>📝 Tóm tắt
| Element | Mục đích | Ví dụ thực tế |
|---|---|---|
<uses-permission> | Khai báo quyền cần thiết | Internet, Camera, Location |
<uses-feature> | Khai báo yêu cầu hardware | Camera required/optional |
<application> | Cấu hình app toàn cục | Icon, theme, Application class |
<activity> | Màn hình UI | Launcher, Deep links |
<service> | Background processing | Music player, Downloads |
<receiver> | Nhận broadcasts | Boot completed, Battery |
<provider> | Chia sẻ data | FileProvider cho sharing |
<meta-data> | Config bổ sung | API keys, Firebase |
<queries> | Package visibility (API 30+) | Check installed apps |
Best Practices:
- Chỉ khai báo permissions thực sự cần - ít permission = ít risk
- Dùng
android:required="false"cho features tùy chọn - Set
exported="false"cho components internal - Sử dụng Network Security Config đúng cách cho production
- Khai báo
<queries>cho Android 11+ nếu cần package visibility