12

Tôi có một số tệp lớn (hình ảnh và video) mà tôi cần lưu trữ trong nhà cung cấp nội dung. Các tài liệu android chỉ ...Làm cách nào để lưu trữ các đốm màu lớn trong nhà cung cấp nội dung android?

Nếu bạn đang phơi bày dữ liệu byte đó là quá lớn để đưa vào bảng riêng của mình - như một file bitmap lớn - các lĩnh vực đó cho thấy nhiều dữ liệu cho khách hàng nên thực sự chứa nội dung: URI chuỗi. Đây là trường cung cấp cho khách hàng truy cập vào tệp dữ liệu. Bản ghi cũng phải có một trường khác, có tên "_data" liệt kê chính xác đường dẫn tệp trên thiết bị cho tệp đó. Trường này không được đọc bởi khách hàng , nhưng theo số Trình phân giải nội dung. Khách hàng sẽ gọi số ContentResolver.openInputStream() trên trường hướng đến người dùng đang giữ URI cho mặt hàng đó. ContentResolver sẽ yêu cầu trường "_data" cho bản ghi và vì nó có quyền cao hơn so với khách hàng, nên có thể truy cập tệp đó trực tiếp và trả lại tệp đính kèm cho tệp cho khách hàng. - http://developer.android.com/guide/topics/providers/content-providers.html#creating

Tôi gặp khó khăn khi tìm một ví dụ. Cụ thể là tôi muốn sử dụng bitmap trong ngữ cảnh một ImageView. Xem xét các mã bán đoạn mã sau (nó không hoạt động) ...

ImageView iv = .... 
String iconUri = cursor.getString(cursor.getColumnIndex(Table.ICON)); 
iv.setImageURI(Uri.parse(iconUri)); 

Quan sát/vấn đề ...

  1. Làm thế nào có thể lưu trữ/thu hồi uri được tái tạo một cách chính xác? (đó là văn bản trong bảng)
  2. Việc triển khai setImageURI làm cho việc sử dụng nội dung giải quyết openInputStream để điều này có hiệu quả.

    String scheme = mUri.getScheme(); 
    ... 
    } else if (ContentResolver.SCHEME_CONTENT.equals(scheme) 
         || ContentResolver.SCHEME_FILE.equals(scheme)) { 
        try { 
        d = Drawable.createFromStream(
          mContext.getContentResolver().openInputStream(mUri), 
          null); 
    

    --frameworks/cơ sở/lõi/java/android/widget/ImageView.java

tôi nhận nó làm việc. Tôi lấy một gợi ý từ MediaStore và MediaProvider. Các tệp có chứa dữ liệu được đặt tên dựa trên nhà cung cấp nội dung (thư mục), tên cột, id hàng và loại phương tiện. Các phân giải nội dung sau đó mua lại các bộ mô tả tập tin như vậy ...

Uri iconUri = Uri.withAppendedPath(Table.getUri(cursor), Table.ICON); 
ib.setImageURI(iconUri); 

... và các nhà cung cấp nội dung trả lời bằng hiện vật ...

@Override 
public ParcelFileDescriptor openFile (Uri uri, String mode) { 
int imode = 0; 
if (mode.contains("w")) imode |= ParcelFileDescriptor.MODE_WRITE_ONLY; 
if (mode.contains("r")) imode |= ParcelFileDescriptor.MODE_READ_ONLY; 
if (mode.contains("+")) imode |= ParcelFileDescriptor.MODE_APPEND; 
List<String> pseg = uri.getPathSegments(); 
if (pseg.size() < 3) return null; 

try { 
    File filePath = filePathFromRecord(pseg.get(2), pseg.get(1)); 
    return ParcelFileDescriptor.open(filePath, imode); 
} catch (FileNotFoundException e) { 
    e.printStackTrace(); 
} 
return null; 
} 
+0

Oops! Tôi trả lời câu hỏi của riêng tôi đó là "Làm thế nào để lưu trữ các đốm lớn trong và nhà cung cấp nội dung Android" bằng cách chỉnh sửa văn bản gốc hơn là đăng câu trả lời. Trong mọi trường hợp, câu trả lời ngắn gọn là bạn sử dụng openFile và bạn bè theo cách được mô tả. – phreed

Trả lời

11

Phreed giải pháp đưa ra trong nửa dưới của câu hỏi về cơ bản là chính xác. Tôi sẽ cố gắng thêm một số chi tiết tại đây.

Khi bạn làm getContentResolver().openInputStream(...), trình phân giải nội dung sẽ chuyển đến nhà cung cấp nội dung của bạn và gọi phương thức openFile của nó. Đây là cách openFile trông trong ContentProvider.java:

public ParcelFileDescriptor openFile(Uri uri, String mode) 
    throws FileNotFoundException { 
throw new FileNotFoundException("No files supported by provider at " 
     + uri); 
} 

Vì vậy, điều này giải thích nơi "Không có tập tin được hỗ trợ ..." lỗi chính xác đến từ! Bạn nhận được xung quanh điều này bằng cách ghi đè phương thức openFile trong lớp con của bạn và cung cấp việc triển khai của riêng bạn. Nó gọn gàng: bạn có quyền kiểm soát hoàn hảo nơi tệp của bạn được đặt khi bất kỳ khách hàng nào thực hiện openInputStream hoặc openOutputStream.

Mẫu mã trong câu hỏi của tín hiệu đưa ra gợi ý cách triển khai có thể trông như thế nào. Đây là phiên bản hơi sửa đổi của tôi, nó cũng tạo ra các thư mục và tập tin khi cần thiết. Tôi mới làm quen với công cụ này vì vậy đây có thể không phải là cách tối ưu để làm mọi thứ, nhưng nó đưa ra một ý tưởng. Đối với một điều, nó có lẽ nên kiểm tra xem lưu trữ bên ngoài có sẵn.

@Override 
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { 
    File root = new File(Environment.getExternalStorageDirectory(), 
      "/Android/data/com.example.myapp/cache"); 
    root.mkdirs(); 
    File path = new File(root, uri.getEncodedPath()); 
    // So, if the uri was content://com.example.myapp/some/data.xml, 
    // we'll end up accessing /Android/data/com.example.myapp/cache/some/data.xml 

    int imode = 0; 
    if (mode.contains("w")) { 
      imode |= ParcelFileDescriptor.MODE_WRITE_ONLY; 
      if (!path.exists()) { 
       try { 
        path.createNewFile(); 
       } catch (IOException e) { 
        // TODO decide what to do about it, whom to notify... 
        e.printStackTrace(); 
       } 
      } 
    } 
    if (mode.contains("r")) imode |= ParcelFileDescriptor.MODE_READ_ONLY; 
    if (mode.contains("+")) imode |= ParcelFileDescriptor.MODE_APPEND;   

    return ParcelFileDescriptor.open(path, imode); 
} 
+0

Xin chào @Peteris Caune.Tôi đã thực hiện phương thức openFile như bạn, nhưng tôi vẫn không tìm ra cách để writte tệp của mình (*. Png) bằng cách sử dụng đối tượng ParcelFileDescriptor từ openFileMethod(), và bây giờ trường '' _data "' được điền như thế nào. Tôi đã dành nhiều thời gian tìm kiếm một ví dụ hoặc giải thích về. cảm ơn – AlexSanchez

+0

tôi tự giải quyết, có vẻ như trường '_data' là độc quyền cho việc sử dụng của nhà cung cấp, vì vậy tôi đã cố gắng tìm nạp dữ liệu như một trường thông thường trong con trỏ. Tôi đã nhận thấy tôi cần sử dụng phương thức 'mResolver.openInputStream (resourceUri);' thay vào đó. btw cảm ơn. – AlexSanchez

0

Có một vấn đề ghi tập tin mặc dù. Làm thế nào để các nhà cung cấp nội dung biết sau đó viết hoàn thành?

Khi đóng một OutputStream thu được thông qua một ContentResolver.openOutputStream() Tôi muốn cho (tương ứng) tuỳ chỉnh ContentProvider để thực hiện một số hành động tùy chỉnh. Hiện tại tôi đang sử dụng tạo một FileObserver trên temporary file nhưng có vẻ như cách thực hiện sai. Suy nghĩ đầu tiên của tôi là phân loại ParcelFileDescriptor được sản xuất theo phương pháp ContentProvider.openFile() nhưng điều đó dường như không hiệu quả đối với tôi.

Điều đó được cho là có một số lỗi dẫn đến việc sử dụng MyParcelFileDescriptor.

  • Tôi không đảm bảo rằng bộ mô tả tệp sẽ không được thu thập rác sớm.
  • Tôi không cố gắng để trọng cuối cùng() (tài liệu dường như cho thấy là nơi để làm điều này nhưng close() dường như là lựa chọn đúng đắn với tôi.

dưới , có sự tương tác nào giữa các đối tượng tệp như được hiển thị bởi ContentResolver hay không và các đối tượng trong số ContentProvider?

+0

Có sự cố khi ghi tệp. Làm thế nào để các nhà cung cấp nội dung biết sau đó viết hoàn thành? http://stackoverflow.com/questions/4147763/android-content-provider-custom-openfile-onclose-behavior – phreed

+0

[tài liệu] (http://developer.android.com/reference/android/content/ContentProvider.html #openFile (android.net.Uri,% 20java.lang.String)) nói: * ... ParcelFileDescriptor được trả về thuộc sở hữu của người gọi, do đó, trách nhiệm của họ là đóng nó khi hoàn thành. Nghĩa là, việc thực hiện phương thức này sẽ tạo một ParcelFileDescriptor mới cho mỗi cuộc gọi. * Vì vậy, bạn không cần phải lo lắng về việc đóng cá thể 'ParcelFileDescriptor'. –

1

Android cung cấp phương thức trợ giúp openFileHelper() giúp thực hiện phương thức openFile() rất dễ dàng. Tất cả những gì bạn phải làm, để sử dụng phương thức này, là cung cấp vị trí của tệp trong một cột có tên “_data”.

@Override 
public ParcelFileDescriptor openFile(Uri uri, String mode) 
     throws FileNotFoundException { 
    if (URI_MATCHER.match(uri) != PHOTO_ID) { 
     throw new IllegalArgumentException 
      ("URI invalid. Use an id-based URI only."); 
    } 
    return openFileHelper(uri, mode); 
} 

Click here for detail