Wednesday, June 9, 2010

Làm việc với Database trên Android (P3)

Tiếp tục tôi sẽ chỉ các bạn tạo một thể hiện của một dòng dữ liệu.
Mỗi một bảng trong SQLite nên tạo một thể hiện kế thừa từ lớp DataRow này.
Một DataRow chứa một số các DataField nên ta khai báo một mảng DataField.
Ngoài ra ta cần phải khai báo một biến trung ContentValues và một thể hiện Database ta đã tạo ở phần 1

package com.tannm.doan.taskmanager.database;

import android.content.ContentValues;
import android.database.Cursor;

public abstract class DataRow {
 // fields
 protected Database userdb = null;
 private DataField[] vecTableDef = null;
 private ContentValues values = new ContentValues();

 // methods
 public DataRow(Database userdb) {
  this.userdb = userdb;
 }

 public Database getUserDb() {
  return userdb;
 }

 public void setTableDefinition(DataField[] vecTableDef) {
  this.vecTableDef = vecTableDef;
  // initialize field parent
  updateDataFieldsParentRow(this);
 }

 public void updateDataFieldsParentRow(DataRow row) {
  for (int i = 0; i < vecTableDef.length; i++)
   vecTableDef[i].setParentRow(row);
 }

 public void copyTableDefinition(DataRow data) {
  setTableDefinition(data.vecTableDef);
 }

 public DataField[] getTableDef() {
  return vecTableDef;
 }

 public boolean validate() {
  return false;
 }

 public void clearContentValues() {
  values.clear();
 }

 public ContentValues getContentValues() {
  return values;
 }

 public void setContentValues(ContentValues values) {
  this.values = values;
  updateDataFieldsParentRow(this);
 }

 public boolean copyContentValues(ContentValues values) {
  this.values = values;
  updateDataFieldsParentRow(this);
  try {
   getValuesFromDataRow();
   return true;
  } catch (Exception e) {
  }
  return false;
 }

 public DataField value(int idx) {
  return vecTableDef[idx];
 }

 public String fieldName(int idx) {
  return vecTableDef[idx].getName();
 }

 public boolean getValuesFromCursor(Cursor cr) {
  if ((cr != null) && (cr.getPosition() != -1)) {
   for (int idx = 0; idx < vecTableDef.length; idx++) {
    DataField field = value(idx);
    // check if null value
    if (cr.isNull(idx)) {
     field.setNull();
    } else {
     final DataField.Type t = field.getType();
     // parse value by type
     if (t == DataField.Type.INT)
      field.set(cr.getLong(idx));
     if (t == DataField.Type.TEXT)
      field.set(cr.getString(idx));
     if (t == DataField.Type.BOOL)
      field.set((cr.getInt(idx) == 1) ? true : false);
    }
   }
   return true;
  }
  return false;
 }

 // sets fields values data (ContentValues values contener) from parent
 // object fields
 public abstract void setValuesForDataRow();

 // gets data from fields values (ContentValues values contener) to parent
 // object fields
 public abstract void getValuesFromDataRow();

 public abstract String getTableName();

}

Thursday, June 3, 2010

Đạo code (Phần 3: Thiết kế)

Lập Trình Sư nói:
"Khi chương trình đang được test đó là thời điểm quá muộn để thay đổi thiết kế."

3.1

Một nam nhân tham dự triển lãm tin học. Mỗi ngày khi vào cửa, gã nói với người cảnh vệ:
"Ta là một đại đạo, nổi danh với nghề trộm cắp như thần. Nói trước với ngươi là ta sẽ không rời triển lãm khi chưa chôm được thứ gì".
Lời nam nhân khiến người cảnh vệ rất đỗi hoang mang, bởi số thiết bị trong triển lãm trị giá đến trăm vạn lượng vàng. Thế nên người cảnh vệ luôn bám sát theo dõi nhất cử nhất động của gã đại đạo. Tuy nhiên gã đại đạo chỉ đi thơ thẩn giữa các gian hàng. Thỉnh thoảng gã cười mỉm một cách khó hiểu.
Khi đại đạo ra khỏi cửa người cảnh vệ liền lôi gã lại để lục soát nhưn chẳng tìm thấy gì.
Hôm sau đại đạo lại đến. Khi đi qua cửa gã thì thầm với cảnh vệ:
"Hôm qua ta đã thoát ra với một đống chiến lợi phẩm, hôm nay có thể sẽ còn nhiều hơn".
Người cảnh vệ lo sốt vó càng theo sát gã đại đạo, nhưng như hôm trước người cảnh vệ chẳng phát hiện được gì.
Ngày cuối cùng, cảnh vệ không kiềm chế được sự tò mò:
"Thưa ngài đạo chích" cảnh vệ nói "Tôi đã hoang mang đến mất ngủ. Hai khai sáng cho tôi, thứ mà ngài lấy trộm là gì vậy?";
Đại đạo mỉm cười, "Ta trộm ý tưởng" hắn nói.

3.2

Một Lập Trình Sư viết chương trình không có cấu trúc. Một lập trình phu học theo cũng viết một chương trình không có cấu trúc. Khi đánh giá kết quả, Lập Trình Sư đã chửi mắng lập trình phu vì viết code không có cấu trúc, ông nói "Tuyệt học của đại cao thủ đâu phải để cho kẻ tầm thường? Ngươi cần phải lĩnh hội được Đạo rồi ngươi mới được phép bỏ qua cấu trúc chương trình".

3.3

Một lập trình phu làm môn khách tại phủ Ngô Vương. Ngô Vương hỏi phu: "Hệ điều hành và phần mềm kế toán, thứ nào dễ thiết kế hơn?"
"Hệ điều hành", phu đáp.
Ngô Vương ngạc nhiên "Rõ ràng một phần mềm kế toán đơn giản hơn nhiều hơn so với một hệ điều hành", ông nói đầy vẻ nghi ngờ.
"Không đúng lắm", phu nói, "khi thiết kế phần mềm kế toán, lập trình phu phải đáp ứng yêu cầu của nhiều người khác nhau: phần mềm phải hoạt động ra sao, báo cáo lên như thế nào, đáp ứng điều kiện gì của luật tô thuế. Ngược lại, hệ điều hành không bị hạn chế vì những ý kiến bên ngoài. Lập trình phu chỉ tìm cách đơn giản nhất để máy thực hiện bài toán. Đó là lý do vì sao hệ điều hành dễ thiết kế".
Ngô Vương mỉm cười gật đầu "Đúng lắm, thế thứ nào dễ debug hơn?"
Phu không đáp.

3.4

Chủ trại lập trình đến gặp Lập Trình Sư và đưa cho ông tài liệu yêu cầu của một phần mềm mới.
Trại chủ hỏi Lập Trình Sư "Việc thiết kế hệ thống sẽ mất bao lâu nếu ta để năm lập trình phu làm?"
"Mất một năm", Lập Trình Sư trả lời ngay.
"Nhưng ta cần hệ thống này hoàn thành càng sớm càng tốt. Sẽ mất bao lâu nếu ta cho mười phu tham gia thiết kế?"
Lập trình sư cau mày "Trường hợp này, ngài sẽ cần hai năm".
"Thế nếu ta đáp 100 phu vào thì sao?"
Lập Trình Sư nhún vai "Sẽ chẳng bao giờ xong" ông nói.

Theo bản dịch của anh HiepTH

Wednesday, June 2, 2010

Đạo code (Phần 2: Lập Trình Sư tiền bối)

Lập Trình Sư nói:
"Sau ba ngày không lập trình, cuộc sống trở nên vô nghĩa."

2.1

Bậc Lập Trình Sư tiền bối rất bí ẩn và uyên thâm. Chúng ta không thể hiểu suy nghĩ của họ. Thế nên ta chỉ mô tả đặc điểm của họ.
Họ cẩn thận như loài hồ ly trên mặt nước. Họ cảnh giác như vị tướng giữa trận tiền. Họ tốt bụng như chủ nhà tiếp bậc khách quý. Họ giản dị như gỗ đá. Họ bí ẩn như hồ đen trong hang tối.
Ai có thể biết bí mật trong con tim khối óc của họ.
Câu trả lời nằm trong Đạo.

2.2

Đại sư Turing một đêm nằm mơ thấy mình hóa máy. Khi thức dậy ông thốt lên:
"Không biết ta là Turing nằm mơ hóa máy hay ta là máy đang nằm mơ hóa Turing đây"

2.3

Một lập trình phu từ một đại công ty máy tính sau khi tham dự đại hội phần mềm trở về trình báo với chủ nhân: "Không hiểu lũ lập trình phu công ty khác là loại gì. Chúng cư xử rất thô lỗ và chẳng quan tâm gì đến vẻ bề ngoài. Đầu tóc chúng dài và bù xù, quần áo thì nhăn nhúm và cũ kỹ. Chúng nhảy xổ vào hội trường và gây huyên náo cả buổi."
Chủ nhân đáp "Đáng lẽ ta không nên đưa ngươi đến đó. Những người ngươi gặp sống ngoài thế giới thực. Họ coi cuộc sống là ngớ ngẩn, tất cả chẳng qua chỉ là một sự trùng hợp tình cờ. Họ đến và đi mà không hề biết đến giới hạn. Họ không quan tâm đến thứ gì khác ngoài chương trình của họ. Vậy thì họ còn bận tâm đến các vấn đề xã hội làm gì?"
"Họ đang sống trong Đạo"

2.4

Đồ đệ hỏi Lập Trình Sư: "Một lập trình phu không bao giờ thiết kế, thảo tài liệu, hay thử nghiệm chương trình. Vậy mà tất cả mọi người biết anh ta đều coi anh ta anh ta là thiên hạ đệ nhất phu. Tại sao vậy?"

Lập Trình Sư trả lời "Kẻ đó đã lĩnh hội được Đạo. Hắn đã thoát khỏi giới hạn của thiết kế, khi hệ thống trục trặc hắn không hề bối rối và tiếp nhận mọi thứ một cách bình thản. Hắn đã thoát khỏi giới hạn của thảo tài liệu, hắn không bao giờ e ngại khi có người khác review code của mình. Hắn đã thoát khỏi giới hạn của thử nghiệm. Mỗi đoạn code hắn viết đều đã sáng sủa, hoàn hảo và đẹp đẽ còn ý nghĩa của chúng đã hiển nhiên như cuộc sống.
Hắn đã đạt đến ngưỡng thần bí của Đạo.

Theo bản dịch của anh HiepTH

Đạo code (Phần 1: Cõi hư vô)

Lập trình sư nói:

"Khi ngươi học được cách lấy mã lỗi từ trong đoạn code bắt lỗi, ngươi có thể xuống núi"

1.1

Hình thành một cách thần bí, sinh ra từ hư vô. Đơn độc và bất động nhưng không ngừng vận động. Là nguồn gốc của mọi chương trình. Ta không biết tên nó là gì nên ta gọi nó là Đạo.

Đạo mà hay thì hệ điều hành sẽ hay. Hệ điều hành mà hay thì trình biên dịch sẽ hay. Trình biên dịch mà hay thì chương trình ắt sẽ hay. Chương trình hay thì bá tính an cư lạc nghiệp, vũ trụ thuận hòa.

Đạo bao trùm vạn vật, lan toả thiên hà. Tựa như cơn gió mát giữa mùa viêm nhiệt, như cơn mưa rào giữa tiết hanh khô.

1.2

Đạo sinh máy ngữ, máy ngữ sinh hợp ngữ, hợp ngữ sinh trình dịch, trình dịch sinh ngôn ngữ, ngôn ngữ biến hóa vô cùng (*).
Nay vạn ngữ đã hình thành. Một ngữ dù thấp kém đến đâu vẫn có mục đích của nó. Mỗi ngữ đều thể hiện tính âm dương của phần mềm. Mỗi ngữ đều có một vị trí trong Đạo.

Tuy nhiên nếu được lựa chọn, các ngươi không nên chọn ngữ COBOL để lập trình.

(*) Chú giải
Ngữ: Ngôn ngữ (lập trình)
Máy ngữ: ngôn ngữ máy - machine language
Hợp ngữ: assembly
Trình dịch: compiler

1.3

Khởi thủy từ Đạo. Đạo sinh thời gian và tài nguyên. Nên thời gian và tài nguyên chính là khía cạnh âm dương của Đạo vậy. (*)
Người lĩnh hội được Đạo luôn có đủ thời gian và tài nguyên để đạt được mục đích của mình. Kẻ không lĩnh hội được Đạo thì luôn thiếu. Không thể khác được.

(*) Chú giải
Thời gian và tài nguyên: Resource & Time, hai yếu tố tính size của phần mềm

1.4

Kẻ khôn ngoan khi nghe về Đạo thì làm theo. Kẻ bình thường khi nghe về Đạo thì nghiên cứu. Kẻ ngu dốt khi nghe về Đạo thì cười nhạo.
Nếu không có kẻ cười nhạo chắc hẳn đã không thành Đạo.


Âm cao tất khó nghe
Tiến lên dễ có đường
Nhân tài hay nở muộn
Code ngon lỗi như thường

Theo bản dịch của anh HiepTH

Tuesday, May 25, 2010

Làm việc với Database trên Android (P2)

Lần trước mình đã đề cập đến việc tạo một lớp quản lý chung với một số tác vụ:
- Tạo database
- Chạy câu SQL
- Tạo table
- ....
Lần này mình xin giới thiệu để tạo một thể hiện của một trường trong Database SQLite
Trong SQLite chỉ có 3 kiểu dữ liệu là: INT, TEXT, BOOL
Vì thế ta cần tạo một biến enum chứa một bộ các hằng số về kiểu dữ liệu này. Sau đó implement một số phương thức như trong VD sau


package com.tannm.doan.taskmanager.database;

import java.util.Calendar;
import android.content.*;

public class DataField {
// types
public static enum Type {
INT, TEXT, BOOL
};

// fields
private Calendar dateOut = Calendar.getInstance();

// fields
private DataRow dataRow = null;
private ContentValues values = null;

// fields
private int index = 0;
private String sName = "";
private Type fieldType = Type.INT;
private boolean bCanBeNull = true;
private boolean bPrimaryKey = false;

// methods
public DataField(int index, String sName, Type fieldType,
boolean bCanBeNull, boolean bPrimaryKey) {
this.index = index;
this.sName = sName;
this.fieldType = fieldType;
this.bCanBeNull = bCanBeNull;
this.bPrimaryKey = bPrimaryKey;
}

public String getColumnDefinition() {
String s = sName + " " + getSqlType(fieldType);
if (bPrimaryKey)
s += " PRIMARY KEY";
if (!bCanBeNull)
s += " NOT NULL";
return s;
}

public Type getType() {
return fieldType;
}

public int getIndex() {
return index;
}

public String getSqlType(Type value) {
switch (value) {
case INT:
return "INTEGER";
case TEXT:
return "TEXT";
case BOOL:
return "INTEGER";
}
return "TEXT";
}

public void setParentRow(DataRow dataRow) {
this.dataRow = dataRow;
this.values = this.dataRow.getContentValues();
}

public String getName() {
return sName;
}

// getters
public String asString() {
return values.getAsString(sName);
}

public long asLong() {
return values.getAsLong(sName);
}

public boolean asBoolean() {
return (values.getAsLong(sName) == 1);
}

public boolean isNull() {
return (values.get(sName) == null);
}

public Calendar asCalendar() {
dateOut.setTimeInMillis(values.getAsLong(sName));
return dateOut;
}

// setters
public void set(String value) {
values.put(sName, value);
}

public void set(long value) {
values.put(sName, value);
}

public void set(boolean value) {
int i = (value) ? 1 : 0;
values.put(sName, i);
}

public void set(Calendar value) {
values.put(sName, value.getTimeInMillis());
}

public void setNull() {
values.put(sName, (String) null);
}
}

Monday, May 24, 2010

Làm việc với Database trên Android (P1)

Cách lưu trữ dữ liệu trên Android bao gồm 3 cách:
+ Sử dụng Shared Preferences
+ Sử dụng file
+ Sử dụng SQLite
Với Shared Preferences (Là lưu dữ liệu dưới dạng một cặp khóa - dữ liệu dưới dạng file xml, rất tiện cho việc lưu trữ cấu hình của ứng dụng) và lưu dữ liệu dưới dạng File thì đơn giản nên mình không đề cập ở đây.
Kiến thức về SQLite mình cũng không nhắc đến nữa, vì mình đã có bài nói về vấn đề này trước đây rồi.
Kinh nghiệm của mình để làm việc với SQLite là:
+ Tạo một lớp quản lý chung với một số tác vụ:
-Tạo database
-Chạy câu SQL
-Tạo table
....
+ Tạo một lớp thể hiện một trường trong database bởi SQLite chỉ hỗ trợ 3 loại dữ liệu nên việc này cũng rất đơn giản.
+ Tạo một lớp thể hiện cho một dòng (row) dữ liệu
+ Tạo một lớp cho thể hiện một table.
Sau đây chúng ta sẽ đi lần lượt từng công việc trên


Đầu tiên ta sẽ đi từ việc tạo một lớp quản lý chung việc tạo database cụ thể bạn có thể xem VD để hiểu việc này
package com.tannm.doan.taskmanager.database;

import java.util.Vector;
import com.tannm.doan.taskmanager.R;
import android.content.*;
import android.database.sqlite.*;
import android.database.*;

public class Database {
// types
public enum Result {
Success, errUnknown, errCantInsertNewData, errCantUpdateData, errCantCreateTable, errNoDbAccess, errCantGetDataFromTable, errCantFindData, errCantGetData, errCantDeleteData, errTableNotExists, errCantSetValuesForDataRow, errCantGetValuesFromDataRow,
};

// fields
public static final String sTableNameAppointments = "Appointments";
public static final String sTableNameTasks = "Tasks";
public static final String sTableNameNotes = "Notes";
public static final String sTableNameAlarms = "Alarms";
public static final String sTableNameEvent = "Event";

// fields
private final String dbName = "TaskManagerDatabase.db";
private Context ctx = null;
private SQLiteDatabase db = null;
private Result resultDbTablesCreated = Result.errUnknown;

// methods
public Database(Context context) {
ctx = context;
open();
createTables();
}

public final String getName() {
return dbName;
}

public static int getErrDesc(Result result) {
int msgId = R.string.errUnknown;
if (result == Result.errCantInsertNewData)
msgId = R.string.errCantInsertNewData;
if (result == Result.errCantUpdateData)
msgId = R.string.errCantUpdateData;
if (result == Result.errCantCreateTable)
msgId = R.string.errCantCreateTable;
if (result == Result.errNoDbAccess)
msgId = R.string.errNoDbAccess;
if (result == Result.errCantGetDataFromTable)
msgId = R.string.errCantGetDataFromTable;
if (result == Result.errCantFindData)
msgId = R.string.errCantFindData;
if (result == Result.errCantGetData)
msgId = R.string.errCantGetData;
if (result == Result.errCantDeleteData)
msgId = R.string.errCantDeleteData;
if (result == Result.errTableNotExists)
msgId = R.string.errTableNotExists;
if (result == Result.errCantSetValuesForDataRow)
msgId = R.string.errCantSetValuesForDataRow;
if (result == Result.errCantGetValuesFromDataRow)
msgId = R.string.errCantGetValuesFromDataRow;
return msgId;
}

public boolean open() {
boolean bSuccess = false;
// open / create database
db = ctx.openOrCreateDatabase(dbName, Context.MODE_PRIVATE, null);
// test result
if (isOpened()) {
bSuccess = true;
} else {
db = null;
}
return bSuccess;
}

public void close() {
if (isOpened())
db.close();
}

public boolean isOpened() {
if (db != null)
return true;
return false;
}

public SQLiteDatabase getSQLiteDb() {
return db;
}

public boolean delete() {
close();
return ctx.deleteDatabase(dbName);
}

public boolean execSQL(String sql) {
boolean bSuccess = false;
try {
if (isOpened())
db.execSQL(sql);
bSuccess = true;
} catch (SQLException e) {
}
return bSuccess;
}

public boolean tableExists(String sTableName) {
boolean bResult = false;
if (isOpened()) {
String sql = "select name from sqlite_master where type = 'table' and name = '%s'";
sql = String.format(sql, sTableName);
Cursor cr = db.rawQuery(sql, null);
if (cr.getCount() > 0)
bResult = true;
cr.close();
}
return bResult;
}

private void createTables() {
resultDbTablesCreated = Database.Result.errUnknown;
if (isOpened()) {
Vector vecDataRows = new Vector();
vecDataRows.add(new DataRowAppointment(this));
vecDataRows.add(new DataRowTask(this));
vecDataRows.add(new DataRowNote(this));
vecDataRows.add(new DataRowAlarm(this));

for (int i = 0; i < vecDataRows.size(); i++) {
DataTable dataTable = new DataTable(vecDataRows.get(i));
if (dataTable.createTable()) {
resultDbTablesCreated = Database.Result.Success;
} else {
resultDbTablesCreated = Database.Result.errCantCreateTable;
break;
}
}
} else {
resultDbTablesCreated = Database.Result.errNoDbAccess;
}
}

public boolean tablesCreated() {
return resultDbTablesCreated == Result.Success;
}

public synchronized boolean databaseReady() {
return (isOpened() && tablesCreated());
}

public Result tablesCreationResult() {
return resultDbTablesCreated;
}
}


Hãy đọc và suy ngẫm về ứng dụng cho các ví dụ tiếp theo.
(Còn tiếp)