2010-09-28 13 views
87

Tôi đang xây dựng một ứng dụng nơi tôi có một bảng cho các sự kiện và một bảng cho các địa điểm. Tôi muốn có thể cấp cho các ứng dụng khác quyền truy cập vào dữ liệu này. Tôi có một vài câu hỏi liên quan đến các phương pháp hay nhất cho loại vấn đề này.Các phương pháp hay nhất để hiển thị nhiều bảng bằng cách sử dụng các nhà cung cấp nội dung trong Android

  1. Làm thế nào tôi nên cấu trúc các lớp cơ sở dữ liệu? Tôi hiện đang có các lớp cho EventsDbAdapter và VenuesDbAdapter, cung cấp logic cho truy vấn mỗi bảng, trong khi có một DbManager riêng biệt (mở rộng SQLiteOpenHelper) để quản lý các phiên bản cơ sở dữ liệu, tạo/nâng cấp cơ sở dữ liệu, truy cập cơ sở dữ liệu (getWriteable/ReadeableDatabase). Đây có phải là giải pháp được đề xuất, hay tôi sẽ tốt hơn hoặc là hợp nhất mọi thứ vào một lớp (ví dụ: DbManager) hoặc tách mọi thứ và cho phép mỗi Adapter mở rộng SQLiteOpenHelper?

  2. Tôi nên thiết kế nhà cung cấp nội dung cho nhiều bảng như thế nào? Mở rộng câu hỏi trước, tôi có nên sử dụng một Nhà cung cấp nội dung cho toàn bộ ứng dụng hay tôi nên tạo các nhà cung cấp riêng cho Sự kiện và Địa điểm?

Hầu hết các ví dụ tôi chỉ tìm thấy đối phó với các ứng dụng bảng đơn, vì vậy tôi sẽ đánh giá cao bất kỳ con trỏ nào ở đây.

Trả lời

10

Tôi khuyên bạn nên kiểm tra mã nguồn dành cho Nhà cung cấp dịch vụ liên hệ Android 2.x. (Mà có thể được tìm thấy trực tuyến). Họ xử lý các truy vấn bảng chéo bằng cách cung cấp các khung nhìn chuyên biệt mà sau đó bạn chạy các truy vấn dựa vào phần cuối. Trên giao diện người dùng, người dùng có thể truy cập thông qua các URI khác nhau thông qua một nhà cung cấp nội dung duy nhất. Có thể bạn cũng muốn cung cấp một hoặc hai lớp để giữ các hằng số cho các tên trường bảng và chuỗi URI của bạn. Các lớp này có thể được cung cấp dưới dạng một API bao gồm hoặc là một lớp trong lớp, và sẽ làm cho ứng dụng tiêu thụ dễ sử dụng hơn nhiều.

Đó là một chút phức tạp, vì vậy bạn cũng có thể muốn kiểm tra cách lịch cũng như để có được một ý tưởng về những gì bạn làm và không cần.

Bạn chỉ cần một bộ điều hợp DB và một nhà cung cấp nội dung cho mỗi cơ sở dữ liệu (không phải trên mỗi bảng) để thực hiện hầu hết công việc, nhưng bạn có thể sử dụng nhiều bộ điều hợp/nhà cung cấp nếu bạn thực sự muốn. Nó chỉ làm cho mọi thứ phức tạp hơn một chút.

+5

com.android.providers.contacts.ContactsProvider2.java https://github.com/android/platform_packages_providers_contactsprovider/blob/master/src/com/android/providers/contacts/ContactsProvider2.java – Alex

+0

@ Cảm ơn Marloke. Ok, tôi hiểu rằng ngay cả nhóm Android cũng sử dụng giải pháp 'switch', nhưng phần này bạn đã đề cập:' Họ xử lý các truy vấn bảng chéo bằng cách cung cấp các khung nhìn chuyên biệt mà bạn chạy truy vấn ngược lại. Trên giao diện người dùng, người dùng có thể truy cập thông qua các URI khác nhau thông qua một nhà cung cấp nội dung duy nhất'. Bạn có nghĩ rằng bạn có thể giải thích chi tiết hơn một chút không? – eddy

112

Có thể hơi muộn đối với bạn, nhưng những người khác có thể thấy điều này hữu ích.

Trước tiên, bạn cần phải tạo ra nhiều CONTENT_URIs

public static final Uri CONTENT_URI1 = 
    Uri.parse("content://"+ PROVIDER_NAME + "/sampleuri1"); 
public static final Uri CONTENT_URI2 = 
    Uri.parse("content://"+ PROVIDER_NAME + "/sampleuri2"); 

Sau đó, bạn mở rộng URI của bạn Matcher

private static final UriMatcher uriMatcher; 
static { 
    uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 
    uriMatcher.addURI(PROVIDER_NAME, "sampleuri1", SAMPLE1); 
    uriMatcher.addURI(PROVIDER_NAME, "sampleuri1/#", SAMPLE1_ID);  
    uriMatcher.addURI(PROVIDER_NAME, "sampleuri2", SAMPLE2); 
    uriMatcher.addURI(PROVIDER_NAME, "sampleuri2/#", SAMPLE2_ID);  
} 

Sau đó tạo bảng của bạn

private static final String DATABASE_NAME = "sample.db"; 
private static final String DATABASE_TABLE1 = "sample1"; 
private static final String DATABASE_TABLE2 = "sample2"; 
private static final int DATABASE_VERSION = 1; 
private static final String DATABASE_CREATE1 = 
    "CREATE TABLE IF NOT EXISTS " + DATABASE_TABLE1 + 
    " (" + _ID1 + " INTEGER PRIMARY KEY AUTOINCREMENT," + 
    "data text, stuff text);"; 
private static final String DATABASE_CREATE2 = 
    "CREATE TABLE IF NOT EXISTS " + DATABASE_TABLE2 + 
    " (" + _ID2 + " INTEGER PRIMARY KEY AUTOINCREMENT," + 
    "data text, stuff text);"; 

Đừng quên để thêm thứ hai DATABASE_CREATE đến onCreate()

Bạn sẽ sử dụng khối chuyển đổi trường hợp để xác định bảng nào được sử dụng.Đây là mã chèn của tôi

@Override 
public Uri insert(Uri uri, ContentValues values) { 
    Uri _uri = null; 
    switch (uriMatcher.match(uri)){ 
    case SAMPLE1: 
     long _ID1 = db.insert(DATABASE_TABLE1, "", values); 
     //---if added successfully--- 
     if (_ID1 > 0) { 
      _uri = ContentUris.withAppendedId(CONTENT_URI1, _ID1); 
      getContext().getContentResolver().notifyChange(_uri, null);  
     } 
     break; 
    case SAMPLE2: 
     long _ID2 = db.insert(DATABASE_TABLE2, "", values); 
     //---if added successfully--- 
     if (_ID2 > 0) { 
      _uri = ContentUris.withAppendedId(CONTENT_URI2, _ID2); 
      getContext().getContentResolver().notifyChange(_uri, null);  
     } 
     break; 
    default: throw new SQLException("Failed to insert row into " + uri); 
    } 
    return _uri;     
} 

Bạn sẽ cần phải chia lên delete, update, getType vv Bất cứ nơi nào cung cấp dịch vụ của bạn gọi cho DATABASE_TABLE hoặc CONTENT_URI bạn sẽ thêm một trường hợp và có DATABASE_TABLE1 hoặc CONTENT_URI1 trong một và # 2 trong lần tiếp theo và cứ tiếp tục cho bao nhiêu tùy thích.

+1

Cảm ơn câu trả lời của bạn, điều này khá gần với giải pháp mà tôi đã sử dụng. Tôi thấy rằng các nhà cung cấp phức tạp làm việc với một số bảng nhận được rất nhiều chuyển đổi-báo cáo, mà không có vẻ như tất cả những thanh lịch. Nhưng tôi hiểu đó là cách mà hầu hết mọi người làm. –

+0

Là notifyChange thực sự phải sử dụng _uri và không phải là uri gốc? – span

+16

Đây có phải là tiêu chuẩn được chấp nhận với Android không? Nó hoạt động, rõ ràng, nhưng có vẻ hơi "clunky". – prolink007

7

Một ContentProvider có thể phục vụ nhiều bảng, nhưng chúng phải liên quan phần nào. Nó sẽ tạo sự khác biệt nếu bạn định đồng bộ hóa nhà cung cấp của mình. Nếu bạn muốn đồng bộ hóa riêng biệt, hãy nói Danh bạ, Thư hoặc Lịch, bạn sẽ cần các nhà cung cấp khác nhau cho mỗi người, ngay cả khi họ kết thúc trong cùng một cơ sở dữ liệu hoặc được đồng bộ hóa với cùng một dịch vụ, vì Bộ điều hợp đồng bộ được gắn trực tiếp với một nhà cung cấp cụ thể.

Theo như tôi có thể nói, bạn chỉ có thể sử dụng một SQLiteOpenHelper duy nhất cho mỗi cơ sở dữ liệu, vì nó lưu trữ thông tin meta của nó trong một bảng trong cơ sở dữ liệu. Vì vậy, nếu ContentProviders của bạn truy cập vào cùng một cơ sở dữ liệu, bạn sẽ phải chia sẻ theo cách nào đó của Trình trợ giúp.

6

Lưu ý: Đây là giải thích/sửa đổi cho câu trả lời do Opy cung cấp.

Cách tiếp cận này subdivides mỗi insert, delete, update, và getType phương pháp với báo cáo công tắc để xử lý mỗi bảng cá nhân của bạn. Bạn sẽ sử dụng một CASE để xác định mỗi bảng (hoặc uri) được tham chiếu. Mỗi CASE sau đó ánh xạ tới một trong các bảng hoặc URI của bạn. Ví dụ: TABLE1 hoặc URI1 được chọn trong TRƯỜNG HỢP # 1, v.v ... cho tất cả các bảng mà ứng dụng của bạn sử dụng.

Đây là ví dụ về cách tiếp cận này. Đây là phương pháp chèn. Nó được thực hiện một chút khác nhau từ Opy nhưng thực hiện chức năng tương tự. Bạn có thể chọn kiểu bạn thích. Tôi cũng muốn chắc chắn chèn trả về một giá trị ngay cả khi chèn bảng không thành công. Trong trường hợp đó, nó trả về một số -1.

@Override 
    public Uri insert(Uri uri, ContentValues values) { 
    int uriType = sURIMatcher.match(uri); 
    SQLiteDatabase sqlDB; 

    long id = 0; 
    switch (uriType){ 
     case TABLE1: 
      sqlDB = Table1Database.getWritableDatabase(); 
      id = sqlDB.insert(Table1.TABLE_NAME, null, values); 
      getContext().getContentResolver().notifyChange(uri, null); 
      return Uri.parse(BASE_PATH1 + "/" + id); 
     case TABLE2: 
      sqlDB = Table2Database.getWritableDatabase(); 
      id = sqlDB.insert(Table2.TABLE_NAME, null, values); 
      getContext().getContentResolver().notifyChange(uri, null); 
      return Uri.parse(BASE_PATH2 + "/" + id); 
     default: 
      throw new SQLException("Failed to insert row into " + uri); 
      return -1; 
    }  
    } // [END insert] 
Các vấn đề liên quan