Bạn có một file trong cache riêng của app và muốn gửi sang ứng dụng khác. Gửi đường dẫn tuyệt đối không làm app nhận có quyền đọc. Giải pháp trong bài là FileProvider, một content URI và quyền đọc tạm thời cho đúng file được gửi.
Đối chiếu tài liệu AndroidX Core ngày 31/10/2026. Mẫu giả định project Android Kotlin dùng AppCompatActivity, AndroidX Core và minSdk từ 23. Đây là hướng dẫn tích hợp vào project có sẵn, chưa chạy Android build hoặc thử Sharesheet trên thiết bị.
1. Khai báo một provider riêng
Tạo lớp ShareFileProvider.kt trong package của ứng dụng. Ví dụ dùng package giả com.example.share; thay bằng namespace thật ở cả mã và manifest. Kế thừa FileProvider giúp làm rõ component được khai báo, thay vì dựa vào sử dụng trực tiếp lớp thư viện trên mọi thiết bị.
package com.example.share
import androidx.core.content.FileProvider
class ShareFileProvider : FileProvider()
Trong phần application của manifest có sẵn, thêm provider sau. ${applicationId} là placeholder được Android Gradle xử lý, giúp authority theo đúng variant. Nó không phải biến để bạn thay bằng đường dẫn file.
<provider xmlns:android="http://schemas.android.com/apk/res/android"
android:name="com.example.share.ShareFileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/share_paths" />
</provider>
Tạo res/xml/share_paths.xml. Chỉ thư mục share/ dưới cache được cấu hình để tạo URI; không mở toàn bộ cache bằng path="." để xử lý một lỗi đường dẫn cụ thể.
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="shared_reports" path="share/" />
</paths>
Tên shared_reports là đoạn tên dùng trong URI, còn share/ là thư mục thực. Cấu hình thư mục cho phép provider tạo URI cho các file bên trong; quyền gửi cho app nhận vẫn phải được cấp cho URI cụ thể.
2. Tạo file mẫu và gửi quyền đọc
Extension sau gọi từ một Activity đang mở, ví dụ trong listener của nút chia sẻ đã có. Nó chỉ ghi một file text nhỏ với dữ liệu minh họa công khai. Với báo cáo lớn, chuyển việc tạo file sang tác vụ nền và xử lý lỗi theo luồng UI của dự án.
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.ClipData
import android.content.Intent
import android.widget.Toast
import androidx.core.content.FileProvider
import java.io.File
import java.io.IOException
fun Activity.shareDemoReport() {
try {
val folder = File(cacheDir, "share")
if (!folder.isDirectory && !folder.mkdirs()) {
throw IOException("Không tạo được thư mục chia sẻ")
}
val report = File.createTempFile("demo-", ".txt", folder)
report.writeText("Báo cáo mẫu AeDev\n", Charsets.UTF_8)
val uri = FileProvider.getUriForFile(
this, "$packageName.fileprovider", report
)
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_STREAM, uri)
clipData = ClipData.newRawUri("Báo cáo mẫu", uri)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
startActivity(Intent.createChooser(intent, "Chia sẻ báo cáo"))
} catch (error: ActivityNotFoundException) {
Toast.makeText(this, "Không có ứng dụng nhận phù hợp", Toast.LENGTH_SHORT).show()
} catch (error: IOException) {
Toast.makeText(this, "Không tạo được báo cáo", Toast.LENGTH_SHORT).show()
}
}
Gọi shareDemoReport() từ Activity chỉ khi người dùng chọn chia sẻ. EXTRA_STREAM mang URI của file, MIME mô tả dữ liệu, còn flag cấp quyền đọc tạm thời. Không thêm quyền ghi vì app nhận chỉ cần đọc báo cáo.
3. Kiểm tra authority, đường dẫn và thời điểm xóa
Trước tiên xác nhận authority trong merged manifest bằng authority truyền vào getUriForFile. Với applicationIdSuffix cho debug, so sánh giá trị sau merge thay vì chỉ nhìn package ở đầu file Kotlin.
Chọn một ứng dụng có thể nhận file text rồi thử mở nội dung. Mở được chooser chỉ chứng minh giao diện chọn app đã xuất hiện; chưa chứng minh app nhận đã đọc được file. Thử cả variant debug và release nếu có authority khác nhau.
Không xóa file ngay sau startActivity: app nhận có thể chưa đọc xong. Demo tạo tên file riêng cho mỗi lần chia sẻ để tránh ghi đè nội dung đang được đọc. Sản phẩm cần chính sách dọn các file cache cũ phù hợp; ví dụ chưa triển khai lịch dọn đó và cache cũng không phải nơi lưu báo cáo bền vững.
Lỗi thường gặp và giới hạn
Lỗi không tìm thấy configured root thường do file nằm ngoài cache/share hoặc resource paths không được provider dùng đúng. Kiểm tra cấu hình trước khi mở rộng root. Lỗi authority thường liên quan applicationId, variant hoặc tên provider trong manifest.
Không bắt mọi Exception rồi coi đó là chia sẻ thành công. Mẫu xử lý lỗi tạo file và không có Activity phù hợp, còn lỗi cấu hình cần được phát hiện trong quá trình tích hợp. Không gửi file có token hoặc dữ liệu riêng tư chỉ vì nó đang nằm trong cache.
XML trong bài đã được parse và rà phạm vi khai báo; Kotlin được rà theo API chính thức, chưa có kết quả chạy trên Android. Bước xác nhận cuối vẫn là app nhận đọc được đúng file với quyền tối thiểu, sau khi bạn kiểm tra trên thiết bị mục tiêu.


