Use SQLite to record user history

如果我們想記錄使用者在APP上互動的歷史,我們可能會需要紀錄她在某個時候,做了什麼樣的一個動作。在Android中有很多可以進行紀錄的方法,其中SQLite API最被廣為人知的應用就是用來實作TodoList、通訊錄或是其他牽涉到Client端資料庫的應用,而這邊我們也使用SQLite來進行紀錄使用者紀錄。
SQLite的教學可以參考Vogella,寫得相當完整且英文描述也非常的平易近人
教學文中主要將使用SQLite存取資料的功能分成三個物件
包括了:

  1. 自訂的SQLiteOpenHelper(用來執行資料庫的一些任務)
  2. Getter & Setter(存取資料)
  3. 還有資料存取物件(Data Access Object,DAO,為連結以上兩者的橋樑)

以下分別介紹這三者的起手式。

Custom SQLiteOpenHelper

public class DBHelper extends SQLiteOpenHelper{

 public DBHelper(Context context) {
                // DATABASE.NAME(String)與DATABASE.VERSION(int)都是我自訂interface的數值
                // 分別代表資料庫的名稱與版本
  super(context, DATABASE.NAME, null, DATABASE.VERSION);
 }

        // 資料庫一開始建置時會執行的動作將在onCreate生命週期中觸發
 @Override
 public void onCreate(SQLiteDatabase db) {

                // 這邊和SQL很像,就用你熟悉的方式輸入指令
  String sql = "CREATE TABLE IF NOT EXISTS " + DATABASE.TABLE +
    " (" +
    " " + DATABASE.UID + " TEXT," +
    " " + DATABASE.DID + " TEXT," +
    " " + DATABASE.TIME + " DATETIME," +
    " " + DATABASE.RID + " TEXT" +
    ");";
  
                // 執行
  db.execSQL(sql);
 }

        // 若發現更新後的資料庫(版本不一樣時),刪除原本的資料庫,並建置一個新的
 @Override
 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

  String sql = "DROP TABLE IF EXISTS " + DATABASE.TABLE;
  
  db.execSQL(sql);
  onCreate(db);
 }
}

Getters & Setters

public class UserHistory {

 private String uid;
 private String did;
 private String time;
 private String rid;

 public String getUid() {
  return uid;
 }

 public void setUid(String uid) {
  this.uid = uid;
 }

 public String getDid() {
  return did;
 }

 public void setDid(String did) {
  this.did = did;
 }

 public String getTime() {
  return time;
 }

        ... // 以此類推

Data Access Object

// 這邊是最重要的部分,所有有關資料存取的動作我們都在這裡調用
// 如此一來就不需要動到以上兩個物件,妥善隱藏資料的本質
public class HistoryDAO {

 private SQLiteDatabase database;
 private DBHelper dbHelper;

 public HistoryDAO(Context context) {
  dbHelper = new DBHelper(context);
 }

 // 開啟資料庫
 public void open() throws SQLException {
  database = dbHelper.getWritableDatabase();
 }

 // 關閉資料庫
 public void close() {
  dbHelper.close();
 }

 // 輸入資料到資料庫中
 public void insert(String uid, String did, String rid) {

  // 使用key/value的方式儲存資料,再丟給資料庫insert
  ContentValues values = new ContentValues();
  values.put(DATABASE.UID, uid);
  values.put(DATABASE.DID, did);
  values.put(DATABASE.TIME, getCurrentTime());
  values.put(DATABASE.RID, rid);

  database.insert(DATABASE.TABLE, null, values);
 }

 // 因為我們在台灣,需要調整時區,以獲得使用者產生互動的正確時間
 private String getCurrentTime() {
  // 註1
  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+8"));
  
  String currentTime = dateFormat.format(new java.util.Date());
  return currentTime;
 }

 // 這邊因為我在測試是不是真的有輸入成功,於是在進行query後取得數量
 public int size() {
  Cursor cursor = database.query(DATABASE.TABLE, new String[] { 
    DATABASE.UID,
    DATABASE.DID,
    DATABASE.TIME,
    DATABASE.RID }, null, null, null, null, null);
  
  return cursor.getCount();
 }
}

註1:時間的格式有標準的規定,例如年份並不支援大寫Y。
SQLite也很有個性,有它自己能接受的時間格式。(繁體中文版本)

藉由以上三個物件的實作,已經可以應付最基礎的資料存取功能。
儲存完使用者的歷史紀錄之後,接著就要開始著手偏好分析。

Reference,


JAVA的日期時間取得 @ ROACH部落落 :: 痞客邦 PIXNET ::

The Future of Web

由於我的研究領域牽涉到了本體論(Ontology)
也就是Tim ‎Berners-Lee(2004)提出的語意網Layer Cake中的一個部分:


因此我略懂RDF也是很正常的。
就像身為一個汽車維修員,隨身攜帶工具也是很正常的一樣XD
所以以下此篇將以語意網的角度淺談Web的未來。

一開始,讓我們先來了解一下Web 1.0 2.0與3.0的不同:

分類(Web)
描述
1.0

沒有互動的機制,所有的資訊都是單向的流通。
2.0

最主要的精神是可以進行協作與共享,模範生有Wikipedia,Facebook等等。
「Anyone can say Anything on Any topic」這個AAA Slogan[1]也將其精神表現得淋漓盡致。
3.0
根據維基百科的定義:
「該詞包含多層含義,用來概括網際網路發展過程中可能出現的各種不同的方向和特徵,包括:將網際網路本身轉化為一個泛型資料庫;跨瀏覽器、超瀏覽器的內容投遞和請求機制;人工智慧技術的運用;語意網。」
※泛型資料庫:我認為是與Linked Data相關概念,請參考Vcard。


語意網中有一層標準和現在的HTML標準很接近,但是又有點些許的不同。
這層便是RDF。(在Semantic Web Layer Cake中Ontology的下層)
和HTML同為XML檔案,且都是W3C定義的標準,其簡介如下:

RDF 語言是 W3C 的語意網活動的组成部分。W3C 的「語意網展望(Semantic Web Vision)」的目標是:
  • Web 資訊擁有精確的涵義 
  • Web 資訊可以被電腦理解並處理 
  • 電腦可以從 Web 上整合資訊

以下是RDF的範例代碼。在RDF中,所有的標籤皆可以是自訂的:
<?xml version="1.0"?>
<RDF>
  <Description about="http://www.w3school.com.cn/RDF">
    <author>David</author>
    <homepage>http://www.w3school.com.cn</homepage>
  </Description>
</RDF>
而從HTML4、HTML5甚至到HTML6來看
其實對於自定義標籤的限制是越來越寬了
從一開始的head、body,到後來還有header、footer等標籤
某種程度看來也是在幫助網頁本身的資訊能夠擁有更精確的涵義

就現在來說,我並不確定我們屬於Web的2.0、2.3、2.5的哪個階段
但可以確定的是,我們已經慢慢地朝著Web 3.0的方向前進
至於4.0是什麼樣的世界 真的很難以想像......

References,


[1] Semantic Web for the Working Ontologist. D.Allemang & J.Hendler, 2008.

Resources of implement custom TabHostButton, ListView with images and texts, and ProgressBar when loading data


Mealionaier是一個可以學習你對食物的偏好
精準推薦給你附近美食的APP。

然而為了更好的使用者體驗,需要實現方便使用、直覺的UI
在實現某些UI時需要一些tricks。

在這邊和大家介紹我之前碰壁時,有如菩薩般出現拯救我的資源
等我論文告一段落了,再來分享詳細的程式碼教學:)

利用RadioGroup與Fragments創造Tab Buttons效果


Tutorial link - 开源项目AndroidUtil-采用Fragment实现TabHost | 方杰
  • 每個Tab都是一個RadioGroup的項目。
  • 為RadioGroup新增它的項目符號後,再將選取按鍵(checkbox)隱藏起來
  • 當使用者點擊Tab時,用程式的方式選取對應的項目。


在ListView中妥善顯示圖片與文字


Tutorial link - Android Adapter Good Practices « Piwaï.info
  • 做好忍痛放棄ArrayList、SimpleAdapter等內建Adapter的心理準備
  • 繼承BaseAdapter,自訂一個新的Adapter from scratch
  • 了解ViewHolder的patterns

動態載入ListView的items並在載入時顯示ProgressBar


Tutorial link - Endless Scrolling with AdapterViews | CodePath Android Cliffnotes
  • 學會操作onScrollListener
  • 繼承BaseAdapter,自訂一個新的Adapter
  • 深入了解如何操作getViewType、getView等BaseAdapter中的method