Menggunakan Google Base dan Google Gears untuk Pengalaman Offline yang Luar Biasa

Artikel pertama dalam seri "Mem-build Aplikasi Ajax yang Lebih Baik dengan Google API".

Dion Almaer dan Pamela Fox, Google
Juni 2007

Catatan Editor: Google Gears API tidak lagi tersedia.

Pengantar

Dengan memadukan Google Base dan Google Gears, kami menunjukkan cara membuat aplikasi yang dapat digunakan saat offline. Setelah membaca artikel ini, Anda akan mengenal Google Base API lebih lanjut, serta memahami cara menggunakan Google Gears untuk menyimpan dan mengakses preferensi pengguna dan data.

Memahami Aplikasi

Untuk memahami aplikasi ini, Anda harus terlebih dahulu memahami Google Base, yang pada dasarnya merupakan database besar item yang mencakup berbagai kategori seperti produk, ulasan, resep, acara, dan lainnya.

Setiap item dianotasi dengan judul, deskripsi, link ke sumber data asli (jika ada), serta atribut tambahan yang bervariasi per jenis kategori. Google Base memanfaatkan fakta bahwa item dalam kategori yang sama memiliki sekumpulan atribut yang sama-misalnya, semua resep memiliki bahan. Item Google Base kadang-kadang akan muncul dalam hasil penelusuran dari penelusuran web Google atau penelusuran produk Google.

Aplikasi demo kami, Base with Gears, memungkinkan Anda menyimpan dan menampilkan penelusuran umum yang mungkin Anda lakukan di Google Base, seperti mencari resep dengan "cokelat" (yum) atau iklan pribadi dengan "walks on the Beach" (romantis!). Anda dapat menganggapnya sebagai "Pembaca Google Base" yang memungkinkan Anda berlangganan penelusuran dan melihat hasil yang diperbarui saat Anda membuka kembali aplikasi, atau saat aplikasi keluar untuk mencari feed yang diperbarui setiap 15 menit.

Developer yang ingin memperluas aplikasi dapat menambahkan lebih banyak fitur, seperti memberi notifikasi secara visual kepada pengguna jika hasil penelusuran berisi hasil baru, memungkinkan pengguna mem-bookmark (memberi bintang) item favorit (offline + online), dan memungkinkan pengguna melakukan penelusuran dengan atribut khusus kategori seperti Google Base.

Menggunakan Feed API data Google Base

Google Base dapat dikueri secara terprogram dengan Google Base data API, yang sesuai dengan framework Google Data API. Protokol Google Data API menyediakan protokol sederhana untuk membaca dan menulis di web, dan digunakan oleh banyak produk Google: Picasa, Spreadsheet, Blogger, Kalender, Notebook, dan lain-lain.

Format Google Data API didasarkan pada XML dan Atom Publishing Protocol, jadi sebagian besar interaksi baca/tulis ada dalam XML.

Contoh feed Google Base berdasarkan Google Data API adalah:
http://www.google.com/base/feeds/snippets/-/products?bq=digital+camera

Jenis feed snippets memberi kita feed item yang tersedia untuk publik. Dengan -/products, kami dapat membatasi feed ke kategori produk. Selain itu, parameter bq= memungkinkan kita membatasi feed lebih lanjut, sehingga hanya hasil yang berisi kata kunci "kamera digital". Jika melihat feed ini di browser, Anda akan melihat XML yang berisi node <entry> dengan hasil yang cocok. Setiap entri berisi elemen umum penulis, judul, konten, dan link, tetapi juga dilengkapi atribut khusus kategori tambahan (seperti "harga" untuk item dalam kategori produk).

Karena pembatasan lintas-domain XMLHttpRequest pada browser, kami tidak diizinkan untuk membaca secara langsung dalam feed XML dari www.google.com dalam kode JavaScript kami. Kita dapat menyiapkan proxy sisi server untuk dibaca dalam XML dan melancarkannya kembali pada lokasi di domain yang sama dengan aplikasi kita, tetapi kita ingin menghindari pemrograman sisi server sepenuhnya. Untungnya, ada alternatif yang tersedia.

Seperti Google Data API lainnya, Google Base data API memiliki opsi output JSON, selain XML standar. Output untuk feed yang kita lihat sebelumnya dalam format JSON akan berada di URL ini:
http://www.google.com/base/feeds/snippets/-/products?bq=digital+camera&alt=json

JSON adalah format pertukaran ringan yang memungkinkan hierarki bertingkat serta berbagai jenis data. Namun yang lebih penting, output JSON adalah kode JavaScript native itu sendiri, sehingga dapat dimuat ke halaman web Anda hanya dengan mereferensikannya dalam tag skrip, mengabaikan pembatasan lintas-domain.

Google Data API juga memungkinkan Anda menentukan output "json-in-script" dengan fungsi callback yang akan dieksekusi setelah JSON dimuat. Hal ini membuat output JSON lebih mudah digunakan, karena kita dapat menambahkan tag skrip secara dinamis ke halaman dan menentukan fungsi callback yang berbeda untuk masing-masing tag.

Jadi, untuk memuat feed JSON API Dasar secara dinamis ke dalam halaman, kita dapat menggunakan fungsi berikut yang membuat tag skrip dengan URL feed (ditambahkan dengan nilai altcallback) dan menambahkannya ke halaman.

function getJSON() {
  var script = document.createElement('script');

  var url = "http://www.google.com/base/feeds/snippets/-/products?bq=digital+camera";
  script.setAttribute('src', url + "&alt=json-in-script&callback=listResults");
  script.setAttribute('type', 'text/JavaScript');
  document.documentElement.firstChild.appendChild(script);
}

Jadi, fungsi callback listResults kita sekarang dapat melakukan iterasi melalui JSON yang diteruskan sebagai satu-satunya informasi parameter dan tampilan pada setiap entri yang ditemukan dalam daftar berbutir.

  function listTasks(root) {
    var feed = root.feed;
    var html = [''];
    html.push('<ul>');
    for (var i = 0; i < feed.entry.length; ++i) {
      var entry = feed.entry[i];
      var title = entry.title.$t;
      var content = entry.content.$t;
      html.push('<li>', title, ' (', content, ')</li>');
    }
    html.push('</ul>');

    document.getElementById("agenda").innerHTML = html.join("");
  }

Menambahkan Google Gears

Setelah memiliki aplikasi yang dapat berkomunikasi dengan Google Base melalui Google Data API, kita ingin mengaktifkan aplikasi ini agar berjalan secara offline. Di sinilah Google Gears berperan.

Ada berbagai pilihan arsitektur untuk menulis aplikasi yang bisa offline. Anda akan bertanya kepada diri sendiri tentang bagaimana cara kerja aplikasi secara online vs. offline (misalnya, apakah aplikasi akan sama persis? Apakah beberapa fitur dinonaktifkan, seperti penelusuran? Bagaimana Anda akan menangani sinkronisasi?)

Dalam kasus ini, kita ingin memastikan bahwa pengguna pada browser tanpa Roda Masih dapat menggunakan aplikasi, sembari memberikan pengguna yang memiliki manfaat plugin dari penggunaan offline dan UI yang lebih responsif.

Arsitektur kami terlihat seperti ini:

  • Kami memiliki objek JavaScript yang bertugas menyimpan kueri penelusuran Anda dan menampilkan kembali hasil dari kueri ini.
  • Jika Google Gears telah dipasang, Anda mendapatkan versi Roda Gigi yang menyimpan segala sesuatu yang ada dalam basis data lokal.
  • Jika Anda belum menginstal Google Gears, Anda akan mendapatkan versi yang menyimpan kueri dalam cookie dan tidak menyimpan hasil secara keseluruhan (yang berarti responsnya sedikit lebih lambat), karena hasilnya terlalu besar untuk disimpan dalam cookie.
Hal yang bagus dari arsitektur ini adalah Anda tidak perlu melakukan pemeriksaan untuk if (online) {} di seluruh toko. Sebagai gantinya, aplikasi memiliki satu pemeriksaan Gears, kemudian adaptor yang tepat digunakan.


Menggunakan Database Lokal Gear

Salah satu komponen Roda Gigi adalah database SQLite lokal yang disematkan dan siap digunakan. Ada API database sederhana yang akan tampak familier bagi Anda jika sebelumnya Anda menggunakan API untuk database sisi server seperti MySQL atau Oracle.

Langkah-langkah untuk menggunakan database lokal cukup sederhana:

  • Melakukan inisialisasi objek Google Gears
  • Mendapatkan objek factory database, dan membuka database
  • Memulai eksekusi permintaan SQL

Mari kita bahas lebih lanjut.


Inisialisasi Objek Google Gears

Aplikasi Anda harus membaca konten /gears/samples/gears_init.js secara langsung, atau dengan menempelkan kode ke file JavaScript Anda sendiri. Setelah memiliki <script src="..../gears_init.js" type="text/JavaScript"></script>, Anda memiliki akses ke namespace google.gears.


Mendapatkan Objek Pabrik Database & amp; Membuka Database
var db = google.gears.factory.create('beta.database', '1.0');
db.open('testdb');

Panggilan satu ini akan memberi Anda objek database yang memungkinkan Anda membuka skema database. Saat Anda membuka database, database tersebut tercakup melalui aturan kebijakan asal yang sama, sehingga "testdb" Anda tidak akan bertentangan dengan "testdb" saya.


Mulai Mengeksekusi Permintaan SQL

Sekarang kita siap mengirim permintaan SQL ke database. Saat mengirim permintaan "pilih", kita akan mendapatkan kembali kumpulan hasil yang dapat diiterasi untuk data yang diinginkan:

var rs = db.execute('select * from foo where name = ?', [ name ]);

Anda dapat beroperasi pada kumpulan hasil yang ditampilkan dengan metode berikut:

booleanisValidRow()
voidnext()
voidclose()
intfieldCount()
stringfieldName(int fieldIndex)
variantfield(int fieldIndex)
variantfieldByName(string fieldname)

Untuk detail selengkapnya, lihat dokumentasi Database Module API. (Catatan Editor: Google Gears API tidak lagi tersedia).


Menggunakan Roda Gigi untuk Enkapsulasi API Tingkat Rendah

Kita ingin mengenkapsulasi dan mempermudah beberapa tugas database umum. Misalnya,

  • Kita ingin memiliki cara yang bagus untuk mencatat log SQL yang dihasilkan saat kita men-debug aplikasi.
  • Kami ingin menangani pengecualian di satu tempat, bukan harus melakukan try{}catch(){} di semua tempat.
  • Kita ingin menangani objek JavaScript, bukan kumpulan hasil saat membaca atau menulis data.

Untuk menangani masalah ini secara umum, kami membuat GearsDB, library open source yang menggabungkan objek Database. Sekarang kita akan menunjukkan cara memanfaatkan GearsDB.

Penyiapan Awal

Di kode window.onload, kita harus memastikan bahwa tabel database yang kita gunakan sudah ada. Jika pengguna telah menginstal Gears saat kode berikut berjalan, mereka akan membuat objek GearsBaseContent:

content = hasGears() ? new GearsBaseContent() : new CookieBaseContent();

Selanjutnya, kita buka database dan membuat tabel jika belum ada:

db = new GearsDB('gears-base'); // db is defined as a global for reuse later!

if (db) {
  db.run('create table if not exists BaseQueries' +
         ' (Phrase varchar(255), Itemtype varchar(100))');
  db.run('create table if not exists BaseFeeds' + 
         ' (id varchar(255), JSON text)');
}

Pada tahap ini, kita yakin bahwa kita memiliki tabel untuk menyimpan kueri dan feed. Kode new GearsDB(name) akan mengenkapsulasi pembukaan database dengan nama yang diberikan. Metode run menggabungkan metode execute level yang lebih rendah, tetapi juga menangani output proses debug ke konsol dan menangkap pengecualian.


Menambahkan Istilah Penelusuran

Saat pertama kali menjalankan aplikasi, Anda tidak akan memiliki penelusuran. Jika Anda mencoba menelusuri Nintendo Wii di produk, kami akan menyimpan istilah penelusuran ini di tabel BaseQuery.

Versi Gears dari metode addQuery melakukan hal ini dengan mengambil input dan menyimpannya melalui insertRow:

var searchterm = { Phrase: phrase, Itemtype: itemtype };
db.insertRow('BaseQueries', searchterm); 

insertRow mengambil objek JavaScript (searchterm) dan menangani INSERTing ke dalam tabel untuk Anda. Ini juga memungkinkan Anda menentukan batasan (misalnya, penyisipan blok keunikan lebih dari satu "Bob"). Namun, biasanya Anda akan menangani batasan ini dalam database itu sendiri.


Mendapatkan Semua Istilah Penelusuran

Untuk mengisi daftar penelusuran sebelumnya, kami menggunakan wrapper pilihan yang bagus bernama selectAll:

GearsBaseContent.prototype.getQueries = function() {
  return this.db.selectAll('select * from BaseQueries');
}

Tindakan ini akan menampilkan array objek JavaScript yang cocok dengan baris dalam database (misalnya [ { Phrase: 'Nintendo Wii', Itemtype: 'product' }, { ... }, ...]).

Dalam hal ini, Anda dapat menampilkan daftar lengkap. Namun, jika memiliki banyak data, Anda mungkin dapat menggunakan callback di panggilan pilih sehingga Anda dapat beroperasi pada setiap baris yang ditampilkan saat masuk:

 db.selectAll('select * from BaseQueries where Itemtype = ?', ['product'], function(row) {
  ... do something with this row ...
});

Berikut adalah beberapa metode pemilihan berguna lainnya di GearsDB:

selectOne(sql, args)Menampilkan objek JavaScript pertama/satu yang cocok
selectRow(table, where, args, select)Biasanya digunakan dalam kasus sederhana untuk mengabaikan SQL
selectRows(table, where, args, callback, select)Sama seperti selectRow, tetapi untuk beberapa hasil.

Memuat Feed

Saat mendapatkan feed hasil dari Google Base, kita harus menyimpannya ke database:

content.setFeed({ id: id, JSON: json.toJSONString() });

... which calls ...

GearsBaseContent.prototype.setFeed = function(feed) {
  this.db.forceRow('BaseFeeds', feed);
}

Pertama-tama, kita mengambil feed JSON dan menampilkannya sebagai String menggunakan metode toJSONString. Kemudian, kita membuat objek feed dan meneruskannya ke dalam metode forceRow. forceRow akan menyisipkan entri jika belum ada, atau MEMPERBARUI entri yang ada.


Menampilkan Hasil Penelusuran

Aplikasi kita menampilkan hasil untuk penelusuran tertentu di panel kanan halaman. Berikut adalah cara kami mengambil feed yang terkait dengan istilah penelusuran:

GearsBaseContent.prototype.getFeed = function(url) {
  var row = this.db.selectRow('BaseFeeds', 'id = ?', [ url ]);
  return row.JSON;
}

Setelah memiliki JSON untuk satu baris, kita dapat eval() untuk mendapatkan objek kembali:

eval("var json = " + jsonString + ";");

Kita siap untuk berlomba dan dapat mulai menempatkan konten dari HTML ke dalam halaman kita.


Menggunakan Toko Resource untuk Akses Offline

Karena kita memperoleh konten dari database lokal, aplikasi ini juga harus bekerja secara offline, bukan?

Tidak. Masalahnya adalah memulai aplikasi ini, Anda harus memuat resource webnya, seperti JavaScript, CSS, HTML, dan gambarnya. Seperti saat ini, jika pengguna Anda melakukan langkah-langkah berikut, aplikasi mungkin masih berfungsi: mulai online, lakukan beberapa penelusuran, jangan tutup browser, lakukan offline. Ini bisa berfungsi karena item masih berada dalam cache browser. Namun, bagaimana jika hal ini tidak terjadi? Kita ingin pengguna dapat mengakses aplikasi dari awal, setelah memulai ulang, dll.

Untuk melakukannya, kita menggunakan komponen LocalServer dan menangkap resource. Saat Anda menangkap sumber daya (seperti HTML dan JavaScript yang diperlukan untuk menjalankan aplikasi), Gears akan menyimpan item ini dan juga akan memerangkap permintaan dari browser untuk mengembalikannya. Server lokal akan bertindak sebagai polisi lalu lintas dan menampilkan konten yang disimpan dari toko.

Kami juga memanfaatkan komponen ResourceStore, yang mengharuskan Anda memberi tahu sistem secara manual file mana yang ingin diambil. Dalam banyak skenario, Anda ingin membuat versi aplikasi dan mengizinkan upgrade secara transaksional. Serangkaian resource akan menentukan versi, dan saat Anda merilis kumpulan resource baru, Anda tentu ingin pengguna dapat mengupgrade file tersebut dengan lancar. Jika itu adalah model Anda, maka Anda akan menggunakan ManagedResourceStore API.

Untuk menangkap sumber daya kita, objek GearsBaseContent akan:

  1. Menyiapkan array file yang perlu direkam
  2. Membuat LocalServer
  3. Membuka atau membuat ResourceStore baru
  4. Panggil untuk mengambil halaman di toko
// Step 1
this.storeName = 'gears-base';
this.pageFiles = [
  location.pathname,
  'gears_base.js',
  '../scripts/gears_db.js',
  '../scripts/firebug/firebug.js',
  '../scripts/firebug/firebug.html',
  '../scripts/firebug/firebug.css',
  '../scripts/json_util.js',    'style.css',
  'capture.gif' ];

// Step 2
try {
  this.localServer = google.gears.factory.create('beta.localserver', '1.0');
} catch (e) {
  alert('Could not create local server: ' + e.message);
  return;
}

// Step 3
this.store = this.localServer.openStore(this.storeName) || this.localServer.createStore(this.storeName);

// Step 4
this.capturePageFiles();

... which calls ...

GearsBaseContent.prototype.capturePageFiles = function() {
  this.store.capture(this.pageFiles, function(url, success, captureId) {
    console.log(url + ' capture ' + (success ? 'succeeded' : 'failed'));
  });
}

Hal penting yang perlu diperhatikan di sini adalah bahwa Anda hanya dapat merekam resource di domain Anda sendiri. Kami menemui keterbatasan ini saat mencoba mengakses file JavaScript GearsDB secara langsung dari file "gears_db.js" aslinya di trunk SVN-nya. Solusinya, tentu saja: Anda perlu mendownload resource eksternal dan menempatkannya di bawah domain. Perlu diketahui bahwa pengalihan 302 atau 301 tidak akan berfungsi, karena LocalServer hanya menerima kode server 200 (Berhasil) atau 304 (Tidak Diubah).

Hal ini memiliki implikasi. Jika Anda menempatkan gambar di images.domainanda.com, Anda tidak dapat menangkapnya. www1 dan www2 tidak dapat saling melihat. Anda dapat menyiapkan proxy sisi server, tetapi tindakan tersebut akan menggagalkan tujuan pemisahan aplikasi Anda ke beberapa domain.

Men-debug Aplikasi Offline

Proses debug aplikasi offline sedikit lebih rumit. Ada lebih banyak skenario untuk diuji:

  • Saya online dengan aplikasi sepenuhnya berjalan di cache
  • Saya sedang online tetapi belum mengakses aplikasi, dan tidak ada apa pun di cache
  • Saya sedang offline tetapi telah mengakses aplikasi
  • Saya sedang offline dan belum pernah mengakses aplikasi (bukan tempat yang tepat!)

Untuk mempermudah, kami menggunakan pola berikut:

  • Kami menonaktifkan cache di Firefox (atau browser pilihan Anda) saat kami perlu memastikan bahwa browser tidak hanya mengambil sesuatu dari cache
  • Kami melakukan debug menggunakan MediaStore (dan ExoPlayer Lite untuk pengujian di browser lain); kami menggunakan console.log() di seluruh tempat, dan mendeteksi konsol untuk berjaga-jaga
  • Kami menambahkan kode JavaScript helper ke:
    • memungkinkan kami untuk menghapus database dan memberikan sumber data yang bersih
    • hapus file yang diambil, sehingga saat memuat ulang, pengguna akan diarahkan ke internet untuk mendapatkannya kembali (berguna saat Anda melakukan iterasi pada pengembangan ;)

Widget debug hanya muncul di sisi kiri halaman jika Anda telah menginstal Gears. Terdapat info untuk membersihkan kode:

GearsBaseContent.prototype.clearServer = function() {
  if (this.localServer.openStore(this.storeName)) {
    this.localServer.removeStore(this.storeName);
    this.store = null;
  }
}

GearsBaseContent.prototype.clearTables = function() {
  if (this.db) {
    this.db.run('delete from BaseQueries');
    this.db.run('delete from BaseFeeds');
  }
  displayQueries();
}

Kesimpulan

Anda dapat memahami bahwa bekerja dengan Google Gears sebenarnya cukup sederhana. Kita menggunakan GearsDB untuk memudahkan komponen Database, dan menggunakan ResourceStore manual, yang berfungsi dengan baik untuk contoh kita.

Area tempat Anda menghabiskan sebagian besar waktu menentukan strategi untuk mendapatkan data secara online, dan cara menyimpannya secara offline. Luangkan waktu untuk menentukan skema database. Jika perlu mengubah skema di masa mendatang, Anda harus mengelola perubahan tersebut karena pengguna saat ini sudah memiliki versi database. Ini berarti Anda harus mengirim kode skrip dengan upgrade db apa pun. Tentunya, hal ini membantu meminimalkan hal ini, dan Anda dapat mencoba GearShift, library kecil yang dapat membantu mengelola revisi.

Kita juga dapat menggunakan ManagedResourceStore untuk melacak file kita, dengan konsekuensi berikut:

  • Kita akan menjadi warga negara yang baik dan membuat versi file untuk memungkinkan upgrade yang bersih di masa mendatang
  • Terdapat fitur ManagedResourceStore yang memungkinkan Anda menggunakan URL untuk konten lainnya. Pilihan arsitektur yang valid adalah membuat roda gigi_base.js menjadi versi non-Roda Gigi, dan alias tersebut sehingga Roda Gigi akan mengunduh roda gigi_base_withgears.js yang memiliki semua dukungan offline.
Untuk aplikasi kami, kami merasa lebih mudah untuk hanya memiliki satu antarmuka dan menerapkan antarmuka tersebut dengan dua cara.

Semoga aplikasi Anda semakin asyik dan mudah! Bergabunglah dengan kami dalam forum Google Gears jika Anda memiliki pertanyaan atau aplikasi untuk dibagikan.