Sao chép và dán

Android cung cấp một khung toàn diện về bảng nhớ tạm cho thao tác sao chép và dán. API này hỗ trợ các kiểu dữ liệu từ đơn giản đến phức tạp, bao gồm cả chuỗi văn bản, cấu trúc dữ liệu phức tạp, dữ liệu văn bản và luồng nhị phân cũng như các thành phần của ứng dụng. Dữ liệu văn bản đơn giản được lưu trữ trực tiếp trong bảng nhớ tạm, còn dữ liệu phức tạp được lưu trữ dưới dạng tệp tham chiếu mà ứng dụng dán sẽ phân giải với nhà cung cấp nội dung. Hoạt động sao chép và dán diễn ra cả trong một ứng dụng và giữa các ứng dụng triển khai khung.

Vì một phần của khung sử dụng trình cung cấp nội dung, nên tài liệu này giả định rằng bạn đã quen thuộc với API Trình cung cấp nội dung Android, được mô tả trong phần Trình cung cấp nội dung.

Người dùng mong muốn nhận được phản hồi khi sao chép nội dung vào bảng nhớ tạm. Vì vậy, ngoài khung hỗ trợ tính năng sao chép và dán, Android còn cho người dùng thấy một giao diện người dùng mặc định khi sao chép trong Android 13 (API cấp 33) trở lên. Do tính năng này, bạn có nguy cơ nhận được thông báo trùng lặp. Bạn có thể tìm hiểu thêm về trường hợp đặc biệt này trong phần Tránh thông báo trùng lặp.

Ảnh động minh hoạ thông báo trong bảng nhớ tạm của Android 13
Hình 1. Giao diện người dùng hiển thị khi nội dung nhập vào bảng nhớ tạm trong Android 13 trở lên.

Cung cấp ý kiến phản hồi cho người dùng theo cách thủ công khi sao chép trong Android 12L (API cấp 32) trở xuống. Xem các đề xuất cho vấn đề này trong tài liệu này.

Khung bảng nhớ tạm

Khi bạn sử dụng khung bảng nhớ tạm, hãy đặt dữ liệu vào đối tượng cắt, sau đó đặt đối tượng cắt vào bảng nhớ tạm trên toàn hệ thống. Đối tượng clip có ba dạng sau:

Văn bản
Chuỗi văn bản. Đặt chuỗi trực tiếp vào đối tượng clip mà sau đó bạn đặt vào bảng nhớ tạm. Để dán chuỗi, hãy lấy đối tượng clip từ bảng nhớ tạm rồi sao chép chuỗi đó vào bộ nhớ của ứng dụng.
URI
Đối tượng Uri đại diện cho bất kỳ dạng URI nào. Thao tác này chủ yếu để sao chép dữ liệu phức tạp từ một nhà cung cấp nội dung. Để sao chép dữ liệu, hãy đặt một đối tượng Uri vào đối tượng cắt và đặt đối tượng cắt đó vào bảng nhớ tạm. Để dán dữ liệu, hãy lấy đối tượng cắt, lấy đối tượng Uri, phân giải đối tượng đó thành một nguồn dữ liệu (chẳng hạn như trình cung cấp nội dung) rồi sao chép dữ liệu từ nguồn đó vào bộ nhớ của ứng dụng.
Intent
Một Intent (ý định). Tính năng này hỗ trợ sao chép lối tắt ứng dụng. Để sao chép dữ liệu, hãy tạo một Intent, đặt đối tượng này vào một đối tượng cắt và đặt đối tượng cắt vào bảng nhớ tạm. Để dán dữ liệu, hãy lấy đối tượng cắt rồi sao chép đối tượng Intent vào vùng bộ nhớ của ứng dụng.

Bảng nhớ tạm chỉ chứa một đối tượng clip tại mỗi thời điểm. Khi ứng dụng đặt một đối tượng cắt vào bảng nhớ tạm, đối tượng cắt trước đó sẽ biến mất.

Nếu muốn cho phép người dùng dán dữ liệu vào ứng dụng của mình, bạn không cần phải xử lý mọi loại dữ liệu. Bạn có thể kiểm tra dữ liệu trên bảng nhớ tạm trước khi cung cấp cho người dùng tuỳ chọn dán dữ liệu đó. Ngoài việc có một số dạng dữ liệu nhất định, đối tượng đoạn video còn chứa siêu dữ liệu cho bạn biết những loại MIME nào có sẵn. Siêu dữ liệu này giúp bạn quyết định xem ứng dụng của mình có thể làm gì đó hữu ích với dữ liệu trong bảng nhớ tạm hay không. Chẳng hạn nếu có một ứng dụng chủ yếu xử lý văn bản, bạn nên bỏ qua các đối tượng cắt chứa URI hoặc ý định.

Bạn cũng nên cho phép người dùng dán văn bản bất kể dạng dữ liệu nào trên bảng nhớ tạm. Để làm như vậy, hãy buộc dữ liệu bảng nhớ tạm thành dạng văn bản biểu diễn, sau đó dán văn bản này. Vấn đề này được mô tả trong phần Chuyển đổi bảng nhớ tạm thành văn bản.

Các lớp lưu dữ liệu vào bảng nhớ tạm

Phần này mô tả các lớp mà khung bảng nhớ tạm sử dụng.

ClipboardManager

Bảng nhớ tạm của hệ thống Android được biểu thị bằng lớp ClipboardManager chung. Đừng trực tiếp tạo thực thể cho lớp này. Thay vào đó, hãy tham chiếu đến đối tượng này bằng cách gọi getSystemService(CLIPBOARD_SERVICE).

ClipData, ClipData.Item và ClipDescription

Để thêm dữ liệu vào bảng nhớ tạm, hãy tạo một đối tượng ClipData chứa nội dung mô tả về dữ liệu và chính dữ liệu đó. Bảng nhớ tạm lưu giữ mỗi lần một ClipData. ClipData chứa một đối tượng ClipDescription và một hoặc nhiều đối tượng ClipData.Item.

Đối tượng ClipDescription chứa siêu dữ liệu về đối tượng clip. Cụ thể, đối tượng này chứa một mảng các loại MIME có sẵn cho dữ liệu của đối tượng cắt. Ngoài ra, trên Android 12 (API cấp 31) trở lên, siêu dữ liệu bao gồm thông tin về việc đối tượng có chứa văn bản cách điệu hay không và về loại văn bản trong đối tượng. Khi bạn đặt một đối tượng cắt vào bảng nhớ tạm, thông tin này sẽ có sẵn để dán các ứng dụng. Dịch vụ này giúp kiểm tra xem các ứng dụng đó có thể xử lý dữ liệu đối tượng cắt hay không.

Đối tượng ClipData.Item chứa dữ liệu văn bản, URI hoặc ý định:

Văn bản
Một CharSequence.
URI
Một Uri. Lớp này thường chứa URI của nhà cung cấp nội dung, mặc dù mọi URI đều được phép. Ứng dụng cung cấp dữ liệu sẽ đặt URI trên bảng nhớ tạm. Các ứng dụng muốn dán dữ liệu sẽ lấy URI từ bảng nhớ tạm và dùng URI này để truy cập vào nhà cung cấp nội dung hoặc nguồn dữ liệu khác và truy xuất dữ liệu đó.
Intent
Một Intent. Loại dữ liệu này cho phép bạn sao chép một lối tắt ứng dụng vào bảng nhớ tạm. Sau đó, người dùng có thể dán lối tắt vào ứng dụng để sử dụng sau.

Bạn có thể thêm nhiều đối tượng ClipData.Item vào một clip. Điều này cho phép người dùng sao chép và dán nhiều lựa chọn dưới dạng một đối tượng cắt. Ví dụ: nếu có tiện ích danh sách cho phép người dùng chọn nhiều mục cùng lúc, bạn có thể sao chép tất cả các mục vào bảng nhớ tạm cùng một lúc. Để thực hiện việc này, hãy tạo một ClipData.Item riêng cho từng mục trong danh sách, sau đó thêm các đối tượng ClipData.Item vào đối tượng ClipData.

Phương thức ClipData thuận tiện

Lớp ClipData cung cấp các phương thức tĩnh thuận tiện để tạo đối tượng ClipData bằng một đối tượng ClipData.Item và một đối tượng ClipDescription đơn giản:

newPlainText(label, text)
Trả về một đối tượng ClipData, trong đó mỗi đối tượng ClipData.Item chứa một chuỗi văn bản. Nhãn của đối tượng ClipDescription được đặt thành label. Loại MIME duy nhất trong ClipDescriptionMIMETYPE_TEXT_PLAIN.

Sử dụng newPlainText() để tạo một đoạn cắt từ một chuỗi văn bản.

newUri(resolver, label, URI)
Trả về một đối tượng ClipData, trong đó mỗi đối tượng ClipData.Item chứa một URI. Nhãn của đối tượng ClipDescription được đặt thành label. Nếu URI là một URI nội dung – tức là nếu Uri.getScheme() trả về content: – thì phương thức này sẽ sử dụng đối tượng ContentResolver được cung cấp trong resolver để truy xuất các loại MIME có sẵn từ nhà cung cấp nội dung. Sau đó, hệ thống sẽ lưu trữ những thông tin này trong ClipDescription. Đối với URI không phải là URI content:, phương thức này sẽ thiết lập loại MIME thành MIMETYPE_TEXT_URILIST.

Sử dụng newUri() để tạo một đoạn cắt từ một URI, cụ thể là URI content:.

newIntent(label, intent)
Trả về một đối tượng ClipData, trong đó đối tượng ClipData.Item duy nhất chứa Intent. Nhãn của đối tượng ClipDescription được đặt thành label. Loại MIME được đặt thành MIMETYPE_TEXT_INTENT.

Sử dụng newIntent() để tạo một phần cắt từ đối tượng Intent.

Chuyển đổi dữ liệu trong bảng nhớ tạm thành văn bản

Ngay cả khi ứng dụng của bạn chỉ xử lý văn bản, bạn vẫn có thể sao chép dữ liệu không phải văn bản từ bảng nhớ tạm bằng cách chuyển đổi dữ liệu đó bằng phương thức ClipData.Item.coerceToText().

Phương thức này chuyển đổi dữ liệu trong ClipData.Item thành văn bản và trả về một CharSequence. Giá trị mà ClipData.Item.coerceToText() trả về sẽ dựa trên dạng dữ liệu trong ClipData.Item:

Văn bản
Nếu ClipData.Item là văn bản, tức là nếu getText() không rỗng – thì CoerceToText() sẽ trả về văn bản đó.
URI
Nếu ClipData.Item là một URI (tức là nếu getUri() không rỗng), coerceToText() sẽ cố gắng sử dụng nó làm URI nội dung.
  • Nếu URI là một URI nội dung và trình cung cấp có thể trả về một luồng văn bản, thì coerceToText() sẽ trả về một luồng văn bản.
  • Nếu URI là một URI nội dung nhưng trình cung cấp không cung cấp luồng văn bản, thì coerceToText() sẽ trả về kết quả biểu diễn của URI. Kết quả biểu diễn giống với kết quả do Uri.toString() trả về.
  • Nếu URI không phải là URI nội dung, thì coerceToText() sẽ trả về giá trị biểu diễn của URI. Kết quả biểu diễn giống với kết quả do Uri.toString() trả về.
Intent
Nếu ClipData.Item là một Intent – tức là nếu getIntent() không rỗng – coerceToText() sẽ chuyển đổi giá trị này thành một URI Ý định và trả về URI đó. Kết quả biểu diễn giống với kết quả do Intent.toUri(URI_INTENT_SCHEME) trả về.

Khung bảng nhớ tạm được tóm tắt trong hình 2. Để sao chép dữ liệu, ứng dụng sẽ đặt đối tượng ClipData vào bảng nhớ tạm ClipboardManager chung. ClipData chứa một hoặc nhiều đối tượng ClipData.Item và một đối tượng ClipDescription. Để dán dữ liệu, ứng dụng sẽ lấy ClipData, nhận loại MIME của ClipDescription và nhận dữ liệu từ ClipData.Item hoặc từ trình cung cấp nội dung do ClipData.Item tham chiếu.

Hình ảnh cho thấy sơ đồ khối của khung sao chép và dán
Hình 2. Khung bảng nhớ tạm của Android.

Sao chép vào bảng nhớ tạm

Để sao chép dữ liệu vào bảng nhớ tạm, hãy xử lý đối tượng ClipboardManager chung, tạo đối tượng ClipData rồi thêm ClipDescription và một hoặc nhiều đối tượng ClipData.Item vào đối tượng đó. Sau đó, hãy thêm đối tượng ClipData đã hoàn tất vào đối tượng ClipboardManager. Điều này được mô tả kỹ hơn trong quy trình sau:

  1. Nếu bạn đang sao chép dữ liệu bằng URI nội dung, hãy thiết lập một nhà cung cấp nội dung.
  2. Lấy bảng nhớ tạm thời của hệ thống:

    Kotlin

    when(menuItem.itemId) {
        ...
        R.id.menu_copy -> { // if the user selects copy
            // Gets a handle to the clipboard service.
            val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
        }
    }
    

    Java

    ...
    // If the user selects copy.
    case R.id.menu_copy:
    
    // Gets a handle to the clipboard service.
    ClipboardManager clipboard = (ClipboardManager)
            getSystemService(Context.CLIPBOARD_SERVICE);
    
  3. Sao chép dữ liệu vào đối tượng ClipData mới:

    • Đối với văn bản

      Kotlin

      // Creates a new text clip to put on the clipboard.
      val clip: ClipData = ClipData.newPlainText("simple text", "Hello, World!")
      

      Java

      // Creates a new text clip to put on the clipboard.
      ClipData clip = ClipData.newPlainText("simple text", "Hello, World!");
      
    • Đối với URI

      Đoạn mã này tạo URI bằng cách mã hoá mã nhận dạng bản ghi vào URI nội dung cho trình cung cấp. Kỹ thuật này được đề cập chi tiết hơn trong phần Mã hoá giá trị nhận dạng trên URI.

      Kotlin

      // Creates a Uri using a base Uri and a record ID based on the contact's last
      // name. Declares the base URI string.
      const val CONTACTS = "content://com.example.contacts"
      
      // Declares a path string for URIs, used to copy data.
      const val COPY_PATH = "/copy"
      
      // Declares the Uri to paste to the clipboard.
      val copyUri: Uri = Uri.parse("$CONTACTS$COPY_PATH/$lastName")
      ...
      // Creates a new URI clip object. The system uses the anonymous
      // getContentResolver() object to get MIME types from provider. The clip object's
      // label is "URI", and its data is the Uri previously created.
      val clip: ClipData = ClipData.newUri(contentResolver, "URI", copyUri)
      

      Java

      // Creates a Uri using a base Uri and a record ID based on the contact's last
      // name. Declares the base URI string.
      private static final String CONTACTS = "content://com.example.contacts";
      
      // Declares a path string for URIs, used to copy data.
      private static final String COPY_PATH = "/copy";
      
      // Declares the Uri to paste to the clipboard.
      Uri copyUri = Uri.parse(CONTACTS + COPY_PATH + "/" + lastName);
      ...
      // Creates a new URI clip object. The system uses the anonymous
      // getContentResolver() object to get MIME types from provider. The clip object's
      // label is "URI", and its data is the Uri previously created.
      ClipData clip = ClipData.newUri(getContentResolver(), "URI", copyUri);
      
    • Đối với ý định

      Đoạn mã này tạo một Intent cho một ứng dụng, sau đó đặt ứng dụng đó vào đối tượng cắt:

      Kotlin

      // Creates the Intent.
      val appIntent = Intent(this, com.example.demo.myapplication::class.java)
      ...
      // Creates a clip object with the Intent in it. Its label is "Intent"
      // and its data is the Intent object created previously.
      val clip: ClipData = ClipData.newIntent("Intent", appIntent)
      

      Java

      // Creates the Intent.
      Intent appIntent = new Intent(this, com.example.demo.myapplication.class);
      ...
      // Creates a clip object with the Intent in it. Its label is "Intent"
      // and its data is the Intent object created previously.
      ClipData clip = ClipData.newIntent("Intent", appIntent);
      
  4. Đặt đối tượng clip mới vào bảng nhớ tạm:

    Kotlin

    // Set the clipboard's primary clip.
    clipboard.setPrimaryClip(clip)
    

    Java

    // Set the clipboard's primary clip.
    clipboard.setPrimaryClip(clip);
    

Cung cấp ý kiến phản hồi khi sao chép vào bảng nhớ tạm

Người dùng mong muốn phản hồi bằng hình ảnh khi ứng dụng sao chép nội dung vào bảng nhớ tạm. Việc này được thực hiện tự động cho người dùng trên Android 13 trở lên, nhưng phải được triển khai theo cách thủ công trong các phiên bản trước.

Kể từ Android 13, hệ thống sẽ hiển thị thông báo xác nhận bằng hình ảnh tiêu chuẩn khi nội dung được thêm vào bảng nhớ tạm. Xác nhận mới thực hiện những việc sau:

  • Xác nhận nội dung đã được sao chép thành công.
  • Cung cấp bản xem trước nội dung đã sao chép.

Ảnh động minh hoạ thông báo trong bảng nhớ tạm của Android 13
Hình 3. Giao diện người dùng hiển thị khi nội dung nhập vào bảng nhớ tạm trong Android 13 trở lên.

Trong Android 12L (API cấp 32) trở xuống, người dùng có thể không chắc chắn liệu họ đã sao chép thành công nội dung hay nội dung họ đã sao chép. Tính năng này chuẩn hoá các thông báo mà các ứng dụng hiển thị sau khi sao chép, đồng thời cung cấp cho người dùng nhiều quyền kiểm soát hơn đối với bảng nhớ tạm.

Tránh thông báo trùng lặp

Trên Android 12L (API cấp 32) trở xuống, bạn nên thông báo cho người dùng khi họ sao chép thành công bằng cách đưa ra phản hồi bằng hình ảnh trong ứng dụng thông qua một tiện ích như Toast hoặc Snackbar sau khi sao chép.

Để tránh hiển thị thông tin trùng lặp, đối với Android 13 trở lên, bạn nên xoá thông báo ngắn hoặc thanh thông báo nhanh xuất hiện sau bản sao trong ứng dụng.

Đăng thanh thông báo nhanh sau khi sao chép trong ứng dụng.
Hình 4. Nếu bạn hiện thanh thông báo nhanh xác nhận sao chép trong Android 13, thì người dùng sẽ thấy các thông báo trùng lặp.
Đăng thông báo ngắn sau khi sao chép trong ứng dụng.
Hình 5. Nếu bạn hiện một thông báo ngắn xác nhận sao chép trong Android 13, thì người dùng sẽ thấy các thông báo trùng lặp.

Sau đây là một ví dụ về cách triển khai phương thức này:

fun textCopyThenPost(textCopied:String) {
    val clipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
    // When setting the clipboard text.
    clipboardManager.setPrimaryClip(ClipData.newPlainText   ("", textCopied))
    // Only show a toast for Android 12 and lower.
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2)
        Toast.makeText(context, “Copied”, Toast.LENGTH_SHORT).show()
}

Thêm nội dung nhạy cảm vào bảng nhớ tạm

Nếu ứng dụng cho phép người dùng sao chép nội dung nhạy cảm vào bảng nhớ tạm, chẳng hạn như mật khẩu hoặc thông tin thẻ tín dụng, thì bạn phải thêm cờ vào ClipDescription trong ClipData trước khi gọi ClipboardManager.setPrimaryClip(). Việc thêm cờ này sẽ ngăn nội dung nhạy cảm xuất hiện trong thông tin xác nhận bằng hình ảnh về nội dung đã sao chép trong Android 13 trở lên.

Bản xem trước văn bản sao chép không gắn cờ nội dung nhạy cảm
Hình 6. Bản xem trước văn bản sao chép không có cờ về nội dung nhạy cảm.
Bản xem trước văn bản sao chép có gắn cờ nội dung nhạy cảm.
Hình 7. Bản xem trước văn bản sao chép có gắn cờ nội dung nhạy cảm.

Để gắn cờ nội dung nhạy cảm, hãy thêm một boolean bổ sung vào ClipDescription. Tất cả các ứng dụng đều phải thực hiện việc này, bất kể cấp độ API mục tiêu.

// If your app is compiled with the API level 33 SDK or higher.
clipData.apply {
    description.extras = PersistableBundle().apply {
        putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true)
    }
}

// If your app is compiled with a lower SDK.
clipData.apply {
    description.extras = PersistableBundle().apply {
        putBoolean("android.content.extra.IS_SENSITIVE", true)
    }
}

Dán nội dung từ bảng nhớ tạm

Như đã mô tả trước đó, hãy dán dữ liệu từ bảng nhớ tạm bằng cách lấy đối tượng bảng nhớ tạm toàn cục, lấy đối tượng cắt, xem xét dữ liệu và sao chép dữ liệu từ đối tượng cắt vào bộ nhớ của riêng bạn (nếu có thể). Phần này giải thích chi tiết về cách dán 3 dạng dữ liệu bảng nhớ tạm.

Dán văn bản thuần tuý

Để dán văn bản thuần tuý, hãy lấy bảng nhớ tạm toàn cục và xác minh rằng bảng nhớ tạm có thể trả về văn bản thuần tuý. Sau đó, hãy lấy đối tượng cắt và sao chép văn bản của đối tượng đó vào bộ nhớ của riêng bạn bằng getText() như mô tả trong quy trình sau:

  1. Lấy đối tượng ClipboardManager toàn cục bằng getSystemService(CLIPBOARD_SERVICE). Ngoài ra, hãy khai báo một biến toàn cục để chứa văn bản được dán:

    Kotlin

    var clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    var pasteData: String = ""
    

    Java

    ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    String pasteData = "";
    
  2. Xác định xem bạn cần bật hay tắt tuỳ chọn "dán" trong hoạt động hiện tại. Xác minh rằng bảng nhớ tạm có chứa một đối tượng cắt và bạn có thể xử lý loại dữ liệu mà đối tượng đó biểu thị:

    Kotlin

    // Gets the ID of the "paste" menu item.
    val pasteItem: MenuItem = menu.findItem(R.id.menu_paste)
    
    // If the clipboard doesn't contain data, disable the paste menu item.
    // If it does contain data, decide whether you can handle the data.
    pasteItem.isEnabled = when {
        !clipboard.hasPrimaryClip() -> {
            false
        }
        !(clipboard.primaryClipDescription.hasMimeType(MIMETYPE_TEXT_PLAIN)) -> {
            // Disables the paste menu item, since the clipboard has data but it
            // isn't plain text.
            false
        }
        else -> {
            // Enables the paste menu item, since the clipboard contains plain text.
            true
        }
    }
    

    Java

    // Gets the ID of the "paste" menu item.
    MenuItem pasteItem = menu.findItem(R.id.menu_paste);
    
    // If the clipboard doesn't contain data, disable the paste menu item.
    // If it does contain data, decide whether you can handle the data.
    if (!(clipboard.hasPrimaryClip())) {
    
        pasteItem.setEnabled(false);
    
    } else if (!(clipboard.getPrimaryClipDescription().hasMimeType(MIMETYPE_TEXT_PLAIN))) {
    
        // Disables the paste menu item, since the clipboard has data but
        // it isn't plain text.
        pasteItem.setEnabled(false);
    } else {
    
        // Enables the paste menu item, since the clipboard contains plain text.
        pasteItem.setEnabled(true);
    }
    
  3. Sao chép dữ liệu từ bảng nhớ tạm. Bạn chỉ có thể đến điểm này trong mã nếu mục trong trình đơn "dán" được bật. Vì vậy, bạn có thể giả định rằng bảng nhớ tạm chứa văn bản thuần tuý. Bạn vẫn chưa biết liệu tệp có chứa một chuỗi văn bản hoặc một URI trỏ đến văn bản thuần tuý hay không. Đoạn mã sau đây kiểm thử điều này, nhưng chỉ cho thấy mã để xử lý văn bản thuần tuý:

    Kotlin

    when (menuItem.itemId) {
        ...
        R.id.menu_paste -> {    // Responds to the user selecting "paste".
            // Examines the item on the clipboard. If getText() doesn't return null,
            // the clip item contains the text. Assumes that this application can only
            // handle one item at a time.
            val item = clipboard.primaryClip.getItemAt(0)
    
            // Gets the clipboard as text.
            pasteData = item.text
    
            return if (pasteData != null) {
                // If the string contains data, then the paste operation is done.
                true
            } else {
                // The clipboard doesn't contain text. If it contains a URI,
                // attempts to get data from it.
                val pasteUri: Uri? = item.uri
    
                if (pasteUri != null) {
                    // If the URI contains something, try to get text from it.
    
                    // Calls a routine to resolve the URI and get data from it.
                    // This routine isn't presented here.
                    pasteData = resolveUri(pasteUri)
                    true
                } else {
    
                    // Something is wrong. The MIME type was plain text, but the
                    // clipboard doesn't contain text or a Uri. Report an error.
                    Log.e(TAG,"Clipboard contains an invalid data type")
                    false
                }
            }
        }
    }
    

    Java

    // Responds to the user selecting "paste".
    case R.id.menu_paste:
    
    // Examines the item on the clipboard. If getText() does not return null,
    // the clip item contains the text. Assumes that this application can only
    // handle one item at a time.
     ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);
    
    // Gets the clipboard as text.
    pasteData = item.getText();
    
    // If the string contains data, then the paste operation is done.
    if (pasteData != null) {
        return true;
    
    // The clipboard doesn't contain text. If it contains a URI, attempts to get
    // data from it.
    } else {
        Uri pasteUri = item.getUri();
    
        // If the URI contains something, try to get text from it.
        if (pasteUri != null) {
    
            // Calls a routine to resolve the URI and get data from it.
            // This routine isn't presented here.
            pasteData = resolveUri(Uri);
            return true;
        } else {
    
            // Something is wrong. The MIME type is plain text, but the
            // clipboard doesn't contain text or a Uri. Report an error.
            Log.e(TAG, "Clipboard contains an invalid data type");
            return false;
        }
    }
    

Dán dữ liệu từ URI nội dung

Nếu đối tượng ClipData.Item chứa một URI nội dung và bạn xác định rằng mình có thể xử lý một trong các loại MIME của đối tượng đó, hãy tạo ContentResolver và gọi phương thức của trình cung cấp nội dung thích hợp để truy xuất dữ liệu.

Quy trình sau đây mô tả cách lấy dữ liệu từ trình cung cấp nội dung dựa trên URI nội dung trên bảng nhớ tạm. Quy trình này kiểm tra xem nhà cung cấp có cung cấp loại MIME mà ứng dụng có thể sử dụng hay không.

  1. Khai báo biến toàn cục chứa loại MIME:

    Kotlin

    // Declares a MIME type constant to match against the MIME types offered
    // by the provider.
    const val MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact"
    

    Java

    // Declares a MIME type constant to match against the MIME types offered by
    // the provider.
    public static final String MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact";
    
  2. Lấy bảng nhớ tạm toàn cục. Ngoài ra, hãy sử dụng trình phân giải nội dung để bạn có thể truy cập vào nhà cung cấp nội dung:

    Kotlin

    // Gets a handle to the Clipboard Manager.
    val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    
    // Gets a content resolver instance.
    val cr = contentResolver
    

    Java

    // Gets a handle to the Clipboard Manager.
    ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    
    // Gets a content resolver instance.
    ContentResolver cr = getContentResolver();
    
  3. Lấy đoạn video chính từ bảng nhớ tạm và nội dung của đoạn video đó dưới dạng URI:

    Kotlin

    // Gets the clipboard data from the clipboard.
    val clip: ClipData? = clipboard.primaryClip
    
    clip?.run {
    
        // Gets the first item from the clipboard data.
        val item: ClipData.Item = getItemAt(0)
    
        // Tries to get the item's contents as a URI.
        val pasteUri: Uri? = item.uri
    

    Java

    // Gets the clipboard data from the clipboard.
    ClipData clip = clipboard.getPrimaryClip();
    
    if (clip != null) {
    
        // Gets the first item from the clipboard data.
        ClipData.Item item = clip.getItemAt(0);
    
        // Tries to get the item's contents as a URI.
        Uri pasteUri = item.getUri();
    
  4. Kiểm tra xem URI đó có phải là URI nội dung hay không bằng cách gọi getType(Uri). Phương thức này trả về giá trị rỗng nếu Uri không trỏ đến một trình cung cấp nội dung hợp lệ.

    Kotlin

        // If the clipboard contains a URI reference...
        pasteUri?.let {
    
            // ...is this a content URI?
            val uriMimeType: String? = cr.getType(it)
    

    Java

        // If the clipboard contains a URI reference...
        if (pasteUri != null) {
    
            // ...is this a content URI?
            String uriMimeType = cr.getType(pasteUri);
    
  5. Kiểm tra xem trình cung cấp nội dung có hỗ trợ loại MIME mà ứng dụng hiểu được hay không. Nếu có, hãy gọi ContentResolver.query() để lấy dữ liệu. Giá trị trả về là Cursor.

    Kotlin

            // If the return value isn't null, the Uri is a content Uri.
            uriMimeType?.takeIf {
    
                // Does the content provider offer a MIME type that the current
                // application can use?
                it == MIME_TYPE_CONTACT
            }?.apply {
    
                // Get the data from the content provider.
                cr.query(pasteUri, null, null, null, null)?.use { pasteCursor ->
    
                    // If the Cursor contains data, move to the first record.
                    if (pasteCursor.moveToFirst()) {
    
                        // Get the data from the Cursor here.
                        // The code varies according to the format of the data model.
                    }
    
                    // Kotlin `use` automatically closes the Cursor.
                }
            }
        }
    }
    

    Java

            // If the return value isn't null, the Uri is a content Uri.
            if (uriMimeType != null) {
    
                // Does the content provider offer a MIME type that the current
                // application can use?
                if (uriMimeType.equals(MIME_TYPE_CONTACT)) {
    
                    // Get the data from the content provider.
                    Cursor pasteCursor = cr.query(uri, null, null, null, null);
    
                    // If the Cursor contains data, move to the first record.
                    if (pasteCursor != null) {
                        if (pasteCursor.moveToFirst()) {
    
                        // Get the data from the Cursor here.
                        // The code varies according to the format of the data model.
                        }
                    }
    
                    // Close the Cursor.
                    pasteCursor.close();
                 }
             }
         }
    }
    

Dán một Ý định

Để dán một ý định, trước tiên, hãy lấy bảng nhớ tạm chung. Hãy kiểm tra đối tượng ClipData.Item để xem đối tượng đó có chứa Intent hay không. Sau đó, hãy gọi getIntent() để sao chép ý định vào bộ nhớ riêng của bạn. Sau đây là đoạn mã minh hoạ:

Kotlin

// Gets a handle to the Clipboard Manager.
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager

// Checks whether the clip item contains an Intent by testing whether
// getIntent() returns null.
val pasteIntent: Intent? = clipboard.primaryClip?.getItemAt(0)?.intent

if (pasteIntent != null) {

    // Handle the Intent.

} else {

    // Ignore the clipboard, or issue an error if
    // you expect an Intent to be on the clipboard.
}

Java

// Gets a handle to the Clipboard Manager.
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);

// Checks whether the clip item contains an Intent, by testing whether
// getIntent() returns null.
Intent pasteIntent = clipboard.getPrimaryClip().getItemAt(0).getIntent();

if (pasteIntent != null) {

    // Handle the Intent.

} else {

    // Ignore the clipboard, or issue an error if
    // you expect an Intent to be on the clipboard.
}

Hệ thống hiển thị thông báo khi ứng dụng truy cập vào dữ liệu bảng nhớ tạm

Trên Android 12 (API cấp 31) trở lên, hệ thống thường hiện một thông báo ngắn khi ứng dụng của bạn gọi getPrimaryClip(). Văn bản bên trong thông báo có định dạng sau:

APP pasted from your clipboard

Hệ thống không hiện thông báo ngắn khi ứng dụng của bạn thực hiện một trong những thao tác sau:

  • Truy cập vào ClipData qua ứng dụng của bạn.
  • Liên tục truy cập vào ClipData qua một ứng dụng cụ thể. Thông báo ngắn chỉ xuất hiện khi ứng dụng của bạn truy cập vào dữ liệu của ứng dụng đó lần đầu tiên.
  • Truy xuất siêu dữ liệu cho đối tượng cắt, chẳng hạn như bằng cách gọi getPrimaryClipDescription() thay vì getPrimaryClip().

Sử dụng nhà cung cấp nội dung để sao chép dữ liệu phức tạp

Nhà cung cấp nội dung hỗ trợ sao chép dữ liệu phức tạp như bản ghi cơ sở dữ liệu hoặc dòng tệp. Để sao chép dữ liệu, hãy đặt URI nội dung vào bảng nhớ tạm. Sau đó, các ứng dụng dán sẽ lấy URI này từ bảng nhớ tạm và dùng URI đó để truy xuất dữ liệu trong cơ sở dữ liệu hoặc chỉ số mô tả luồng tệp.

Vì ứng dụng dán chỉ có URI nội dung cho dữ liệu, nên ứng dụng này cần biết cần truy xuất phần dữ liệu nào. Bạn có thể cung cấp thông tin này bằng cách mã hoá giá trị nhận dạng cho dữ liệu trên chính URI đó hoặc bạn có thể cung cấp một URI duy nhất trả về dữ liệu mà bạn muốn sao chép. Kỹ thuật bạn chọn phụ thuộc vào cách sắp xếp dữ liệu của bạn.

Các phần sau đây mô tả cách thiết lập URI, cung cấp dữ liệu phức tạp và cung cấp luồng tệp. Những nội dung mô tả này giả định rằng bạn đã quen thuộc với các nguyên tắc chung về thiết kế trình cung cấp nội dung.

Mã hoá giá trị nhận dạng trên URI

Một kỹ thuật hữu ích để sao chép dữ liệu vào bảng nhớ tạm bằng URI là mã hoá giá trị nhận dạng cho chính dữ liệu đó. Sau đó, nhà cung cấp nội dung của bạn có thể lấy giá trị nhận dạng từ URI và sử dụng giá trị đó để truy xuất dữ liệu. Ứng dụng dán không cần phải biết rằng giá trị nhận dạng tồn tại. Lớp này chỉ cần lấy "tệp tham chiếu" của bạn (URI cộng với giá trị nhận dạng) từ bảng nhớ tạm, cung cấp cho trình cung cấp nội dung của bạn rồi lấy lại dữ liệu.

Bạn thường mã hoá giá trị nhận dạng trên một URI nội dung bằng cách ghép nối giá trị nhận dạng đó vào cuối URI. Ví dụ: giả sử bạn định nghĩa URI nhà cung cấp như chuỗi sau:

"content://com.example.contacts"

Nếu bạn muốn mã hoá tên vào URI này, hãy sử dụng đoạn mã sau:

Kotlin

val uriString = "content://com.example.contacts/Smith"

// uriString now contains content://com.example.contacts/Smith.

// Generates a uri object from the string representation.
val copyUri = Uri.parse(uriString)

Java

String uriString = "content://com.example.contacts" + "/" + "Smith";

// uriString now contains content://com.example.contacts/Smith.

// Generates a uri object from the string representation.
Uri copyUri = Uri.parse(uriString);

Nếu đang sử dụng một nhà cung cấp nội dung, bạn nên thêm một đường dẫn URI mới cho biết URI đó là để sao chép. Ví dụ: giả sử bạn đã có đường dẫn URI sau:

"content://com.example.contacts/people"
"content://com.example.contacts/people/detail"
"content://com.example.contacts/people/images"

Bạn có thể thêm một đường dẫn khác để sao chép URI:

"content://com.example.contacts/copying"

Sau đó, bạn có thể phát hiện URI "sao chép" bằng cách so khớp mẫu và xử lý URI đó bằng mã dành riêng cho việc sao chép và dán.

Bạn thường sử dụng kỹ thuật mã hoá nếu đang sử dụng trình cung cấp nội dung, cơ sở dữ liệu nội bộ hoặc bảng nội bộ để sắp xếp dữ liệu. Trong những trường hợp như vậy, bạn có nhiều dữ liệu cần sao chép và có thể là một giá trị nhận dạng duy nhất cho từng dữ liệu. Để phản hồi truy vấn từ ứng dụng dán, bạn có thể tra cứu dữ liệu theo giá trị nhận dạng và trả về dữ liệu đó.

Nếu bạn không có nhiều dữ liệu thì có thể không cần phải mã hoá giá trị nhận dạng. Bạn có thể sử dụng một URI dành riêng cho nhà cung cấp của mình. Để phản hồi một truy vấn, nhà cung cấp của bạn sẽ trả về dữ liệu hiện có.

Sao chép cấu trúc dữ liệu

Thiết lập trình cung cấp nội dung để sao chép và dán dữ liệu phức tạp làm lớp con của thành phần ContentProvider. Mã hoá URI mà bạn đặt vào bảng nhớ tạm để bảng nhớ tạm này trỏ đến bản ghi chính xác mà bạn muốn cung cấp. Ngoài ra, hãy xem xét trạng thái hiện tại của đơn đăng ký:

  • Nếu đã có nhà cung cấp nội dung, bạn có thể thêm vào phần chức năng của nhà cung cấp đó. Bạn có thể chỉ cần sửa đổi phương thức query() để xử lý các URI đến từ các ứng dụng muốn dán dữ liệu. Bạn nên sửa đổi phương thức để xử lý mẫu URI "sao chép".
  • Nếu ứng dụng của bạn duy trì một cơ sở dữ liệu nội bộ, thì bạn nên di chuyển cơ sở dữ liệu này vào một nhà cung cấp nội dung để tạo điều kiện cho việc sao chép từ cơ sở dữ liệu đó.
  • Nếu không sử dụng cơ sở dữ liệu, bạn có thể triển khai một trình cung cấp nội dung đơn giản với mục đích duy nhất là cung cấp dữ liệu cho các ứng dụng mà dán từ bảng nhớ tạm.

Trong trình cung cấp nội dung, hãy ghi đè ít nhất các phương thức sau:

query()
Các ứng dụng dán giả định rằng chúng có thể lấy dữ liệu của bạn bằng cách sử dụng phương thức này với URI mà bạn đặt vào bảng nhớ tạm. Để hỗ trợ sao chép, hãy để phương thức này phát hiện các URI có chứa đường dẫn "sao chép" đặc biệt. Sau đó, ứng dụng của bạn có thể tạo một URI "sao chép" để đưa vào bảng nhớ tạm, chứa đường dẫn sao chép và con trỏ đến bản ghi chính xác mà bạn muốn sao chép.
getType()
Phương thức này phải trả về các loại MIME cho dữ liệu mà bạn định sao chép. Phương thức newUri() gọi getType() để đặt các loại MIME vào đối tượng ClipData mới.

Các loại MIME cho dữ liệu phức tạp được mô tả trong phần Trình cung cấp nội dung.

Bạn không cần phải có bất kỳ phương thức nào khác của nhà cung cấp nội dung, chẳng hạn như insert() hoặc update(). Một ứng dụng dán chỉ cần lấy các loại MIME được hỗ trợ và sao chép dữ liệu từ nhà cung cấp của bạn. Nếu bạn đã sử dụng các phương thức này, chúng sẽ không ảnh hưởng đến hoạt động sao chép.

Các đoạn mã sau đây minh hoạ cách thiết lập ứng dụng để sao chép dữ liệu phức tạp:

  1. Trong các hằng số toàn cục của ứng dụng, hãy khai báo chuỗi URI cơ sở và đường dẫn xác định chuỗi URI mà bạn đang dùng để sao chép dữ liệu. Đồng thời, hãy khai báo một loại MIME cho dữ liệu được sao chép.

    Kotlin

    // Declares the base URI string.
    private const val CONTACTS = "content://com.example.contacts"
    
    // Declares a path string for URIs that you use to copy data.
    private const val COPY_PATH = "/copy"
    
    // Declares a MIME type for the copied data.
    const val MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact"
    

    Java

    // Declares the base URI string.
    private static final String CONTACTS = "content://com.example.contacts";
    
    // Declares a path string for URIs that you use to copy data.
    private static final String COPY_PATH = "/copy";
    
    // Declares a MIME type for the copied data.
    public static final String MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact";
    
  2. Trong hoạt động mà người dùng sao chép dữ liệu, hãy thiết lập đoạn mã để sao chép dữ liệu vào bảng nhớ tạm. Để phản hồi một yêu cầu sao chép, hãy đặt URI vào bảng nhớ tạm.

    Kotlin

    class MyCopyActivity : Activity() {
        ...
    when(item.itemId) {
        R.id.menu_copy -> { // The user has selected a name and is requesting a copy.
            // Appends the last name to the base URI.
            // The name is stored in "lastName".
            uriString = "$CONTACTS$COPY_PATH/$lastName"
    
            // Parses the string into a URI.
            val copyUri: Uri? = Uri.parse(uriString)
    
            // Gets a handle to the clipboard service.
            val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    
            val clip: ClipData = ClipData.newUri(contentResolver, "URI", copyUri)
    
            // Sets the clipboard's primary clip.
            clipboard.setPrimaryClip(clip)
        }
    }
    

    Java

    public class MyCopyActivity extends Activity {
        ...
    // The user has selected a name and is requesting a copy.
    case R.id.menu_copy:
    
        // Appends the last name to the base URI.
        // The name is stored in "lastName".
        uriString = CONTACTS + COPY_PATH + "/" + lastName;
    
        // Parses the string into a URI.
        Uri copyUri = Uri.parse(uriString);
    
        // Gets a handle to the clipboard service.
        ClipboardManager clipboard = (ClipboardManager)
            getSystemService(Context.CLIPBOARD_SERVICE);
    
        ClipData clip = ClipData.newUri(getContentResolver(), "URI", copyUri);
    
        // Sets the clipboard's primary clip.
        clipboard.setPrimaryClip(clip);
    
  3. Trong phạm vi toàn cục của nhà cung cấp nội dung, hãy tạo trình so khớp URI và thêm mẫu URI khớp với URI mà bạn đặt trên bảng nhớ tạm.

    Kotlin

    // A Uri Match object that simplifies matching content URIs to patterns.
    private val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
    
        // Adds a matcher for the content URI. It matches.
        // "content://com.example.contacts/copy/*"
        addURI(CONTACTS, "names/*", GET_SINGLE_CONTACT)
    }
    
    // An integer to use in switching based on the incoming URI pattern.
    private const val GET_SINGLE_CONTACT = 0
    ...
    class MyCopyProvider : ContentProvider() {
        ...
    }
    

    Java

    public class MyCopyProvider extends ContentProvider {
        ...
    // A Uri Match object that simplifies matching content URIs to patterns.
    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    
    // An integer to use in switching based on the incoming URI pattern.
    private static final int GET_SINGLE_CONTACT = 0;
    ...
    // Adds a matcher for the content URI. It matches
    // "content://com.example.contacts/copy/*"
    sUriMatcher.addURI(CONTACTS, "names/*", GET_SINGLE_CONTACT);
    
  4. Thiết lập phương thức query(). Phương thức này có thể xử lý nhiều mẫu URI, tuỳ thuộc vào cách bạn mã hoá, nhưng chỉ mẫu dùng cho thao tác sao chép vào bảng nhớ tạm mới xuất hiện.

    Kotlin

    // Sets up your provider's query() method.
    override fun query(
            uri: Uri,
            projection: Array<out String>?,
            selection: String?,
            selectionArgs: Array<out String>?,
            sortOrder: String?
    ): Cursor? {
        ...
        // When based on the incoming content URI:
        when(sUriMatcher.match(uri)) {
    
            GET_SINGLE_CONTACT -> {
    
                // Queries and returns the contact for the requested name. Decodes
                // the incoming URI, queries the data model based on the last name,
                // and returns the result as a Cursor.
            }
        }
        ...
    }
    

    Java

    // Sets up your provider's query() method.
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
        String sortOrder) {
        ...
        // Switch based on the incoming content URI.
        switch (sUriMatcher.match(uri)) {
    
        case GET_SINGLE_CONTACT:
    
            // Queries and returns the contact for the requested name. Decodes the
            // incoming URI, queries the data model based on the last name, and
            // returns the result as a Cursor.
        ...
    }
    
  5. Thiết lập phương thức getType() để trả về một loại MIME thích hợp cho dữ liệu được sao chép:

    Kotlin

    // Sets up your provider's getType() method.
    override fun getType(uri: Uri): String? {
        ...
        return when(sUriMatcher.match(uri)) {
            GET_SINGLE_CONTACT -> MIME_TYPE_CONTACT
            ...
        }
    }
    

    Java

    // Sets up your provider's getType() method.
    public String getType(Uri uri) {
        ...
        switch (sUriMatcher.match(uri)) {
        case GET_SINGLE_CONTACT:
            return (MIME_TYPE_CONTACT);
        ...
        }
    }
    

Phần Dán dữ liệu từ URI nội dung mô tả cách lấy URI nội dung từ bảng nhớ tạm rồi sử dụng URI đó để lấy và dán dữ liệu.

Sao chép luồng dữ liệu

Bạn có thể sao chép và dán lượng lớn văn bản và dữ liệu tệp nhị phân dưới dạng luồng. Dữ liệu có thể có các dạng như sau:

  • Tệp được lưu trữ trên thiết bị thực
  • Luồng từ socket
  • Một lượng lớn dữ liệu được lưu trữ trong hệ thống cơ sở dữ liệu cơ bản của nhà cung cấp

Trình cung cấp nội dung cho luồng dữ liệu sẽ cấp quyền truy cập vào dữ liệu bằng đối tượng chỉ số mô tả tệp, chẳng hạn như AssetFileDescriptor, thay vì đối tượng Cursor. Ứng dụng dán sẽ đọc luồng dữ liệu bằng cách sử dụng chỉ số mô tả tệp này.

Để thiết lập ứng dụng của bạn cho việc sao chép luồng dữ liệu với nhà cung cấp, hãy làm theo các bước sau:

  1. Thiết lập URI nội dung cho luồng dữ liệu mà bạn đang đặt vào bảng nhớ tạm. Các phương án để thực hiện việc này bao gồm:
    • Mã hoá giá trị nhận dạng cho luồng dữ liệu vào URI, như mô tả trong phần Mã hoá giá trị nhận dạng trên URI, sau đó duy trì một bảng trong trình cung cấp của bạn chứa các giá trị nhận dạng và tên luồng tương ứng.
    • Mã hoá tên luồng trực tiếp trên URI.
    • Sử dụng một URI độc nhất luôn trả về luồng hiện tại từ nhà cung cấp. Nếu bạn sử dụng tuỳ chọn này, hãy nhớ cập nhật nhà cung cấp để trỏ đến một luồng khác bất cứ khi nào bạn sao chép luồng vào bảng nhớ tạm bằng URI.
  2. Cung cấp một loại MIME cho mỗi loại luồng dữ liệu mà bạn định cung cấp. Các ứng dụng dán cần thông tin này để xác định xem có thể dán dữ liệu vào bảng nhớ tạm hay không.
  3. Triển khai một trong các phương thức ContentProvider trả về chỉ số mô tả tệp cho một luồng. Nếu bạn mã hoá giá trị nhận dạng trên URI nội dung, hãy sử dụng phương thức này để xác định luồng nào cần mở.
  4. Để sao chép luồng dữ liệu vào bảng nhớ tạm, hãy tạo URI nội dung và đặt URI đó vào bảng nhớ tạm.

Để dán luồng dữ liệu, ứng dụng sẽ lấy đối tượng clip từ bảng nhớ tạm, nhận URI rồi sử dụng URI đó trong lệnh gọi phương thức chỉ số mô tả tệp ContentResolver để mở luồng. Phương thức ContentResolver gọi phương thức ContentProvider tương ứng, chuyển phương thức đó URI nội dung. Nhà cung cấp của bạn trả về chỉ số mô tả tệp cho phương thức ContentResolver. Sau đó, ứng dụng dán có trách nhiệm đọc dữ liệu qua luồng.

Danh sách sau đây liệt kê các phương thức chỉ số mô tả tệp quan trọng nhất đối với nhà cung cấp nội dung. Mỗi phương thức này đều có một phương thức ContentResolver tương ứng, trong đó chuỗi "Mô tả" được thêm vào tên phương thức. Ví dụ: đầu ra tương tự ContentResolver của openAssetFile()openAssetFileDescriptor().

openTypedAssetFile()

Phương thức này trả về một chỉ số mô tả tệp tài sản, nhưng chỉ khi loại MIME đã cung cấp được nhà cung cấp hỗ trợ. Phương thức gọi (ứng dụng thực hiện thao tác dán) cung cấp một mẫu loại MIME. Nhà cung cấp nội dung của ứng dụng sao chép URI vào bảng nhớ tạm sẽ trả về tên người dùng tệp AssetFileDescriptor nếu có thể cung cấp loại MIME đó và gửi ngoại lệ nếu không thể.

Phương thức này xử lý các mục con trong tệp. Bạn có thể dùng đối tượng này để đọc các tài sản mà nhà cung cấp nội dung đã sao chép vào bảng nhớ tạm.

openAssetFile()
Phương thức này là một dạng openTypedAssetFile() chung hơn. Phương thức này không lọc các loại MIME được phép, nhưng có thể đọc các mục con trong tệp.
openFile()
Đây là một dạng thức chung của openAssetFile(). Không thể đọc các phần phụ của tệp.

Bạn có thể tuỳ ý sử dụng phương thức openPipeHelper() cùng với phương thức chỉ số mô tả tệp. Điều này cho phép ứng dụng dán đọc dữ liệu luồng trong một luồng ở chế độ nền bằng một ống dẫn. Để sử dụng phương thức này, hãy triển khai giao diện ContentProvider.PipeDataWriter.

Thiết kế chức năng sao chép và dán hiệu quả

Để thiết kế chức năng sao chép và dán hiệu quả cho ứng dụng của bạn, hãy nhớ những điểm sau:

  • Tại bất kỳ thời điểm nào, bảng nhớ tạm chỉ chứa một đối tượng clip duy nhất. Thao tác sao chép mới của bất kỳ ứng dụng nào trong hệ thống sẽ ghi đè đối với đoạn video trước đó. Vì người dùng có thể di chuyển khỏi ứng dụng của bạn và sao chép trước khi quay lại, nên bạn không thể giả định rằng bảng nhớ tạm chứa đoạn video mà người dùng đã sao chép trước đó trong ứng dụng của bạn.
  • Mục đích của nhiều đối tượng ClipData.Item cho mỗi đoạn video là hỗ trợ việc sao chép và dán nhiều lựa chọn thay vì các dạng tham chiếu khác nhau đến một lựa chọn duy nhất. Thường thì bạn muốn mọi đối tượng ClipData.Item trong một đoạn video có cùng một dạng. Nghĩa là, tất cả đều phải là văn bản, URI nội dung hoặc Intent đơn giản và không được kết hợp với nhau.
  • Khi cung cấp dữ liệu, bạn có thể biểu diễn các loại MIME khác nhau. Thêm các loại MIME mà bạn hỗ trợ vào ClipDescription, sau đó triển khai các loại MIME trong trình cung cấp nội dung của bạn.
  • Khi bạn nhận được dữ liệu từ bảng nhớ tạm, ứng dụng của bạn chịu trách nhiệm kiểm tra loại MIME có sẵn rồi quyết định sử dụng loại MIME nào (nếu có). Ngay cả khi có một đối tượng cắt trên bảng nhớ tạm và người dùng yêu cầu dán, thì ứng dụng của bạn không bắt buộc phải thực hiện thao tác dán đó. Hãy dán nếu loại MIME đó tương thích. Bạn có thể chuyển đổi dữ liệu trên bảng nhớ tạm thành văn bản bằng coerceToText(). Nếu ứng dụng của bạn hỗ trợ nhiều loại MIME có sẵn, bạn có thể cho phép người dùng chọn loại MIME.