11.1B: Menambahkan penyedia konten ke database Anda.

Daftar Isi:

Penyedia konten di aplikasi nyata lebih kompleks dari versi dasar yang Anda bangun di praktik sebelumnya.

Di dunia nyata:

  • Backend adalah database, sistem file, atau opsi storage persisten lainnya.
  • Front-end menampilkan data pada UI yang nyaman dilihat dan memungkinkan pengguna mengubah data.

Anda akan jarang membuat aplikasi dari awal. Sering kali, Anda akan melakukan debug, mengoptimalkan, atau memperluas aplikasi yang sudah ada.

Dalam praktik ini, Anda akan memeriksa aplikasi WordListSQL dan memperluasnya untuk menggunakan penyedia konten sebagai layer antara database SQL dan RecyclerView.

Anda bisa melakukannya dengan dua cara.

  • Mengoptimalkan dan memperluas aplikasi WordListSQL. Ini akan mencakup mengubah arsitektur aplikasi dan mengoptimalkan kode.
  • Mulai dari awal dan gunakan kembali kode dari WordListSQL dan MinimalistContentProvider.

Praktik ini akan memperagakan cara mengoptimalkan aplikasi WordListSQL yang sudah ada karena ini yang kemungkinan besar Anda temui di pekerjaan.

Yang harus sudah Anda KETAHUI

Untuk praktik ini, Anda harus sudah mengenal cara:

  • Menampilkan data di RecyclerView.
  • Memulai dan mengembalikan dari Aktivitas.
  • Membuat, mengubah, dan berinteraksi dengan database SQLite menggunakan SQLiteOpenHelper.
  • Memahami arsitektur penyedia konten minimal yang sudah Anda bangun di praktik sebelumnya.

Yang akan Anda PELAJARI

Anda akan mempelajari cara:

  • Membuat penyedia konten yang sepenuhnya dikembangkan untuk aplikasi yang sudah ada.
  • Mengoptimalkan aplikasi untuk mengakomodasi penyedia konten.

Yang akan Anda LAKUKAN

Praktik ini memerlukan penyiapan yang lebih mirip dengan development aplikasi di dunia nyata.

Mulai dengan aplikasi WordListSQLInteractive yang sudah dibuat di praktik sebelumnya, yang menampilkan kata dari database SQLite di RecyclerView, dan pengguna bisa membuat, mengedit, dan menghapus kata.

Anda akan memperluas dan memodifikasi aplikasi ini:

  • Mengimplementasikan kelas Contract untuk mengekspos antarmuka aplikasi anda ke aplikasi lain.
  • Mengimplementasikan ContentProvider dan melakukan kueri menggunakan ContentResolver.
  • Mengoptimalkan kelas MainActivity, WordListAdapter, dan WordListOpenHelper untuk bekerja dengan penyedia konten.

Ringkasan Aplikasi

Aplikasi WordListSQLWithContentProvider akan memiliki fitur berikut:

  • Penyedia konten yang bisa menyisipkan, menghapus, memperbarui, dan melakukan kueri pada database.
  • Sebuah Contract dan rangkaian izin yang memungkinkan aplikasi lain untuk mengakses penyedia konten ini.
  • Resolver konten yang berinteraksi dengan penyedia konten untuk menyisipkan, menghapus, memperbarui, dan melakukan kueri data.
  • Antarmuka pengguna dan fungsionalitas yang tidak berubah.

Aplikasi Anda akan terlihat sama seperti di akhir praktik storage data. Layar aplikasi final

Ringkasan komponen aplikasi

Diagram berikut menampilkan ringkasan komponen aplikasi yang menggunakan SQLiteDatabase dengan penyedia konten. Satu-satunya perbedaan dari aplikasi penyedia konten minimal adalah penyedia konten mengambil data dari database melalui helper yang terbuka. Ringkasan komponen aplikasi.

Diagram berikut menampilkan arsitektur aplikasi WorldListSQLInteractive dengan penyedia konten yang ditambahkan; ini adalah aplikasi WordListSQLWithContentProvider yang akan Anda bangun di praktik ini.

Komponen aplikasi WordListSQLWithContentProvider yang telah selesai.

Lihat bab konsep untuk penjelasan yang detail mengenai semua komponen dan cara interaksinya.

Ringkasan perubahan

Berikut adalah ringkasan perubahan yang akan Anda buat pada WordListInteractive untuk menambahkan penyedia konten.

  • Kelas Baru: Contract, ContentProvider, ContentResolver
  • Kelas yang berubah: WordListOpenHelper, MainActivity, WordListAdapter
  • Kelas yang harus tidak berubah: WordItem, MyButtonOnClickListener, ViewHolder

Tugas 1. Mengunduh dan menjalankan kode dasar

Praktik ini melanjutkan aplikasi WordListSQLInteractive dan MinimalistContentProvider yang telah Anda buat sebelumnya. Anda akan memperluas salin WordListSQLInteractive. Anda bisa memulai dari kode Anda sendiri atau mengunduh aplikasi.

Tugas 2. Menambahkan kelas Contract ke WordListSQLInteractive

Mulai dengan membuat kelas Contract yang mendefinisikan konstanta database publik, konstanta URI, dan tipe MIME. Anda akan menggunakan konstanta ini di semua kelas lainnya. Bagian pertama yang diimplementasikan adalah kelas Contract.

2.1 Tambahkan kelas Contract

  1. Pelajari dokumentasi Mendefinisikan Skema dan Kontrak.
  2. Tambahkan kelas final publik ke proyek Anda dan beri nama Contract.

    Kelas Contract ini berisi semua informasi yang diperlukan aplikasi lain untuk menggunakan penyedia konten aplikasi Anda. Anda bisa memberi nama apa pun pada kelas ini, tetapi umumnya disebut sebagai "Contract".

    public final class Contract {}
    
  3. Agar kelas Contract tidak dibuat instance-nya, tambahkan constructor pribadi yang kosong.

    Ini adalah pola standar untuk kelas yang digunakan untuk menampung informasi meta dan konstanta untuk aplikasi.

    private Contract() {}
    

2.2 Pindahkan konstanta database ke dalam Contract

Pindahkan konstanta untuk database sehingga aplikasi lain perlu mengetahui di luar WordListOpenHelper ke dalam Contract dan membuatnya publik.

  1. Pindahkan DATABASE_NAME dan buat menjadi publik.

    public static final String DATABASE_NAME = "wordlist";
    

    Buat kelas dalam abstrak statis untuk setiap tabel dengan nama kolom. Kelas dalam yang baru ini biasanya mengimplementasikan antarmuka BaseColumns. Dengan mengimplementasikan antarmuka BaseColumns, kelas Anda bisa mewarisi bidang kunci utama yang disebut sebagai _ID yang diharapkan ada oleh beberapa kelas Android, seperti adaptor kursor. Kelas dalam ini tidak diperlukan, tetapi bisa membantu database Anda bekerja baik dengan framework Android.

  2. Buat kelas dalam WordList yang mengimplementasikan BaseColumns.
    public static abstract class WordList implements BaseColumns {
    }
    
  3. Pindahkan nama WORD_LIST_TABLE, serta nama kolom KEY_ID dan KEY_WORD dari WordListOpenHelper ke dalam kelas WordList di Contract dan buat menjadi publik.
  4. Kembali ke WordListOpenHelper dan tunggu agar Android Studio mengimpor konstanta dari Contract atau mengimpornya secara manual, jika Anda belum menyiapkan impor otomatis.

    Gunakan File > Settings > Editor > General > Auto Import di Windows/Linux atau Android Studio > Preferences >Editor >General > Auto Import di Mac untuk mengonfigurasi impor otomatis.)

2.3 Definsikan Konstanta URI

  1. Deklarasikan skema URI untuk penyedia konten Anda.

    Menggunakan Contract di MinimalistContentProvider sebagai contoh, deklarasikan AUTHORITY, CONTENT_PATH. Tambahkan CONTENT_PATH_URI untuk mengembalikan semua item dan ROW_COUNT_URI yang mengembalikan nomor entri. Di AUTHORITY, gunakan nama aplikasi Anda.

    public static final int ALL_ITEMS = -2;
    public static final String COUNT = "count";
    
    public static final String AUTHORITY =
           "com.android.example.wordlistsqlwithcontentprovider.provider";
    
    public static final String CONTENT_PATH = "words";
    
    public static final Uri CONTENT_URI =
           Uri.parse("content://" + AUTHORITY + "/" + CONTENT_PATH);
    public static final Uri ROW_COUNT_URI =
           Uri.parse("content://" + AUTHORITY + "/" + CONTENT_PATH + "/" + COUNT);
    

2.4 Deklarasikan tipe MIME

Tipe MIME menjelaskan tipe dan format data. Tipe MIME digunakan untuk memproses data dengan benar. Tipe MIME umum misalnya text/html untuk laman web dan application/json. Baca selengkapnya tentang tipe MIME untuk penyedia konten di dokumentasi Android.

  1. Deklarasikan tipe MIME untuk satu atau beberapa respons catatan:
    static final String SINGLE_RECORD_MIME_TYPE =
       "vnd.android.cursor.item/vnd.com.example.provider.words";
    static final String MULTIPLE_RECORDS_MIME_TYPE =
       "vnd.android.cursor.item/vnd.com.example.provider.words";
    
  2. Jalankan aplikasi Anda. Aplikasi harus berjalan dan tampak dan bertindak persis seperti sebelum Anda mengubahnya.

Tugas 3. Membuat Penyedia Konten

Bagian kedua yang harus diimplementasikan adalah kelas ContentProvider.

Dalam tugas ini, Anda akan membuat penyedia konten, mengimplementasikan metode kuerinya, dan mengaitkannya dengan WordListAdapter dan WordListOpenHelper. Sebagai ganti melakukan kueri pada WordListOpenHelper, WordListAdapter akan menggunakan resolver konten untuk melakukan kueri pada penyedia konten, yang pada akhirnya akan melakukan kueri pada WordListOpenHelper yang akan melakukan kueri pada database. Perjalanan kueri melalui komponen aplikasi.

3.1 Buat kelas WordListContentProvider

  1. Buat kelas baru yang memperluas ContentProvider dan beri nama WordListContentProvider.
  2. Di Android Studio, klik bola lampu merah, pilih "Implement methods", dan klik OK untuk mengimplementasikan semua metode yang dicantumkan.
  3. Tentukan TAG log.
  4. Deklarasikan UriMatcher.

    Penyedia konten ini menggunakan UriMatcher, sebuah kelas utilitas yang memetakan URI ke angka, sehingga Anda bisa menggunakannya.

    private static UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    
  5. Deklarasikan variabel kelas WordListOpenHelper, mDB.
    private WordListOpenHelper mDB;
    
  6. Deklarasikan kode untuk URI matcher sebagai konstanta.

    Ini akan menyatukan kode di satu tempat dan memudahkannya diubah. Gunakan kelipatan sepuluh, sehingga menyisipkan kode tambahan bisa langsung dilakukan.

    private static final int URI_ALL_ITEMS_CODE = 10;
    private static final int URI_ONE_ITEM_CODE = 20;
    private static final int URI_COUNT_CODE = 30;
    
  7. Ubah metode onCreate() untuk
    • menginsialisasi mDB dengan WordListOpenHelper,
    • memanggil metode initializeUriMatching() yang akan Anda buat nanti,
    • dan mengembalikan true.
      @Override
      public boolean onCreate() {
         mDB = new WordListOpenHelper(getContext());
         initializeUriMatching();
         return true;
      }
      
  8. Buat metode void pribadi initializeUriMatching().
  9. Di initializeUriMatching(), tambahkan URI ke matcher untuk mendapatkan semua item, satu item, dan jumlahnya.

    Periksa Contract dan gunakan metode initializeUriMatching() di aplikasi MinimalistContentProver sebagai template.

Solusi:

private void initializeUriMatching(){
   sUriMatcher.addURI(Contract.AUTHORITY, Contract.CONTENT_PATH, URI_ALL_ITEMS_CODE);
   sUriMatcher.addURI(Contract.AUTHORITY, Contract.CONTENT_PATH + "/#", URI_ONE_ITEM_CODE);
   sUriMatcher.addURI(Contract.AUTHORITY, Contract.CONTENT_PATH + "/" + Contract.COUNT, URI_COUNT_CODE );
}

3.2 Implementasikan WordListContentProvider.query()

Gunakan MiniContentProvider sebagai template untuk mengimplementasikan metode query().

  1. Modifikasi WordListContentProvider.query().
  2. Gunakan pernyataan Switch untuk kode yang dikembalikan oleh sUriMatcher.
  3. Untuk URI_ALL_ITEMS_CODE, URI_ONE_ITEM_CODE, URI_COUNT_CODE, panggil yang sesuai di WordListOpenHelper (mDB).

Perhatikan bagaimana menetapkan hasil dari mDB.query() ke kursor, akan menghasilkan kesalahan, karena WordListOpenHelper.query() mengembalikan WordItem.

Perhatikan bagaimana menetapkan hasil dari mDB.count() ke kursor, akan menghasilkan kesalahan, karena WordListOpenHelper.count() mengembalikan sebuah long.

Anda akan memperbaiki kedua kesalahan ini nanti.

Solusi:

@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection,
                   String[] selectionArgs, String sortOrder) {

   Cursor cursor = null;

   switch (sUriMatcher.match(uri)) {
       case URI_ALL_ITEMS_CODE:
           cursor = mDB.query(ALL_ITEMS);
           break;

       case URI_ONE_ITEM_CODE:
           cursor = mDB.query(parseInt(uri.getLastPathSegment()));
           break;

       case URI_COUNT_CODE:
           cursor = mDB.count();
           break;

       case UriMatcher.NO_MATCH:
           // You should do some error handling here.
           Log.d(TAG, "NO MATCH FOR THIS URI IN SCHEME: " + uri);
           break;
       default:
           // You should do some error handling here.
           Log.d(TAG, "INVALID URI - URI NOT RECOGNIZED: "  + uri);
   }
   return cursor;
}

3.3 Perbaiki WordListOpenHelper.query() untuk mengembalikan Cursor dan menangani pengembalian semua item.

Perbaiki WordListOpenHelper.query() untuk mengembalikan kursor.

Karena penyedia konten bekerja dengan kursor, Anda bisa menyederhanakan metode WordListOpenHelper.query() untuk mengembalikan kursor.

  1. Tambahkan kode dengan sebuah kueri untuk mengembalikan semua item dari database untuk menangani kasus cursor = mDB.query(ALL_ITEMS) dari pernyataan Switch di atas.
  2. Sederhanakan WordListOpenHelper.query() agar mengembalikan kursor.

    Ini memperbaiki kesalahan di WordListContentProvider.query().

    Akan tetapi, ini merusak WordListOpenHelper.OnBindViewHolder(), yang mengharapkan WordItem dari WordListOpenHelper.

Untuk mengatasi ini, WordListContentProvider.onBindViewHolder() harus menggunakan resolver konten, sebagai ganti memanggil database secara langsung, yang akan Anda lakukan setelah memperbaiki WordListContentProvider.count().

Catatan: Jenis kesalahan dan perbaikan berjenjang seperti ini umum ditemui saat mengerjakan aplikasi dunia nyata. Jika aplikasi yang sedang Anda kerjakan dirancang dengan baik, Anda bisa memperbaiki kesalahan satu demi satu.

Solusi:

/**
 * Queries the database for an entry at a given position.
 *
 * @param position The Nth row in the table.
 * @return a WordItem with the requested database entry.
 */
public Cursor query(int position) {
    String query;
    if (position != ALL_ITEMS) {
        position++; // Because database starts counting at 1.
        query = "SELECT " + KEY_ID + "," + KEY_WORD + " FROM "
                 + WORD_LIST_TABLE
                 +" WHERE " + KEY_ID + "=" + position + ";";
    } else {
        query = "SELECT  * FROM " + WORD_LIST_TABLE
                 + " ORDER BY " + KEY_WORD + " ASC ";
    }

    Cursor cursor = null;
    try {
        if (mReadableDB == null) {
            mReadableDB = this.getReadableDatabase();
        }
        cursor = mReadableDB.rawQuery(query, null);
    } catch (Exception e) {
        Log.d(TAG, "QUERY EXCEPTION! " + e);
    } finally {
        return cursor;
    }
}

3.4 Perbaiki WordListOpenHelper.count() untuk mengembalikan sebuah Cursor

Karena penyedia konten bekerja dengan kursor, Anda juga harus mengubah metode WordListOpenHelper.count() untuk mengembalikan kursor.

Gunakan MatrixCursor yang merupakan kursor dari baris dan kolom yang bisa diubah.

  1. Buat MatrixCursor menggunakan Contract.CONTENT_PATH.
  2. Di dalam blok try, dapatkan jumlah dan tambahkan sebagai baris ke kursor.
  3. Kembalikan kursor.

Solusi:

public Cursor count(){
   MatrixCursor cursor = new MatrixCursor(new String[] {Contract.CONTENT_PATH});
   try {
       if (mReadableDB == null) {
           mReadableDB = getReadableDatabase();
   }
       int count =  (int) DatabaseUtils.queryNumEntries(mReadableDB, WORD_LIST_TABLE);
       cursor.addRow(new Object[]{count});
   } catch (Exception e) {
       Log.d(TAG, "EXCEPTION " + e);
   }
   return cursor;
}

Ini akan memperbaiki kesalahan di WordListContentProvider.count(), tetapi merusak WordListAdapter.getItemCount(), yang mengharapkan sebuah long dari WordListOpenHelper.

Di WordListAdapter.onBindViewHolder(), sebagai ganti memanggil database secara langsung, Anda harus menggunakan resolver konten, yang akan Anda lakukan nanti.

3.5 Perbaiki WordListAdapter.onBindViewHolder() untuk menggunakan resolver konten

Selanjutnya, Anda akan memperbaiki WordListAdapter.onBindViewHolder() untuk menggunakan resolver konten, bukan memanggil WordListOpenHelper secara langsung. Mengubah onBindViewHolder untuk menggunakan resolver konten

  1. Di WordListAdapter, hapus variabel mDB, karena Anda tidak lagi secara langsung mengacu ke database. Ini menampilkan kesalahan di Android Studio yang akan memandu perubahan Anda selanjutnya.
  2. Di constructor, hapus penetapan ke mDB.
  3. Refactor > Change tanda tangan constructor dan hapus parameter db.
  4. Tambahkan variabel instance untuk parameter kueri karena akan digunakan lebih dari sekali.

    Resolver konten mengambil parameter kueri, yang harus Anda bangun. Pembuatan struktur kueri sama dengan kueri SQL, tetapi sebagai ganti pernyataan Selection, kueri menggunakan URI. Parameter kueri sangat mirip dengan kueri SQL.

    private String queryUri = Contract.CONTENT_URI.toString(); // base uri
    private static final String[] projection = new String[] {Contract.CONTENT_PATH}; //table
    private String selectionClause = null;
    private String selectionArgs[] = null;
    private String sortOrder = "ASC";
    
  5. Di onBindViewHolder(), hapus dua baris kode pertama.
    • WordItem current - mDB.query(position);
    • holder.wordItemView.setText(current.getWord());
  6. Define an empty String variable named word.
  7. Definisikan variabel integer yang disebut sebagai id dan setel menjadi -1.
  8. Buat resolver konten dengan parameter kueri yang ditetapkan dan simpan hasilnya di sebuah Cursor yang disebut sebagai cursor. (Lihat MainActivity aplikasi MinimalistContentProvider untuk contohnya.)

    String word = "";
    int id = -1;
    
    Cursor cursor = mContext.getContentResolver().query(Uri.parse(
                        queryUri), null, null, null, sortOrder);
    
  9. Sebagai ganti hanya mengirimkan WordItem, WordListAdapter.onBindViewHolder() harus melakukan pekerjaan ekstra yaitu mengekstrak kata dari kursor yang dikembalikan oleh resolver konten.
    • Jika kursor yang dikembalikan berisi data, ekstrak kata dan setel teks holder tampilan.
    • Ekstrak id-nya, karena Anda akan memerlukannya untuk listener klik.
    • Tutup kursor. Ingatlah bahwa Anda tidak menutup kursor di WordListOpenHelper.query(), karena Anda telah mengembalikannya.
    • Tangani jika ada kejadian tanpa data di kursor.
    • Implementasikan sumber daya string apa pun yang direferensikan.
if (cursor != null) {
      if (cursor.moveToPosition(position)) {
            int indexWord = cursor.getColumnIndex(Contract.WordList.KEY_WORD);
            word = cursor.getString(indexWord);
            holder.wordItemView.setText(word);
            int indexId = cursor.getColumnIndex(Contract.WordList.KEY_ID);
            id = cursor.getInt(indexId);
       } else {
            holder.wordItemView.setText(R.string.error_no_word);
       }

       cursor.close();

} else {
       Log.e (TAG, "onBindViewHolder: Cursor is null.");
}
  1. Perbaiki parameter untuk listener klik untuk dua tombol:

    • current.getId() ⇒ id
    • current.getWord() ⇒ word

    Listener klik yang diperbarui untuk tombol DELETE tampak seperti ini:

    @Override
    public void onClick(View v) {
    selectionArgs = new String[]{Integer.toString(id)};
    int deleted = mContext.getContentResolver().delete(
        Contract.CONTENT_URI, Contract.CONTENT_PATH,selectionArgs);
     if (deleted > 0) {
         // Need both calls
         notifyItemRemoved(h.getAdapterPosition());
         notifyItemRangeChanged(
                 h.getAdapterPosition(), getItemCount());
     } else {
          Log.d (TAG, mContext.getString(R.string.not_deleted) + deleted);
     }
    }
    
  2. Ganti panggilan ke mDB.delete(id) di callback tombol DELETE dengan panggilan resolver konten yang akan dihapus.
    selectionArgs = new String[]{Integer.toString(id)};
    int deleted = mContext.getContentResolver().delete(
                   Contract.CONTENT_URI, Contract.CONTENT_PATH, selectionArgs);
    

3.6 Ubah WordListAdapter.getItemCount() untuk menggunakan resolver konten

Sebagai ganti meminta hitungan dari database, getItemCount() harus tersambung ke resolver konten dan meminta hitungan. Di Contract, Anda mendefinisikan URI untuk mendapatkan hitungan tersebut:

public static final String COUNT = "count";
public static final Uri ROW_COUNT_URI =
       Uri.parse("content://" + AUTHORITY + "/" + CONTENT_PATH + "/" + COUNT

Ubah WordListAdaptergetItemCount() ke:

  • Menggunakan kueri resolver konten untuk mendapatkan hitungan item
  • Menggunakan ROW_COUNT_URI di kueri Anda
  • Hitungan adalah tipe integer dan merupakan elemen pertama Cursor yang dikembalikan
  • Mengekstrak count dari kursor dan mengembalikannya
  • Mengembalikan -1
  • Menutup kursor

Gunakan kode yang baru saja Anda tulis untuk onBindViewHolder sebagai panduan

Solusi:

@Override
public int getItemCount() {
    Cursor cursor = mContext.getContentResolver().query(
                     Contract.ROW_COUNT_URI, new String[] {"count(*) AS count"},
                     selectionClause, selectionArgs, sortOrder);
     try {
         cursor.moveToFirst();
         int count = cursor.getInt(0);
         cursor.close();
         return count;
     } catch (Exception e){
         Log.d(TAG, "EXCEPTION getItemCount: " + e);
         return -1;
     }
  }

3.7 Tambahkan penyedia konten ke Manifes Android

  1. Jalankan aplikasi Anda.
  2. Periksa logcat untuk melihat penyebab kesalahan (yang sangat umum).
  3. Tambahkan penyedia konten ke Manifes Android di dalam tag <application>.
    <provider
       android:name=".WordListContentProvider"  android:authorities="com.android.example.wordlistsqlwithcontentprovider.provider">
    </provider>
    
  4. Jalankan aplikasi Anda.

Aplikasi Anda harus berjalan dan berfungsi penuh. Jika tidak, bandingkan kode Anda dengan kode solusi yang diberikan dan gunakan debugger dan log untuk menemukan masalah.

3.8 Apa berikutnya?

  • Anda telah mengimplementasikan penyedia konten dan metode query()-nya.
  • Anda telah mengikuti kesalahan untuk memperbarui metode di kelas WordListOpenHelper dan WordListAdapter untuk bekerja dengan penyedia konten.
  • Saat Anda menjalankan aplikasi, panggilan metode masuk ke penyedia konten.
  • Untuk operasi penyisipan, penghapusan, dan pembaruan, aplikasi Anda masih memanggil WordListOpenHelper.

Dengan infrastruktur yang telah Anda bangun, Anda tidak perlu lagi berupaya keras mengimplementasikan metode yang tersisa.

Tugas 4. Mengimplementasikan Metode Penyedia Konten

4.1 getType()

Metode getType() dipanggil oleh aplikasi lain yang ingin menggunakan penyedia konten ini, untuk melihat jenis data seperti apa yang dikembalikan oleh aplikasi Anda.

Gunakan pernyataan Switch untuk mengembalikan tipe MIME yang sesuai.

  • Tipe MIME dicantumkan dalam Contract.
  • SINGLE_RECORD_MIME_TYPE adalah untuk URI_ALL_ITEMS_CODE
  • MULTIPLE_RECORDS_MIME_TYPE adalah untuk URI_ONE_ITEM_CODE

Solusi:

@Nullable
@Override
public String getType(Uri uri) {
   switch (sUriMatcher.match(uri)) {
       case URI_ALL_ITEMS_CODE:
           return MULTIPLE_RECORDS_MIME_TYPE;
       case URI_ONE_ITEM_CODE:
           return SINGLE_RECORD_MIME_TYPE;
       default:
           return null;
   }
}

Tantangan: Bagaimana Anda bisa menguji metode ini, karena metode ini tidak dipanggil oleh aplikasi Anda. Bisakah Anda menyebutkan tiga cara yang berbeda untuk menguji apakah metode ini bekerja dengan benar?

4.2 Panggil penyedia konten untuk menyisipkan dan memperbarui kata di MainActivity.

Untuk memperbaiki operasi penyisipan, MainActivity().onActivityResult perlu memanggil penyedia konten, bukan database untuk menyisipkan dan memperbarui kata.

  1. Di MainActivity, hapus deklarasi mDB dan pembuatan instance-nya.

Di OnActivityResult()

Menyisipkan:

  1. Jika panjang kata tidak nol, buat variabel ContentValues bernama "values" dan tambahkan kata yang dimasukkan oleh pengguna ke dalamnya menggunakan string "word" sebagai kunci.
  2. Ganti mDB.insert(word); dengan permintaan insert ke sebuah resolver konten.

Memperbarui:

  1. Ganti mDB.update(id, word); dengan permintaan update ke sebuah resolver konten.

Cuplikan solusi:

// Update the database
if (word.length() != 0) {
   ContentValues values = new ContentValues();
   values.put(Contract.WordList.KEY_WORD, word);
   int id = data.getIntExtra(WordListAdapter.EXTRA_ID, -99);

   if (id == WORD_ADD) {
      getContentResolver().insert(Contract.CONTENT_URI, values);
   } else if (id >=0) {
       String[] selectionArgs = {Integer.toString(id)};
       getContentResolver().update(Contract.CONTENT_URI, values, Contract.WordList.KEY_ID, selectionArgs
);
   }
   // Update the UI
   mAdapter.notifyDataSetChanged();

4.3 Implementasikan insert() di penyedia konten

Metode insert() di penyedia konten merupakan pass-through. Jadi, Anda

  1. memanggil metode insert() OpenHelper,
  2. mengonversi long id yang dikembalikan ke sebuah URI konten ke item yang disisipkan,
  3. dan mengembalikan URI tersebut.

Android Studio melaporkan kesalahan untuk parameter values, yang akan Anda perbaiki di langkah selanjutnya.

Solusi:

public Uri insert(Uri uri, ContentValues values) {
   long id = mDB.insert(values);
   return Uri.parse(CONTENT_URI + "/" + id);
}

4.4 Perbaiki insert() di WordListOpenHelper

Android Studio melaporkan kesalahan untuk parameter values.

  1. Buka WordListOpenHelper. Metode insert() ditulis untuk mengambil parameter String.
  2. Ubah parameter menjadi tipe ContentValues.
  3. Hapus deklarasi dan penetapan values di isi metode.

4.5 Implementasikan update() di penyedia konten

Perbaiki metode update dengan cara yang sama seperti yang Anda pakai untuk memperbaiki metode insert.

  1. Di WordListContentProvider, implementasikan update(), yang merupakan satu baris kode yang meneruskan id dan word sebagai argumen.
    return mDB.update(parseInt(selectionArgs[0]),
    values.getAsString(Contract.WordList.KEY_WORD));
    
  2. Anda tidak perlu membuat perubahan apa pun untuk memperbarui WordListOpenHelper.

4.6 Implementasikan delete() di penyedia konten

Di WordListContentProvider, implementasikan metode delete() dengan memanggil metode delete() di WordListOpenHelper dengan di kata untuk dihapus.

return mDB.delete(parseInt(selectionArgs[0]));

4.7. Jalankan aplikasi Anda.

Ya. Demikian saja. Jalankan aplikasi Anda dan pastikan semua berjalan dengan baik.

Dan jika aplikasi Anda masih belum bekerja, Anda harus memperbaiki semua masalah. Anda memerlukan kode yang berfungsi dengan baik di praktik selanjutnya. Di pelajaran tersebut, Anda akan menulis aplikasi yang menggunakan penyedia konten untuk memuat data daftar kata ke dalam antarmuka pengguna.

Kode solusi

Proyek Android Studio: word_list_sql_with_content_provider

Tantangan penyusunan kode

Catatan: Semua tantangan penyusunan kode opsional dan bukan prasyarat untuk pelajaran berikutnya.

  • Daftar kata hanyalah sebuah daftar yang berisi kata-kata tunggal, yang tidak terlalu berguna. Perluas aplikasi untuk menampilkan definisi, serta tautan ke informasi yang berguna, misalnya developer.android.com, stackoverflow, atau wikipedia.
  • Tambahkan aktivitas yang memungkinkan pengguna mencari kata.
  • Tambahkan pengujian dasar untuk semua fungsi di WordListContentProvider.

Rangkuman

  • Dalam produksi, sebagian besar developer aplikasi biasanya akan mengoptimalkan aplikasi untuk mengakomodasi penyedia konten.
  • Selama pengoptimalan, developer biasanya akan mengalami perubahan dan kesalahan bertahap.
  • Anda perlu memisahkan UI dari database menggunakan penyedia konten dan resolver konten.
  • UI harus tidak berubah selama proses pengoptimalan dari database yang disematkan ke sumber data eksternal.
  • Kelas Contract mendefinisikan konstanta umum untuk semua komponen di aplikasi yang dioptimalkan.
  • Kelas Contract melokalkan semua konstanta umum agar memudahkan pengelolaan.
  • Saat mengoptimalkan, memiliki diagram kelas akses database akan berguna bagi Anda.
  • Berhati-hatilah saat merancang URI akses data yang diperlukan aplikasi lain untuk mengakses data.
  • Semua akses ke database Anda harus diubah untuk mengakses Resolver Konten, bukan mengakses langsung kelas helper (misalnya: WordListOpenHelper)
  • Jika data yang mendasari diubah, penting bagi Anda untuk memberi tanda kepada UI untuk menyegarkan menggunakan notifyDataSetChanged().

[Konsep terkait]

Dokumentasi konsep terkait ada di Dasar-Dasar Developer Android: Konsep.

Ketahui selengkapnya

Dokumentasi Developer

Video:

results matching ""

    No results matching ""