Sunday, December 12, 2010

Chỉ dẫn lập trình game 3D trên Android (P5)

Đang chờ deploy cái web viết tiếp loạt bài về OpenGL vậy.
Bài hôm nay tôi sẽ giới thiệu cho các bạn cách vẽ một hình đa giác cách xoay chúng như thế nào. Bài trước các bạn đã được học về GLSurfaceView và hãy chắc chắn rằng bạn đã đọc nó vì nó rất quan trọng cho bài hôm nay.
Loạt bài trước tôi cũng đã giới thiệu với các bạn một số khái niệm về đỉnh, tam giác, đa giác. Hôm nay trước khi vào vẽ đa giác tôi sẽ giới thiệu cho các bạn thêm một số khái niệm khác.
1, Cạnh:
Cạnh là một đường thẳng nối 2 đỉnh với nhau. Chúng là đường viền của mặt và đa giác. Trong không gian 3D một cạnh có thể là một đường giao nhau của 2 mặt, do đó khi cạnh này chuyển đổi sẽ dẫn đến sự thay đổi theo của các đỉnh, các mặt kết nối với chúng.
Trong OpenGL ES bạn không định nghĩa các cạnh, thay vào đó bạn định nghĩa các mặt bởi các đỉnh khi đó ta sẽ có các cạnh.
Nếu bạn muốn sửa, thay đổi mộ cạnh bạn có thể thay đổi 2 đỉnh tạo nên cạnh đó.
2, Mặt:
Một cách tổng quát, mặt trong toán học là đa tạp n-1 chiều trong không gian n chiều. Trong tô pô, một mặt hai chiều được gọi là mặt đơn giản nếu nó đồng phôi với một hình vuông trên mặt phẳng hai chiều.
Ví dụ: mặt bán cầu là một mặt đơn giản, còn toàn bộ mặt cầu lại không phải là một mặt đơn giản.
Trong không gian ba chiều, mặt được mặc định là đa tạp hai chiều, là tập hợp những điểm trong hệ tọa độ Đề các ba chiều Oxyz thỏa phương trình: z = f(x,y).
Trong OpenGL ES ta hiểu rằng có thể chia mặt tới mức nhỏ nhất khi đó một mặt chỉ là một tam giác, chúng được tạo lên bởi 3 đỉnh, một chuyển đổi của mặt sẽ ảnh hưởng tới các đỉnh, cạnh liên kết với nó.
Khi uốn khúc trên bề mặt của bạn điều này rất quan trọng để thực hiện các điều khiển đúng hướng của nó. Khi đó bạn phải điều khiển những gì sẽ sảy ra với mặt phía trước và cả mặt phía sau nữa. Tại sao điều này lại rất quan trọng bởi vì hiệu năng của ứng dụng sẽ tăng lên khi bạn không phải vẽ cả 2 mặt mà chỉ thực hiện vẽ mặt phía trước. Đó là ý tưởng tốt sử dụng sự uốn khúc cho tất cả các dự án của bạn. Bạn có thể định nghĩa phía trên của bề mặt bằng cách.
gl.glFrontFace(GL10.GL_CCW); // OpenGL docs
gl.glEnable(GL10.GL_CULL_FACE); // OpenGL docs
gl.glCullFace(GL10.GL_BACK); // OpenGL docs

3, Render:
Thời gian để bạn đưa một cái j đó ra ngoài màn hình, có 2 phương thức để bạn làm điều này, 2 phương thức đó là:
public abstract void glDrawArrays(int mode, int first, int count); // OpenGL docs

public abstract void glDrawElements(int mode, int count, int type, Buffer indices); // OpenGL docs

Một số render cơ bản nhất:
GL_POINTS: Render một điểm ra màn hình.
GL_LINE_STRIP: Render một dãy các phân đoạn đường thẳng nối liền nhau, nhưng không nối điểm đầu và điểm cuối.
GL_LINE_LOOP: Giống như GL_LINE_STRIP nhưng không nối điểm đầu và điểm cuối.
GL_LINES: Render một đường thẳng.
GL_TRIANGLES: Render một tam giác.
GL_TRIANGLE_STRIP: Render một loạt các tam giác theo thứ tự VD: Ta có các đỉnh v0, v1, v2, v3, v4, v5 khi đó các tam giác được render là (v0, v1, v2) sau đó là (v2, v1, v3) (lưu ý thứ tự), tiếp theo là (v2, v3, v4) ...
Gl_triangle_strip
GL_TRIANGLE_FAN: Render một loạt các tam giác giống như GL_TRIANGLE_STRIP nhưng các tam giác được vẽ giống như một hình quạt dựa vào một gốc.
Gl_triangle_strip

Giờ chúng ta sẽ vẽ thử một tam giác:
Bạn vẫn còn project hôm trước chứ, ta sẽ thay đổi một chút để có thể render ra được một tam giác.
Ta chỉ cần sửa lại lớp VortexRenderer lại như sau:

package vn.vndev.android.opengl;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLSurfaceView;

/**
 * 
 * @author TanNM
 * 
 */
public class VortexRenderer implements GLSurfaceView.Renderer {
 private static final String LOG_TAG = VortexRenderer.class.getSimpleName();

 private float red = 0f;
 private float green = 0f;
 private float blue = 0f;

 // a raw buffer to hold indices allowing a reuse of points.
 private ShortBuffer indexBuffer;

 // a raw buffer to hold the vertices
 private FloatBuffer vertexBuffer;

 private short[] indicesArray = { 0, 1, 2 };
 private int nrOfVertices = 3;

 private float angle;

 @Override
 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
  // preparation
  gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
  initTriangle();
 }

 @Override
 public void onSurfaceChanged(GL10 gl, int w, int h) {
  gl.glViewport(0, 0, w, h);
 }

 public void setAngle(float angle) {
  this.angle = angle;
 }

 @Override
 public void onDrawFrame(GL10 gl) {
  // define the color we want to be displayed as the "clipping wall"
  gl.glClearColor(red, green, blue, 1.0f);

  // clear the color buffer to show the ClearColor we called above...
  gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

  // set rotation
  gl.glRotatef(angle, 0f, 1f, 0f);

  gl.glColor4f(0.2f, 0.5f, 0.2f, 0.5f);
  gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
  gl.glDrawElements(GL10.GL_TRIANGLES, nrOfVertices,
    GL10.GL_UNSIGNED_SHORT, indexBuffer);
 }

 private void initTriangle() {
  // float has 4 bytes
  ByteBuffer vbb = ByteBuffer.allocateDirect(nrOfVertices * 3 * 4);
  vbb.order(ByteOrder.nativeOrder());
  vertexBuffer = vbb.asFloatBuffer();

  // short has 4 bytes
  ByteBuffer ibb = ByteBuffer.allocateDirect(nrOfVertices * 2);
  ibb.order(ByteOrder.nativeOrder());
  indexBuffer = ibb.asShortBuffer();

  float[] coords = { -0.5f, -0.5f, 0f, // (x1, y1, z1)
    0.5f, -0.5f, 0f, // (x2, y2, z2)
    0f, 0.5f, 0f // (x3, y3, z3)
  };
  vertexBuffer.put(coords);
  indexBuffer.put(indicesArray);
  vertexBuffer.position(0);
  indexBuffer.position(0);
 }

 public void setColor(float r, float g, float b) {
  red = r;
  green = g;
  blue = b;
 }
}

Để hình tam giác của ta quay với tốc độ tùy vào vị chí mà ta click ta set thêm góc giống như
renderer.setAngle((event.getX() + event.getY()) / 20);
trong phương thức
public boolean onTouchEvent(final MotionEvent event)
của lớp VortexView

Bạn có thể download mã nguồn của ứng dụng về chạy thử.

Friday, December 3, 2010

Chỉ dẫn lập trình game 3D trên Android (P4)

Chúc các bạn một ngày tốt lành, hôm qua đi đá bóng về đau chân quá ko làm việc được nên ngồi viết bài tiếp vậy!
Hôm trước ứng bài demo chạy tốt chứ? Hôm nay tôi sẽ giải thích sơ qua một chút về ứng dụng đó.
1, Trước hết là lớp GLSurfaceView:
Bắt đầu được hỗ trợ trên Android từ phiên bản 1.5.
Lớp này hỗ trợ bạn kết nối vào OpenGL từ view bằng cách quản lý một bề mặt và ghép chúng vào hệ thống view của Android. Nó quản lý một EGL display, cho phép OpenGL thực hiện render vào một bề mặt.
Tạo khả năng giúp OpenGL ES làm việc với vòng đời của Activity.
Tạo khả năng lựa chọn định dạng frame buffer pixel một cách thích hợp.
Quản lý các thread rendering riêng biệt cho phép tạo các chuyển động mượt mà :D.
Cung cấp tools debugging một cách đơn giản cho tracing các lời gọi API OpenGL ES và kiểm tra lỗi.

Bạn muốn tiến xa hơn trong OpenGL thì đây là nơi mà bạn cần phải bắt đầu.
Để sử dụng GLSurfaceView bạn chỉ cần gọi phương thức:
 public void  setRenderer(GLSurfaceView.Renderer renderer);
 
có trong GLSurfaceView. Để tùy biến các thuộc tính của GLSurfaceView thì trước khi gọi phương thức setRenderer bạn có thể gọi một số các phương thức khác như:
 setDebugFlags(int);
 setEGLConfigChooser(boolean);
 setEGLConfigChooser(EGLConfigChooser);
 setEGLConfigChooser(int, int, int, int, int, int);
 setGLWrapper(GLWrapper);
 
Mặc định GLSurfaceView sẽ tạo một bề mặt với định dạng mặc định là PixelFormat.RGB_565. Nếu bạn cần một bề mặt trong suốt bạn có thể gọi phương thức getHolder().setFormat(PixelFormat.TRANSLUCENT).
Một GLSurfaceView phải được thông báo khi Activity là paused và resumed. GLSurfaceView clients cần phải gọi phương thức onPause() khi activity pauses và gọi onResume() khi activity resumes. Lời gọi cho phép GLSurfaceView thực hiện pause hay resume các thread rendering, và nó cũng cho phép GLSurfaceView release hay khởi tạo lại các đối tượng OpenGL display.

2, GLSurfaceView.Renderer:
Là một render interface.
Render sẽ được gọi trên một thread riêng biệt vì thế hiệu năng rendering sẽ tách rời UI thread. Nhưng các render cần giao tiếp với các UI thread bởi vì UI thread là nơi nhận được các sự kiện input. Các render có thể giao tiếp bằng bất kỳ kỹ thuật java chuẩn nào cho giao tiếp cross-thread, hoặc đơn giản hơn là sử dụng phương thức queueEvent(Runnable).
Khi bạn implementation render này bạn cần khai báo các phương thức sau:
 // Gọi khi một bề mặt được khởi tạo hay khởi tạo lại
 public void onSurfaceCreated(GL10 gl, EGLConfig config) 

 // Gọi để vẽ frame hiện tại.
 // Phương thức này sẽ chịu trách nhiệm vẽ frame hiện tại.
 public void onDrawFrame(GL10 gl)

 // Gọi khi bề mặt thay đổi kích thước
 public void onSurfaceChanged(GL10 gl, int width, int height)
 

onSurfaceCreated: Đây là nơi tốt để bạn cài đặt một điều gì đó mà bạn không thường xuyên thay đổi trong vòng đời của rendering.
onDrawFrame: Đây là nơi sự kiện vẽ thực sự diễn ra.
onSurfaceChanged: Nếu thiết bị của bạn cho phép chuyển giữa màn hình nằm ngang hay nằm dọc, bạn sẽ có lời gọi tới phương thức này khi sự chuyển đổi đó diễn ra. Bạn có thể thiết lập lại các tỷ lệ mới ở đây

Bạn có thể download mã nguồn của bài trước và bài này tại đây

Thursday, December 2, 2010

Chỉ dẫn lập trình game 3D trên Android (P3)

Hôm nay tôi sẽ chỉ các bạn lập trình OpenGL trong Android, cách sử dụng OpenGL trong cả 2 ngôn ngữ Java và C. Điều này rất quan trọng trong việc tái sử dụng mã nguồn trong Java và C cũng như sử dụng được các tính năng tốt nhất mà mỗi ngôn ngữ cung cấp.
Bất kỳ một nhà phát triển game nào cũng cần phải biết OpenGL là kim chỉ nam cho nhưng nhà phát triển game chuyên nghiệp. Bạn khó có thể tìm thấy một game mạnh mẽ nào mà không sử dụng API của OpenGl, bởi vì nó có khả năng tận dụng và tăng tốc phần cứng hơn hầu hết tất cả các phần mềm renderer khác.
OpenGL có thể là một môn học đáng sợ cho người mới bắt đầu (that's true for me :D). Nhưng bạn cũng không thể thành thạo OpenGL tới mức biết được làm thế nào để nó có thể vẽ các phần tử đó với OpenGL API. Bạn chỉ cần sự ham học hỏi, mong muốn tìm hiểu một công cụ mạnh mẽ để xây dựng các ứng dụng đồ họa như game.
Trong giới hạn của bài này tôi không thể chỉ bạn tất cả về OpenGL mà tôi chỉ đưa ra ví dụ sử dụng chúng trong ứng dụng Android, dựa vào đó và các API của OpenGL bạn có thể tùy biến theo ý của bạn.
Tham khảo thêm về OpenGL tại:
http://www.opengl.org/sdk/docs/man/
http://www.falloutsoftware.com/tutorials/gl/gl0.htm
http://mathworld.wolfram.com/OrthographicProjection.html
http://nehe.gamedev.net/

Về thiết bị thì khỏi bàn, hiện nay Android ngày càng trở nên mạnh mẽ cho việc phát triển các ứng dụng đồ họa.
Để tận dụng tối đa hiệu năng của CPU, Google đã đưa OpenGL Embedded System (OpenGL-ES) vào trong hệ điều hành Android.
OpenGL-ES cung cấp một loạt các API làm cho game có hiệu năng sử lý cao, tăng tốc phần cứng. Đây là các Java API, để xây dựng lại chúng từ C là một công việc rất phức tạp. Tôi sẽ chỉ các bạn cách thức làm việc trên cả Java và C.
Bắt đầu bằng một số khái niệm cơ bản về OpenGL

1, Đỉnh: là một điểm trong không gian 3D trong OpenGl có thể chỉ là 2 tọa độ (x, y) hoặc nhiều tọa độ như (x, y, z, w) ở đó w là tùy chọn và mặc định nó có giá trị là 1.0, tương tự trục z cũng là tùy chọn và giá trị mặc định của nó là 0. Tất các các đối tượng được vẽ bởi các đỉnh hay chính là các điểm, do đó kho nói tới điểm cũng chính là 1 đỉnh.
2, Tam giác: được tạo nên bởi 3 điểm, trong OpenGL chúng ta có thể dùng 3 đỉnh để tạo nên 1 tam giác.
3, Đa giác: được tạo nên bởi nhiều hơn 2 điểm, tam giác cũng là 1 đa giác.

Nào trước hết chúng ta bắt đầu chúng với Java:
Tạo một Android project mới, với activity là Vortex và một số các lớp sau:
package vn.vndev.android.opengl;

import android.app.Activity;
import android.os.Bundle;

/**
 * 
 * @author TanNM
 *
 */
public class Vortex extends Activity {
 private static final String LOG_TAG = Vortex.class.getSimpleName();
 private VortexView vortexView;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  // Hien thi toan man hinh
  this.requestWindowFeature(Window.FEATURE_NO_TITLE); 
  getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
    WindowManager.LayoutParams.FLAG_FULLSCREEN);
  vortexView = new VortexView(this);
  setContentView(vortexView);
 }
}
package vn.vndev.android.opengl;

import android.content.Context;
import android.opengl.GLSurfaceView;
import android.view.MotionEvent;

/**
 * 
 * @author TanNM
 *
 */
public class VortexView extends GLSurfaceView {
 private static final String LOG_TAG = VortexView.class.getSimpleName();
 private VortexRenderer renderer;

 /**
  * 
  * @param context
  */
 public VortexView(Context context) {
  super(context);
  renderer = new VortexRenderer();
  setRenderer(renderer);
 }

 public boolean onTouchEvent(final MotionEvent event) {
  queueEvent(new Runnable() {
   public void run() {
    renderer.setColor(event.getX() / getWidth(), event.getY() / getHeight(), 1.0f);
   }
  });
  return true;
 }
}
package vn.vndev.android.opengl;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLSurfaceView;

/**
 * 
 * @author TanNM
 *
 */
public class VortexRenderer implements GLSurfaceView.Renderer {
 private static final String LOG_TAG = VortexRenderer.class.getSimpleName();

 private float red = 0.7f;
 private float green = 0.5f;
 private float blue = 0.8f;

 @Override
 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
  gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
 }

 @Override
 public void onSurfaceChanged(GL10 gl, int w, int h) {
  gl.glViewport(0, 0, w, h);
  float ratio = (float) w / h;
  gl.glMatrixMode(GL10.GL_PROJECTION);
  gl.glLoadIdentity();
  gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
 }

 @Override
 public void onDrawFrame(GL10 gl) {
  gl.glClearColor(red, green, blue, 1.0f);
  gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
 }

 public void setColor(float r, float g, float b) {
  red = r;
  green = g;
  blue = b;
 }
}
Ok giờ bạn hãy chạy thử ứng dụng và click lên các vị trí khác nhau của màn hình. Thấy j nhỉ?

Friday, November 26, 2010

Tối ưu hóa ứng dụng J2ME

Ứng dụng trên J2ME thường bị chậm bởi hạn chế về bộ nhớ, vậy làm sao để chúng ta có thể tối ưu hóa ứng dụng J2ME của mình. Đây là một số chỉ dẫn có ích cho bạn:
1, Khởi tạo và hủy bỏ đối tượng: Khi tạo một đối tượng bằng từ khóa new chúng sẽ chiếm một bộ nhớ của thiết bị do đó chỉ nên tạo đối tượng khi cần thiết, nên sử dụng lại các đối tượng đã có. Không nên tạo đối tượng trong các vòng lặp

for (int i = 0; i < length; i++) {
ClassName p = new ClassName ();
results[i] = p.calculateResult();
}


Chú ý: Khi sử dụng toán tử "+" với các chuỗi String nhỏ khi đó nó sẽ tạo ra các đối tượng String không cần thiết vì thế nếu sử dụng thao tác với chuỗi tôi khuyến cáo các bạn nên dùng StringBuffers hoặc mảng char

2, Trong vòng lặp bạn không nên sử dụng các thuật toán phức tạp, các tính toán trong vòng lặp. Một số cách khai báo vòng lặp tối ưu:


//Một vòng lặp tồi (Vòng lặp a)
for (int i = 0; i < v.size(); i++) {
// Do something
}
//Tốt hơn vòng lặp a (Vòng lặp b)
int size = v.size();
for (int i = 0; i < size; i++) {
// Do something
}
//Tốt hơn vòng lặp b (Vòng lặp c)
for (int i = 0,n = v.size(); i < n; i++) {
// Do something
}
/* Nếu thứ tự trong đối tượng bạn duyệt không quan trọng, bạn hãy duyệt ngược lại như bên dưới để tranh các biến local trong stack. So sánh với 0 luôn hiệu quả nhất trong các ngôn ngữ */
//Tốt hơn vòng lặp c
for( int i = v.size() - 1; i >= 0; i-- ) {
// Do something
}


3, Phân chia ứng dụng của bạn ra: Một MIDP trong thời gian thực thi sẽ load các lớp mà chúng cần, khi đó nếu một lớp lớn sẽ làm tốn bộ nhớ hơn. Hãy chia ra các lớp nhỏ nếu có thể.
Trong câu lệnh import, chỉ rõ lớp mà bạn sử dụng, không nên để như vn.vndev.mzone.util.* mà nên để như vn.vndev.mzone.util.StringUtil
Khai báo biến, khai báo là public nếu có thể bởi vì khi bạn khai báo private bạn sẽ phải truy xuất chúng thông qua phương thức getter và setter khi đó sẽ tốn hiệu năng hơn.

4, Tránh sử dụng thread, khi sử dụng một vài synchronization, hiệu năng của hệ thống sẽ giảm xuống 2/3 lần

5, Sử dụng lại các thành phần giao diện giống như Forms,TextBox... thay vì khởi tạo chúng nếu có thể, điều này làm giảm bộ nhớ sử dụng của ứng dụng.

6, Tránh thường xuyên mở Recordstores

7, Sử dụng loại phù hợp cho biến trong J2ME, như vậy sẽ tránh lãng phí bộ nhớ RAM

8, Sử dụng lại biến: Cố gắng sử dụng lại biến cang nhiều càng tốt, bởi vì tạo nhiều biến làm giảm hiệu năng và tốn dung lượng bộ nhớ. Bằng cách khai báo các biến global và nhớ ko làm ảnh hưởng tới design của lớp.

9, Chia các mảng đa chiều thành các mảng 1 chiều, truy xuất mảng đa chiều luôn làm giảm hiệu năng hệ thống.

10, Tính toán: Nên sử dụng các toán tử dịc bit thay vì dùng các toán tử bình thường khác ví dụ: sử dụng int a = 11<< 1 thay vì dùng int a = 11* 2.
Thay vì nhân chia ta nên sử dụng cộng và trừ càng nhiều càng tốt.

11, Sử dụng lời gọi Native Methods: giống như String.indexOf(), String.lastIndexOf()

12, Sử dụng obfuscater trong ứng dụng của bạn.
Đừng gọi garbage collector trong chương trình của bạn:
System.gc();

Tránh sử dụng synchronization.

13, Cố gắng giữ tính đa hình của đối tượng bằng các interface.

14, Sử dụng Singleton Design Pattern.

Thursday, November 18, 2010

Chỉ dẫn lập trình game 3D trên Android (P2)

Bài hôm nay tôi sẽ hướng dẫn các bạn biên dịch native code trong ứng dụng Android.
JNI là viết tắt của Java Native Interface. Sử dụng JNI chúng ta có thể gọi các hàm được viết trong những ngôn ngữ khác từ Java.
Trước hết bạn tạo một Android project tạo một activity MainActivity:

package com.vndev.jni.activities;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import jni.Natives;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {
    private static final String LIB = "libch02.so";
    private static final String LIB_PATH = "/data/data/com.vndev.jni.activities/files/" + LIB;

    /** Called when the activity is first created. */

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        try {

            // Install lib

            System.out.println("Installing LIB: " + LIB);

            // Copy lib from assests folder to files folder:

            // /data/data/PKG_NAME/files

            writeToStream(getAssets().open(LIB), openFileOutput(LIB, 0));

            // Load Lib

            System.load(LIB_PATH);

            // Run it

            String[] argv = { "MyLib", "arg1", "arg2" };

            Natives.LibMain(argv);

        } catch (Exception e) {

            e.printStackTrace();

        }

    }



    /**

     * Write to a stream

     * 

     * @param in

     * @param out

     * @throws IOException

     */

    public static void writeToStream(InputStream in, OutputStream out)

            throws IOException {

        byte[] bytes = new byte[2048];

        for (int c = in.read(bytes); c != -1; c = in.read(bytes)) {

            out.write(bytes, 0, c);

        }

        in.close();

        out.close();

    }

}

Trong activity này ta sẽ load thư viện từ C vào bằng cách gọi:

System.load(LIB_PATH);


Activity này gọi một phương thức trong lớp Natives:
Natives.LibMain(argv);


Phương thức này được khai báo:
public static native int LibMain(String[] argv);


Phương thức này có từ khóa native, nó sẽ gọi phương thức LibMain trong thư viên được load ở trên.
Toàn bộ nội dung class Natives sẽ như sau:
package jni;

public class Natives {
    /**
     * Native Main Doom Loop
     * @param argv
     * @return
     */
    public static native int LibMain(String[] argv);
    
    /**
     * This fires on messages from the C layer
     * @param text
     */
    @SuppressWarnings("unused")
    private static void OnMessage(String text, int level) {
        System.out.println("OnMessage text:" + text + " level=" + level);
    }
}

Để tạo file header cho lớp này ta sử dụng javah trong java như sau
Javah -jni Natives


Khi đó ta có file header như sau
/* DO NOT EDIT THIS FILE - it is machine generated */

#include 

/* Header for class jni_Natives */



#ifndef _Included_jni_Natives

#define _Included_jni_Natives

#ifdef __cplusplus

extern "C" {

#endif

/*

 * Class:     jni_Natives

 * Method:    LibMain

 * Signature: ([Ljava/lang/String;)I

 */

JNIEXPORT jint JNICALL Java_jni_Natives_LibMain

  (JNIEnv *, jclass, jobjectArray);



//implements method call by java native



#ifdef __cplusplus

}

#endif

#endif



Giờ bạn cần tạo một file C khác dựa vào khai báo trên cài đặt phương thức này trong C và biên dịch chúng, chạy để thấy kết quả.

Download source

Thursday, November 11, 2010

Chỉ dẫn lập trình game 3D trên Android (P1)

Trước khi bạn bắt đầu, hãy chuẩn bị cho mình một số kỹ năng cần thiết để bắt đầu:
Về ngôn ngữ lập trình, không chỉ Java mà bạn cần biết cả ngôn ngữ lập trình C.
Bởi vì Java cho bạn cách thức lập trình hướng đối tượng và nó chạy trên Android, nhưng chỉ có C mới có sức mạnh mà một lập trình viên games cần tới.
Một số kỹ năng cứng bạn cần biết trong Android là:
    Activities, view, layout....
    Services.
    Content providers.
    Broadcast receivers.
Một số kỹ năng khác là: Hiểu biết cơ bản về Linux và shell scripting:
    Bạn cần biết một số câu lệnh cơ bản như: liệt kê các file, cài đặt một thành phần, một ứng dụng...

Yêu cầu về phần mềm:
    Hệ điều hành Ubuntu.
    Eclipse.
    Android SDK.
    Java JDK.
(Còn tiếp)

11 Lời Khuyên Của Bill Gates


1) Cuộc sống vốn không công bằng, chúng ta phải tìm cách thích ứng với nó.
2) Thế giới này không quan tâm đến cái tự ái trong bạn, mà chỉ mong bạn lập được thành tích trước khi bản thân bạn tự cảm thấy hài lòng (Nói tóm lại mặt dày là ngon).
3) Sau khi tốt nghiệp, bạn không thể có ngay mức lương 40.000$/năm, không thể trở thành chủ tịch một công ty, sở hữu một chiếc xe hơi có lắp điện thoại cho đến khi bạn tự mình kiếm được những thứ ấy. Hãy bắt tay vào làm đi, thay vì ngồi đó mà mơ mộng.
4) Nếu cho rằng thầy giáo của bạn nghiêm khắc, hãy đợi đến khi đi làm bạn sẽ biết. Thầy giáo cũng đến lúc hết nhiệm kì công tác, nhưng ông chủ thì mãi mãi là ông chủ của bạn.
5) Món "bánh nướng kẹp thịt" rẻ tiền không hề làm giảm đi giá trị của bạn. Ông bà ta có định nghĩa khác về "bánh nướng kẹp thịt", họ gọi là thời cơ. Gặp những người khi sa cơ lỡ vận, đừng trở nên bất đắc chí mà hãy sử dụng nghịch cảnh như là một động lực để vươn lên.
6) Nếu bạn lâm vào cảnh khó khăn, đừng mải dằn vặt về những lỗi lầm đã qua mà hãy rút ra bài học từ đó. Hãy cố gắng đứng vững và đi lên từ những thất bại của mình
7) Trước khi sinh ra bạn, cha mẹ bạn không như hiện tại đâu. Họ trở nên như thế là vì bao năm nay họ phải kiếm tiền nuôi bạn, giặt quần áo cho bạn. Nếu muốn diệt "ký sinh trùng" đeo bám suốt đời ba mẹ bạn, trước tiên hãy diệt trừ con rệp trong tủ áo của bạn.
8) Nhà trường có thể không còn phân biệt học sinh giỏi hay kém nhưng cuộc sống thì có đấy. Một số trường đã từ bỏ điểm kém, chỉ cần bạn muốn tìm lời giải chính xác, nhà trường sẽ cho bạn vô số cơ hội. Nhưng cuộc sống thì không phải vậy. Bạn phải không ngừng nỗ lực vươn lên để tự khẳng định mình.
9) Cuộc sống không phân chia học kỳ, bạn sẽ không có kỳ nghỉ hè, cũng ít có ông chủ nào giúp bạn phát hiện chính mình. Bạn phải tự mình phát hiện những điều ấy.
10) Truyền hình không phản ánh cuộc sống chân thực. Trong cuộc sống hiện thực, người ta phải rời khỏi tiệm cà phê để đi làm việc chứ không phải ngồi đấy xem TV.
11) Bạn hãy cư xử tế nhị với kẻ là bạn cho là nhạt nhẽo, vô vị, vì có thể một ngày nào đó bạn có thể phải làm việc với một người vô vị như thế

Tải cuốn sách này 

Wednesday, September 22, 2010

Tuesday, September 14, 2010

Đạo code(Phần 8: Phần cứng và phần mềm)

Lập trình sư nói: “Không có gió, cỏ cây bất động. Không có nhu liệu, phần cứng vô dụng”

8.1 Lập trình phu hỏi: “Thưa ! con biết 1 công ty máy tính vĩ đại hơn tất cả công ty còn lại, ví như gã khổng lồ với bầy lùn, mỗi phần nhỏ có thể sánh với thương nghiệp hoàn chỉnh. Sao lại như thế ?”
Lập trình sư nói: “Đừng hỏi ta vớ vẫn thế. Công ty lớn vì nó lớn. Nếu nó chỉ bán phần cứng, không ai mua nó. Nếu nó chỉ bán nhu liệu, vô nhân dụng đến. Nếu nó chỉ là hệ thống bảo trì, thiên hạ xem như bậc nô tỳ. Vì nó kết hợp tất cả, thiên hạ tin đó là trời !

8.2 Hôm nọ lập trình sư đến chỗ lập trình phu. Y đang bận tâm vào trò chơi điện tử cầm tay. Lập trình sư nói: ”Này, ta có thể thử nó được không ?” Lập trình phu (vẻ bất ngờ) trao nó cho lập trình sư.

“Ta thấy thiết bị yêu cầu 3 mức độ: dễ, trung bình, khó. Ngay cả mỗi thiết bị có mức độ chơi khác, khi thiết bị không chế ngự được con người, hay không được con người chế ngự.”

“Thưa ! Con cầu xin đại sư. Làm sao tìm được cách cài đặt thần bí kia” Lập trình phu hỏi, vẻ náo nức

Lập trình sư quăng thiết bị xuống sân, lấy chân nghiền nát. Ngay lúc đó, lập trình phu đạt đại ngộ !

8.3 Lần nọ, một lập trình phu làm việc trên bộ vi xử lý:
“Hãy nhìn tôi đi. Thật là sảng khoái. Tôi có hệ điều hành của chính tôi và thiết bị lưu trữ. Tôi không chia sẻ tài nguyên với bất cứ kẻ nào. Nhu liệu của tôi trước sau như một, sử dụng nhẹ tựa lông hồng. Sao bằng hữu không bỏ thứ đó và tham gia với tôi ?”

Lập trình phu trên máy tính lớn nói:
“Một máy tính lớn trông giống bậc hiền nhân đang tọa thiền giữa trung tâm dữ liệu. Đĩa cứng xếp chồng bất tận ví như đại dương bao la. Nhu liệu của nó như các góc cạnh của viên kim cương. Các chương trình, lần lượt, đi qua như dòng thiên hà chảy xiết. Đó là vì sao tại hạ cảm thấy hoan hỷ trong hiện tại”

Lập trình phu nọ nghe xong, im lặng. Nhưng cả hai đã trở thành bằng hữu cho đến cuối đời.

8.4 Phần cứng gặp phần mềm trên đường đến đỉnh Changtse. Mềm nói: “Các hạ là Âm, còn tôi là Dương. Nếu ta đi cùng nhau sẽ được nổi tiếng, kiếm được nhiều tiền” Và cả hai lên đường, mưu tính đại sự.

Rồi họ gặp Phần Dẻo (Firmware), ăn mặc rách rưới, tay chống cây gậy bước tới: “Đạo tồn tại giữa Âm và Dương. Đạo tĩnh lặng tựa mặt hồ không chút gợn sóng. Đạo không cần tên tuổi, bởi vậy không ai biết sự hiện diện của Đạo. Đạo không cần tiền bạc, Đạo đã hoàn mỹ trong Đạo. Đạo tồn tại trên Thời gian và Tài nguyên !”

Phần cứng, Phần mềm, cảm thấy hổ thẹn, bèn quay về nhà.

Tuesday, September 7, 2010

Đạo code(Phần 7: Sự thông thái hợp nhất)

Lập trình sư nói:
“Ngươi có thể giải thích một chương trình (cho ai), nhưng ngươi không thể làm cho máy tính của
hắn ta biết đọc viết”

7.1
Lập trình phu hỏi lập trình sư: “phương đông có đại cây cấu trúc người đời thường gọi ‘Cơ Quan Đầu Não Liên Hợp’. Nó bành trướng hình thể với phó chủ tịch (vice president) và kế toán phu (accountant). Nó có vô số ghi chú, đại thể tựa như ‘Cút đi!” hay “Lại đây này!”, thiên hạ chẳng ai hiểu nó nói gì. Hằng niên các tên mới được đặt vào nhánh, nhưng vô nghĩa. Thử hỏi thứ vô lý (unnatural entity) như vậy tồn tại để làm chi ?”

Lập trình sư nói: “ngươi nhận biết một đại cấu trúc và bối rối vì nó không có mục đích hợp lý. Dám hỏi ngươi có thể hứng thú với sự xoay chuyển không ngừng của nó ? Sao ngươi không tận hưởng sự nhàn nhã lập trình bên dưới các nhánh cây ấy ? Sao ngươi bị phiền bởi sự vô dụng của nó ?”

7.2
Phương đông có loài cá kình to lớn. Nó hóa thành loài hạc mà cánh bao trùm cả bầu trời. Khi nó bay qua đất liền, nó mang thông điệp từ “Cơ Quan Đầu Não Liên Hợp”, rơi đúng chỗ các lập trình phu, giống như hải cẩu vạch dấu trên bãi biển. Rồi nó men theo gió, quay về cố hương.

‘Lập trình phu mới’ dõi theo cánh chim mà trong lòng ngập tràn nghi vấn. ‘Lập trình phu trung’ kinh hãi, khiếp sợ thông điệp. ‘Lập trình sư’ bình thản, tiếp tục công việc.

7.3
Lão pháp sư từ Tháp Ngà mang đến lập trình sư món đồ cuối cùng để kiểm tra. Pháp sư đẩy cái hộp đen lớn vào văn phòng. Lập trình sư chờ đợi.

“Đây là trạm làm việc tích hợp, phân tán, đa dụng, thiết kế với hệ điều hành độc quyền, 6 đời ngôn ngữ, đa giao diện. Tạo ra nó mất vài trăm năm làm việc. Các hạ có thấy ngạc nhiên không ?” Pháp sư nói.
Lập trình sư trả lời: “Thật quá đỗi kinh ngạc.”

“Cơ Quan Đầu Não Liên Hợp đã ra lệnh rằng hãy sử dụng nó như nền cho các chương trình mới, Ngài có đồng ý không ?” Pháp sư hỏi.
Lập trình sư đáp: “tất nhiên, ta sẽ chuyển nó đến trung tâm dữ liệu ngay!”

Vài ngày sau, lập trình phu thơ thẩn đến văn phòng lập trình sư nói: “Con không tìm thấy danh sách cho chương trình mới, sư phụ biết nó ở đâu không ?”
“Nó xếp thành chồng trên nền của trung tâm dữ liệu”

7.4
Lập trình sư đi từ chương trình này đến chương trình khác không chút lo sợ. Không sự thay đổi trong quản lý nào làm hại y. Y vĩnh viễn không bị sa thảy, ngay cả dự án nếu bị hủy. Tại sao thế ? Y ngập tràn bởi Đạo !

Wednesday, August 25, 2010

10 Cách Kéo Dài Thời Lượng Pin Cho Android

‘Hiện đại thì hại điện’ là câu miêu tả dành cho các máy dùng hệ điều hành Android. Màn hình kích thước lớn, kết nối không dây phong phú, các ứng dụng mạng xã hội… đua nhau ngốn pin của máy làm pin nhanh chóng bị cạn kiệt. Nếu cảm thấy máy của mình hết pin quá nhanh thì mời bạn xem qua 10 cách khắc phục sau đây.

1. Kiểm tra thành phần nào đang ngốn pin:
Chức năng ‘Battery Use’ (trong menu Settings > About Phone > Battery use) liệt kê các thành phần đang ngốn pin của máy và hiển thị mức độ % sử dụng pin. Số % càng cao nghĩa là thành phần đó ngốn pin càng nhiều. Bạn có thể nhấn vào từng thành phần để điều chỉnh cách thức mà chúng hoạt động hoặc tắt bớt các ứng dụng để tránh lãng phí pin.

pix205
2. Điều chỉnh độ sáng màn hình:
Màn hình kích thước càng lớn, độ sáng càng cao thì càng ngốn nhiều pin. Để giảm độ sáng màn hình, bạn vào menu Settings > Sound and Display > Brightness, nhấn vào ô Automatic brightness adjustment để máy tự điều chỉnh độ sáng hoặc dùng thanh trượt bên dưới để tăng/giảm độ sáng màn hình.

Ngoài ra, rút ngắn thời gian đèn màn hình tự động tắt trong phần Screen timeout cũng giúp kéo dài thời gian sử dụng pin.

pix206
3. Tắt Wi-Fi và Bluetooth khi không cần dùng:
Kết nối Wi-Fi giúp cho các ứng dụng trong máy, nhất là các ứng dụng có kết nối mạng như Email, Facebook, Twitter, RSS… làm việc trơn tru và hiệu quả nhưng cũng vì thế mà làm cho pin hết nhanh. Nếu bạn đến những nơi không có sóng Wi-Fi hoặc tín hiệu Wi-Fi yếu thì máy sẽ dò tìm Wi-Fi liên tục, làm máy nhanh hết pin hơn. Vì vậy, nếu không cần thiết thì bạn hãy tắt Wi-Fi đi bằng cách kéo thanh trạng thái từ phía trên màn hình xuống và nhấn vào biểu tượng Wi-Fi để tắt nó đi. Làm tương tự với biểu tượng kết nối Bluetooth.

pix207

4. Tắt chức năng định vị GPS:
Không phải lúc nào bạn cũng cần đến chức năng xác định vị trí qua GPS, nếu mở GPS liên tục thì pin sẽ rất mau hết. Để tắt GPS, bạn vào menu Settings > Location and security và bỏ dấu chọn ở dòng Use GPS Satellites. Lúc này điện thoại vẫn có thể xác định vị trí của bạn thông qua Wi-Fi và các trạm thu phát sóng nhưng độ chính xác sẽ kém đi so với GPS.

pix208

Sử dụng widget Power Control:

‘Power Control’ là một widget có sẵn trong các máy Android dùng để tắt/mở nhanh các kết nối mạng, bao gồm: Wi-Fi, Bluetooth, GPS, Sync và chỉnh độ sáng màn hình. Bạn nhấn và giữ ngón tay lên vùng trống của màn hình ngoài, chọn Android Widgets > Power control để đưa widget này ra ngoài màn hình chờ. Sau này bạn có thể tắt/mở các kết nối nhanh chóng chỉ với 1 cú nhấn.

pix209

5. Tắt bớt các ứng dụng tự động đồng bộ hóa dữ liệu (auto sync):
Các ứng dụng có kết nối mạng như Email, Facebook, Twitter… đều cho người dùng điều chỉnh khoảng thời gian chương trình tự động kết nối và tải dữ liệu về. Bạn hãy vào menu Settings (hoặc Configurations của từng chương trình) > Refresh interval và tăng mức thời gian trong đó lên để giảm bớt số lần chương trình tự động tải dữ liệu về.

pix210

6. Tắt bớt các phần mềm chạy ngầm:
Các phần mềm đang chạy ngầm không những làm lãng phí pin của máy mà còn khiến máy chạy ì ạch. Bạn có thể dùng một trong các phần mềm như ‘Smart Bar’, ‘TasKiller’ hoặc ‘Advanced Task Manager’ (tải về từ Market) để xem các ứng dụng nào đang chạy ngầm bên trong điện thoại và tắt chúng đi nếu không cần thiết.

7. Gỡ bỏ bớt các ứng dụng không cần thiết:
Vào menu Settings > Applications > Manage Applications để xem danh sách các phần mềm đã cài vào máy, nếu thấy phần mềm nào không cần thiết thì hãy gõ bỏ chúng đi để tránh gây lãng phí tài nguyên của máy và tự động chạy gây ngốn pin. Gỡ bỏ bằng cách chọn vào phần mềm đó, bấm Uninstall.

pix211

8. Tắt bớt các widget không sử dụng:
Màn hình nhiều widget mang lại nhiều tiện lợi cho bản thân nhưng nếu sử dụng quá nhiều widget thì chúng sẽ kết nối mạng nhiều lần hơn, máy phải xử lý nặng nề hơn và do đó cũng hao pin hơn. Để gỡ các widget xuống, bạn nhấn và giữ ngón tay lên widget đó cho đến khi phía dưới màn hình hiện ra biểu tượng giỏ rác, bạn kéo widget đó bỏ vào giỏ rác là xong.

9. Tắt chức năng Live Wallpaper:
Live Wallpaper đem lại sức sống cho màn hình điện thoại với những hiệu ứng hình ảnh sống động như thật, nhưng bù lại cũng ngốn pin hơn. Nếu bạn muốn ưu tiên thời lượng dùng pin hơn sự đẹp mắt thì cũng nên tắt chúng đi bằng cách nhấn và giữ ngón tay lên vùng trống của màn hình chờ, chọn Wallpapers và chọn 1 tấm ảnh tĩnh trong phần Gallery hoặc Wallpaper gallery.

pix212

10. Giữ điện thoại ở nơi mát mẻ:
Tránh để điện thoại ở những nơi có nhiệt độ cao hoặc tiếp xúc trực tiếp với ánh nắng Mặt Trời trong thời gian dài cũng giúp tránh được tình trạng pin nhanh bị cạn kiệt.

Nguồn: Tinh Tế

Saturday, July 17, 2010

SMS Tutorial (P2. What Makes SMS Messaging So Successful Worldwide?)

2. What Makes SMS Messaging So Successful Worldwide?
SMS is a success all over the world. The number of SMS messages exchanged every day is enormous. SMS messaging is now one of the most important revenue sources of wireless carriers. What is so special about SMS that makes it so popular worldwide? Some of the reasons are discussed below.

2.1. SMS Messages can be Sent and Read at Any Time
Nowadays, almost every person has a mobile phone and carries it most of the time. With a mobile phone, you can send and read SMS messages at any time, no matter you are in your office, on a bus or at home.

2.2. SMS Messages can be Sent to an Offline Mobile Phone
Unlike a phone call, you can send an SMS message to your friend even when he/she has not switched on the mobile phone or when he/she is in a place where the wireless signal is temporarily unavailable. The SMS system of the mobile network operator will store the SMS message and later send it to your friend when his/her mobile phone is online.

2.3. SMS Messaging is Less Disturbing While You can Still Stay in Touch
Unlike a phone call, you do not need to read or reply an SMS message immediately. Besides, writing and reading SMS messages do not make any noise. While you have to run out of a theater or library to answer a phone call, you do not need to do so if SMS messaging is used.

2.4. SMS Messages are Supported by 100% GSM Mobile Phones and They can be Exchanged between Different Wireless Carriers
SMS messaging is a very mature technology. All GSM mobile phones support it. Not only that you can exchange SMS messages with mobile users of the same wireless carrier, but you can also exchange SMS messages with mobile users of many other wireless carriers worldwide.

2.5. SMS is a Suitable Technology for Wireless Applications to Build on
Here are some of the reasons that make SMS a suitable technology for wireless applications to build on:

* Firstly, SMS messaging is supported by 100% GSM mobile phones. Building wireless applications on top of the SMS technology can maximize the potential user base.
* Secondly, SMS messages are capable of carrying binary data besides text. They can be used to transfer ringtones, pictures, operator logos, wallpapers, animations, VCards, VCals (calendar entries), etc.
*Thirdly, SMS supports reverse billing, which enables payment to be made conveniently. For example, suppose you want to develop a commercial ringtone download application that charges a fee from the user for each ringtone downloaded. One way to accept payment is to use a reverse billing phone number obtained from a wireless carrier. To buy a ringtone, the user will write an ordinary SMS text message that contains the ID of the ringtone he/she wants to buy and send it to your SMS application's reverse billing phone number. Your SMS application will then send back one or more reverse billing SMS messages that carry the ringtone. The user will be charged a fee for the reverse billing SMS messages he/she received. The fee will be included in the user's monthly mobile phone bill or be deducted from his/her prepaid card credits. Depending on the agreement between you and the wireless carrier, all or part of the money received will be given to you.

Source: http://www.developershome.com

SMS Tutorial (P1. Introduction to SMS Messaging)

1. Introduction to SMS Messaging
1.1. What is SMS (Short Message Service)?
SMS stands for Short Message Service. It is a technology that enables the sending and receiving of messages between mobile phones. SMS first appeared in Europe in 1992. It was included in the GSM (Global System for Mobile Communications) standards right at the beginning. Later it was ported to wireless technologies like CDMA and TDMA. The GSM and SMS standards were originally developed by ETSI. ETSI is the abbreviation for European Telecommunications Standards Institute. Now the 3GPP (Third Generation Partnership Project) is responsible for the development and maintenance of the GSM and SMS standards.

As suggested by the name "Short Message Service", the data that can be held by an SMS message is very limited. One SMS message can contain at most 140 bytes (1120 bits) of data, so one SMS message can contain up to:

* 160 characters if 7-bit character encoding is used. (7-bit character encoding is suitable for encoding Latin characters like English alphabets.)
* 70 characters if 16-bit Unicode UCS2 character encoding is used. (SMS text messages containing non-Latin characters like Chinese characters should use 16-bit character encoding.)

SMS text messaging supports languages internationally. It works fine with all languages supported by Unicode, including Arabic, Chinese, Japanese and Korean.

Besides text, SMS messages can also carry binary data. It is possible to send ringtones, pictures, operator logos, wallpapers, animations, business cards (e.g. VCards) and WAP configurations to a mobile phone with SMS messages.

One major advantage of SMS is that it is supported by 100% GSM mobile phones. Almost all subscription plans provided by wireless carriers include inexpensive SMS messaging service. Unlike SMS, mobile technologies such as WAP and mobile Java are not supported on many old mobile phone models.

1.2. Concatenated SMS Messages / Long SMS Messages

One drawback of the SMS technology is that one SMS message can only carry a very limited amount of data. To overcome this drawback, an extension called concatenated SMS (also known as long SMS) was developed. A concatenated SMS text message can contain more than 160 English characters. Concatenated SMS works like this: The sender's mobile phone breaks down a long message into smaller parts and sends each of them as a single SMS message. When these SMS messages reach the destination, the recipient mobile phone will combine them back to one long message.

The drawback of concatenated SMS is that it is less widely supported than SMS on wireless devices.

1.3. EMS (Enhanced Messaging Service)

Besides the data size limitation, SMS has another major drawback -- an SMS message cannot include rich-media content such as pictures, animations and melodies. EMS (Enhanced Messaging Service) was developed in response to this. It is an application-level extension of SMS. An EMS message can include pictures, animations and melodies. Also, the formatting of the text inside an EMS message is changeable. For example, the message sender can specify whether the text in an EMS message should be displayed in bold or italic, with a large font or a small font.

The drawback of EMS is that it is less widely supported than SMS on wireless devices. Also, many EMS-enabled wireless devices only support a subset of the features defined in the EMS specification. A certain EMS feature may be supported on one wireless device but not on the other.

Source: http://www.developershome.com

Đạo code (Phần 6: Quản đốc)

Phần 6: Quản đốc

Lập trình sư nói:
“Lập trình phu nhiều và các quản đốc ít thì môn phái sẽ hưng thịnh”

6.1
Các quản đốc chỉ biết họp bàn, lập trình phu ngồi chỉ biết ngồi chơi. Thì khi tổng quản báo cáo về tình hình thu chi, ngân quĩ sẽ bị sụt giảm. Khi quản đốc chỉ biết nói trăng sao, chung chung, tất cả sẽ bị mây mù bao phủ.
Đấy không phải là đạo lập trình.
Các quản đốc theo sát tình hình lập trình phu không thể ngồi chơi. Thì tổng quản lên kế hoạch, cân đối và mọi thứ đi vào trật tự. Khi mà các quản đốc nắm rõ vấn đề, thì vấn đề sẽ sớm được giải quyết.
Đấy chính là đạo lập trình

6.2
Tại sao lập trình phu kém năng động?
Bởi vì hầu hết thời gian họ dành cho hội họp.
Tại sao lập trình phu nổi loạn.
Bởi vì các quản đốc cản trở họ quá nhiều
Tại sao lập trình phu lần lượt ra đi?
Bởi vì đã đến lúc để họ ra đi.
Đừng việc cho những quản đốc ít quyền lực, họ không có giá trị trong bang.

6.3
Khi quản đốc gặp khó khăn, lập trình phu làm việc cho ông ta có sáng kiến mới để tạo ra chương trình ưa chuộng và bán chạy. Kết quả, ông ta giữ vững thế lực của mình.
Quản đốc tặng thưởng nhưng lập trình phu từ chối nó. Anh ta nói : “Tôi viết chương trình bởi vì tôi đam mê, tôi không trông chờ một phần thưởng”.
Quản đốc đưa ra lập luận: “Đây là lập trình phu, anh ta giữ một vị trí khá quan trọng, anh ta hoàn thành tốt mọi việc. Cần phải thăng cấp cho anh ta thành cố vấn của quản đốc”.
Khi nói về vấn đề này, lập trình phu phủ nhận ý kiến này, anh ta nói: “Tôi tồn tại khi tôi lập trình. Nếu tôi được thăng chức tôi sẽ không biết làm gì. Tôi có thể đi đâu? Tôi có một chương trình và tôi làm việc trên nó.”

6.4
Quản đốc đi đến chỗ các lập trình phu và nói với họ: “Các bạn thân mến,giờ làm việc của các bạn đang bắt đầu làm việc từ 9 giờ sáng và kết thúc lúc 5 giờ chiều”. Việc này, gây ra một chút bực bội và gò bó cho các bạn.
Quản đốc lại nói: “Thật ra, trong một vài trường hợp các bạn có thể quyết định giờ làm của mình. Dài hay ngắn ko quan trọng, miễn là các bạn có thể kết thúc dự án đúng như lịch định.”Lập trình phu cảm thấy hài lòng, anh ta bắt đầu công việc vào buổi trưa và kết thúc lúc gần sáng.

Sunday, June 27, 2010

Đạo code (Phần 5: Cải tiến)

Phần 5: Cải tiến

Lập Trình Sư nói
"Một chương trình, dù chỉ có 3 dòng code, một ngày nào đó vẫn cần phải được cải tiến"

5.1

Cửa giả dùng hay chẳng phí dầu (không cần phải tra dầu vào bản lề nếu dùng cẩn thận )
Suối nguồn chảy xiết há thành ao (suối chảy xiết thì không thể đọng nước như ao tù)
Lời hay khó lọt kẻ cứng đầu (mịa, chỗ này khó dịch thành thơ quá)
Phần mềm mục nát nếu không launch (phần mềm nếu không chạy (launch) thì sớm muộn gì cũng mục nát thôi )

Đó là những bí mật tuyệt vời.

5.2

Quản đốc hỏi một lập trình phu xem anh ta cần bao lâu để hoàn thành chương trình đang viết. "Sẽ xong trong ngày mai" phu đáp.
"Ta cho rằng điều đó là không thể" quản đốc nói, "Thực sự là ngươi cần bao lâu?"
Phu nghĩ một lúc. Rồi anh ta trả lời: "Tại hạ có một số chức năng muốn viết thêm. Những chức năng này sẽ mất ít nhất hai tuần để hoàn thành".
"Như vậy vẫn là quá ngắn so với dự tính" quản đốc nói "Thôi, chỉ cần ngươi thông báo cho ta khi chương trình hoàn tất là ta mãn nguyện rồi."
Lập trình phu đồng ý.
Vài năm sau quản đốc rửa tay gác phím, trên đường đến dự tiệc chia tay, quản đốc thấy lập trình phu ngủ gục trên bàn phím. Phu đã lập trình suốt đêm.

5.3

Lập trình phu một lần được giao viết một chương trình quản lý tài chính nhỏ.
Anh ta nhận việc và làm như điên trong nhiều ngày. Khi lập trình sư xem kết quả ông nhận thấy phu đã lập trình cả một chương trình soạn thảo, một đống hàm đồ họa tổng quát, một giao diện trí tuệ nhân tạo nhưng chẳng có dòng code nào về tài chính.
Lập trình sư lập tức chất vấn và phê phán, "Đừng nôn nóng" lập trình phu bực mình "tại hạ sẽ lập trình chương trình tài chính sau cùng" gã nói.

5.4

Đời nào dân tốt bỏ mùa?
Đời nào thầy tốt bỏ qua trò nghèo?
Cha tốt con chẳng đói meo
Phu tốt code chẳng mòn theo tháng ngày

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

Friday, June 11, 2010

Đạo code (Phần 4: Lập trình)

Phần 4: Lập trình

Lập Trình Sư nói:
"Chương trình tốt tự tạo ra thiên đường cho mình, chương trình tồi tự tạo ra địa ngục giam mình"

4.1

Chương trình cần hết sức gọn nhẹ, nên được cấu tạo từ nhiều hàm liên kết như chuỗi ngọc vậy. Tư tưởng và mục đích của chương trình phải liền mạch, không ngắt quãng. Mạch này không nên quá dài, không nên quá ngắn, không nên có biến thừa, không nên có vòng lặp có thể giản lược, không nên thiếu cấu trúc, không nên quá cứng nhắc.
Chương trình cần tuân theo "Luật hạn chế shock". Luật đó là gì? Đơn giản là khi trả lời người sử dụng, chương trình cần hạn chế tối đa việc gây bất ngờ cho họ.
Một chương trình dù phức tạp đến đâu cũng nên hoạt động như một đơn vị độc lập. Chương trình nên chú trọng đến logic bên trong hơn là giao diện bên ngoài.

* * *

Một chương trình không đáp ứng được những yêu cầu trên sẽ vô cùng lộn xộn. Cách duy nhất để sửa chữa là viết lại chương trình.

4.2

Lập trình phu hỏi Lập Trình Sư: "Thưa, chương trình con viết lúc chạy lúc không mặc dù con đã tuân theo quy tắc lập trình. Con cảm thấy vô cùng bế tắc. Đâu là nguyên nhân thảm cảnh này?"
Lập Trình Sư đáp: "Ngươi bối rối vì ngươi chưa ngộ Đạo. Chỉ có kẻ ngốc mới luôn đòi hỏi một hành động dựa trên lý trí từ phía con người. Vậy thì tại sao ngươi lại đòi hỏi điều đó từ thứ mà con người tạo ra? Máy móc chỉ là máy móc, Đạo mới là hoàn hảo. Quy tắc lập trình chỉ là nhất thời, Đạo mới là mãi mãi. Thế nên để được khai sáng, ngươi phải suy ngẫm về Đạo"
"Vậy làm sao để con nhận ra là mình đã được khai sáng?", lập trình phu hỏi.
"Chương trình của ngươi khi đó sẽ chạy trơn tru", Lập Trình Sư đáp.

4.3

Một Lập Trình Sư giảng về bản chất của Đạo cho một đồ đệ. "Đạo nằm trong tất cả các phần mềm dù phần mềm đó thấp kém đến đâu", Lập Trình Sư nói.
"Trong máy tính cầm tay có Đạo chứ?", đồ đệ hỏi.
"Có", Lập Trình Sư đáp.
"Trong trò chơi điện tử có Đạo chứ?", đồ đệ tiếp.
"Tất nhiên, kể cả trong trò chơi điện tử cũng có Đạo", Lập Trình Sư đáp.
"Cả trong hệ điều hành DOS cho máy tính cá nhân nữa chứ?"
Lập trình sư ho một tiếng, người ông khẽ rung lên "Bài giảng hôm nay kết thúc" ông nói.

4.4

Lập trình phu của thái tử viết một phần mềm, ngón tay phu như nhảy múa trên bàn phím. Chương trình khi dịch không có một lỗi và chạy mượt như cơn gió nhẹ.
"Kinh khủng quá" thái tử thốt lên, "Kỹ năng của túc hạ thật hoàn hảo!"
"Kỹ năng ư?", phu rời mắt khỏi màn hình, "Thứ tại hạ tuân theo là Đạo - vượt trên mọi kỹ năng! Khi tại hạ bắt đầu lập trình, tại hạ thấy toàn bộ bài toán trong một khối hỗn độn. Ba năm sau tại hạ không còn thấy khối hỗn độn nữa. Thay vào đó là những hàm con. Nhưng bây giờ tại hạ không còn thấy gì nữa. Toàn bộ bài toán hoàn toàn không tồn tại trong code. Tri thức của tại hạ trở nên nhàn nhã. Tư tưởng của tại hạ không cần kế hoạch, tự do hoạt động theo bản năng. Tóm lại, chương trình của tại hạ có thể tự lập trình. Thực sự thỉnh thoảng nó cũng gặp một số lỗi lớn. Thấy lỗi xuất hiện, tại hạ chậm rãi, lặng lẽ theo dõi. Rồi tại hạ sửa một dòng code và lỗi lớn vừa xuất hiện đã tan biến như làn khói mỏng. Sau đó tại hạ dịch lại chương trình, ngồi yên tận hưởng cảm giác hân hoan chảy trong cơ thể. Tại hạ nhắm mắt một lúc rồi log off".
"Giá như lập trình phu của ta ai cũng khôn ngoan như thế!" thái tử nói.

Wednesday, June 9, 2010

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

Ngoài ra ta cần có một lớp khai báo một số các phương thức utility để dễ dàng làm việc với Database. Bạn hãy xem ví dụ để thấy cụ thể các phương thức này.

package com.tannm.doan.taskmanager.database;

import android.database.Cursor;

public class DataTable {
 // fields
 private DataRow dataRow = null;

 // methods
 public DataTable(DataRow dataRow) {
  this.dataRow = dataRow;
 }

 public Database getUserDb() {
  return dataRow.getUserDb();
 }

 public String getTableName() {
  return dataRow.getTableName();
 }

 public DataRow getDataRow() {
  return dataRow;
 }

 public boolean createTable() {
  if (getUserDb().tableExists(getTableName())) {
   return true;
  } else {
   return getUserDb()
     .execSQL(
       getSqlTableDefinition(getTableName(), dataRow
         .getTableDef()));
  }
 }

 public String getSqlTableDefinition(String sTableName,
   DataField[] vecTableDef) {
  String def = "CREATE TABLE " + sTableName + " (";
  for (int i = 0; i < vecTableDef.length; i++) {
   def += vecTableDef[i].getColumnDefinition();
   if (i < (vecTableDef.length - 1))
    def += ", ";
  }
  def += ")";
  return def;
 }

 public long insertValues() {
  long lRowId = getUserDb().getSQLiteDb().insert(getTableName(), null,
    dataRow.getContentValues());
  return lRowId;
 }

 public long updateValues(long lRowId) {
  String sWhere = String.format("_ID = %d", lRowId);
  long lRowsUpdated = getUserDb().getSQLiteDb().update(getTableName(),
    dataRow.getContentValues(), sWhere, null);
  return lRowsUpdated;
 }

 public long deleteDataRow(long lRowId) {
  String sWhere = String.format("_ID = %d", lRowId);
  long lRowsUpdated = getUserDb().getSQLiteDb().delete(getTableName(),
    sWhere, null);
  return lRowsUpdated;
 }

 public Cursor locateDataRow(long lRowId) {
  final String s = "select * from %s where _ID = %d";
  String sql = String.format(s, getTableName(), lRowId);
  Cursor cr = getUserDb().getSQLiteDb().rawQuery(sql, null);
  // if cursor valid, set first data row as current
  if ((cr != null) && (cr.getCount() > 0))
   cr.moveToFirst();
  return cr;
 }

 public Cursor locateAlarmDataRow(int iType, long lRefID) {
  final String s = "select * from %s where Type = %d and RefID = %d";
  String sql = String.format(s, getTableName(), iType, lRefID);
  Cursor cr = getUserDb().getSQLiteDb().rawQuery(sql, null);
  // if cursor valid, set first data row as current
  if ((cr != null) && (cr.getCount() > 0))
   cr.moveToFirst();
  return cr;
 }

 public Database.Result updateData(boolean bInsertMode, long lEditRowId) {
  Database.Result result = Database.Result.errUnknown;
  if (getUserDb().isOpened()) {
   try {
    dataRow.setValuesForDataRow();
   } catch (Exception e) {
    return Database.Result.errCantSetValuesForDataRow;
   }
   // select update mode
   if (bInsertMode) {
    // insert new data row
    long lRowId = insertValues();
    if (lRowId > 0) {
     result = Database.Result.Success;
    } else {
     result = Database.Result.errCantInsertNewData;
    }
   } else {
    // update existing data row
    long lRowsUpdated = updateValues(lEditRowId);
    if (lRowsUpdated == 1) {
     result = Database.Result.Success;
    } else {
     result = Database.Result.errCantUpdateData;
    }
   }
  } else {
   result = Database.Result.errNoDbAccess;
  }
  return result;
 }

 public Database.Result deleteData(long iRowId) {
  Database.Result result = Database.Result.errUnknown;
  if (getUserDb().isOpened()) {
   if (getUserDb().tableExists(getTableName())) {
    long lRowsDeleted = deleteDataRow(iRowId);
    if (lRowsDeleted == 1) {
     result = Database.Result.Success;
    } else {
     result = Database.Result.errCantDeleteData;
    }
   } else {
    result = Database.Result.errTableNotExists;
   }
  } else {
   result = Database.Result.errNoDbAccess;
  }
  return result;
 }

 public Database.Result getRowDataForEdit(long lRowId) {
  Database.Result result = Database.Result.errUnknown;
  // get requested data row
  Cursor cr = locateDataRow(lRowId);
  if (cr == null) {
   result = Database.Result.errCantGetData;
  } else {
   if (cr.getCount() > 0) {
    if (dataRow.getValuesFromCursor(cr)) {
     try {
      dataRow.getValuesFromDataRow();
     } catch (Exception e) {
      return Database.Result.errCantGetValuesFromDataRow;
     }
     result = Database.Result.Success;
    } else {
     result = Database.Result.errCantGetDataFromTable;
    }
    cr.close();
   } else {
    result = Database.Result.errCantFindData;
   }
  }
  return result;
 }
}

Trên đây là toàn bộ kinh nghiệm làm việc với Database mà tôi học được trong quá trình làm việc thực tế. Mong giúp đỡ những người mới làm việc với SQLite trên Android. Chúc các bạn thành công

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)

Saturday, February 6, 2010

Cấu hình mail server cho jira sử dụng gmail trên Linux

1. Chạy câu lệnh: openssl s_client -connect smtp.gmail.com:465(Nếu chưa có openssl bạn hãy download nó)
2. Copy đoạn bắt đầu từ "-----BEGIN CERTIFICATE-----" tới "-----END CERTIFICATE-----" (nhớ là phải có cả đoạn BEGIN và END)
3. Lưu nó vào một file gmail.pem
4. Run: keytool -import -file gmail.pem -alias smtp.gmail.com -keystore "$JAVA_HOME/jre/lib/security/cacerts"
5. keytool nằm trong $JAVA_HOME/bin
6. lib/security/cacerts nằm trong $JAVA_HOME/jre
7. Sửa $JIRA_HOME/conf/server.xml thêm đoạn sau vào:

<resource name="mail/GmailSmtpServer"

auth="Container"

type="javax.mail.Session"

mail.smtp.host="smtp.gmail.com"

mail.smtp.port="465"

mail.smtp.auth="true"

mail.smtp.user="MyGmailAccount@gmail.com"

password="MyPassword"

mail.smtp.starttls.enable="true"

mail.smtp.socketFactory.class="javax.net.ssl.SSLSocketFactory"

/>

8. Lưu file server.xml lại
9. Chuyen 2 file javamail-1.3.3.jar va activation-1.0.2.jar từ $JIRA_HOME/atlassian-jira/WEB-INF/lib
Tới $JIRA_HOME/common/lib
10. Khởi động lại jira
11. Đăng nhập vào jira với tài khoản administrator
12. Tới phần Mail Servers
13. Cấu hình SMTP
14. Sử dụng JNDI với tham số: java:comp/env/mail/GmailSmtpServer
15. Kết thúc, bạn hãy test thử và xem kết quả của mình.

Create a service for JIRA on CentOS 5 or RHEL 5

Tạo file jira trong /etc/init.d với nội dung như sau.

#!/bin/bash
#
# chkconfig: 2345 85 15
# description: jira
# processname: jira
# source function library
. /etc/init.d/functions

JAVA_HOME="/app/jdk"
JRE_HOME="/app/jdk"
CATALINA_HOME="/app/jira"

RETVAL=0

start() {
echo -n $"Starting jira services: "
. /app/jira/bin/catalina.sh start
RETVAL=$?
echo
}

stop() {
echo -n $"Shutting down jira services: "
. /app/jira/bin/catalina.sh stop
RETVAL=$?
echo
}

case "$1" in
start)
start
;;
stop)
stop
;;
restart|reload)
stop
start
;;
status)
status jira
RETVAL=$?
;;
*)
echo $"Usage: $0 {start|stop|restart|status}"
exit 1
esac
exit $RETVAL

Tiếp tới bạn vào thư mục /etc/init.d
# cd /etc/init.d
Cấp quyền execute
# chmod a+x jira
Thêm nó vào danh sách chkconfig:
# chkconfig --add jira


Ok rồi
Bạn có thể test thử
start
# /etc/init.d/jira start
stop
# /etc/init.d/jira stop
restart
# /etc/init.d/jira restart

Friday, January 15, 2010

Auto startup oracle

1, Đầu tiên bạn đăng nhập vào root và sửa file /etc/oratab: Thay đổi ký tự cuối cùng thành Y.
ORCL:/app/oracle/111:Y
Trước đó trông nó như: ORCL:/app/oracle/111:N
2, Tạo file oracle trong thư mục /etc/init.d/ với nội dung như sau:
#!/bin/bash
#
# oracle Init file for starting and stopping
# Oracle Database. Script is valid for 10g and 11g versions.
#
# chkconfig: 35 80 30
# description: Oracle Database startup script

# Source function library.

. /etc/rc.d/init.d/functions


ORACLE_OWNER="oracle"
ORACLE_HOME="/app/oracle"

case "$1" in
start)
echo -n $"Starting Oracle DB:"
su - $ORACLE_OWNER -c "$ORACLE_HOME/bin/dbstart $ORACLE_HOME"
echo "OK"
;;
stop)
echo -n $"Stopping Oracle DB:"
su - $ORACLE_OWNER -c "$ORACLE_HOME/bin/dbshut $ORACLE_HOME"
echo "OK"
;;
*)
echo $"Usage: $0 {start|stop}"
esac

3, Chạy câu lệnh:
chmod 750 /etc/init.d/oracle
chkconfig --add oracle --level 0356

Ok roài! restart thử để thấy kết quả :D!

Wednesday, January 13, 2010

Lấy địa chỉ IP của máy android của bạn!

Cũng chưa có kinh nghiệm nhiều trong lập trình android.
Nhưng mình vừa học được cách lấy ip từ máy android share cho mọi người tham khảo:
Đầu tiên bạn có thể thấy trong lớp WifiInfo có phương thức getIpAddress().
Nhưng kết quả trả về lại là một số nguyên:

WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
int ipAddress = wifiInfo.getIpAddress();

Hình như đây không phải là cách đúng.

Một cách khác cũng khá triển vọng là bạn có thể tạo một kết nối socket (socket connection) và dùng thể hiện của Socket để lấy địa chỉ ip của máy mình.
try {
    Socket socket = new Socket("www.androidvn.net", 80);
    Log.i("", socket.getLocalAddress().toString());
} catch (Exception e) {
    Log.i("", e.getMessage());
}

Nhưng cách này hình như lại có một số điểm bất lợi:
    + Bạn tạo ra một kết nối, tạo ra một lưu lượng vận chuyển dẫn đến người dùng có thể bị đánh vào cước :D.
    + Doạn chương trình này được quyết định bởi server mà bạn kết nối tới.
    + Tại đây có thể xảy ra Exception.

Tư tưởng đúng cho trường hợp này là tìm và lặp trên tất các các giao tiếp mạng và tất cả địa chỉ IP :D.

public String getLocalIpAddress() {
    try {
        for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
            NetworkInterface intf = en.nextElement();
            for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                InetAddress inetAddress = enumIpAddr.nextElement();
                if (!inetAddress.isLoopbackAddress()) {
                    return inetAddress.getHostAddress().toString();
                }
            }
        }
    } catch (SocketException ex) {
        Log.e(LOG_TAG, ex.toString());
    }
    return null;
}

Nếu hàm trả về null có nghĩa là không có kết nối nào available.
Nếu hàm trả về một String thì đó là địa chỉ ip hiện tại của thiết bị khi kết nối wifi hay 3G.

Monday, January 11, 2010

Light Racer 2.0 - Day 9 - Adding Items

After working with the AI a bit, I realized that the game feels totally dull to me. I spent a little time thinking about game design and decided to go with levels and items. Items weren't designed in to the game at all so I have to add the whole concept in from scratch. The first item going in is the Speed Boost item. When a player picks it up, their racer will move a notch faster until the end of the round. I have a problem though, the game is coded to run at the speed of the current level, not of the current player.

Here's what I did today:

Built Project Plan
Designed Icon
Implemented basic non-working SpeedBoostItem that could just draw itself
Added SpeedBoostItem to the world
Changed the way motor sounds are handled so they will work at different speeds
Modified game to allow for different speed players
Added a method incrementSpeed() to Player
Added item collision detection
Added random item placement - needs refining
Animated SpeedBoostItem

The hardest thing was adding the concept of speed on to the Player class, which is necessary to have players run at independent speeds. The problem was that all of the AI code and collision detection was using the overall game's speed to calculate distances. I had to change several methods to use the specific player's speed in the calculations, which took about an hour. After that, it was fairly straight-forward.

I drew the images of the item in photoshop:

Speed Boost Item in PhotoshopSpeed Boost Item in Photoshop 3 Image Files3 Image Files

I created the item class and added it to the world. The main game is responsible for placing the item. After that, the item is responsible for detecting a collision and responding. When it's done, it will set a flag that it is finished and the game will remove it from the world. The collision detection is very simple. Here's how it works:

private void checkCollisions(World world) {
// check to see if a player is colliding with me, do something to that player
int top = y - (int) (height * .5) - PLAYER_ITEM_TOUCH_PADDING;
int bottom = y + (int) (height * .5) + PLAYER_ITEM_TOUCH_PADDING;
int left = x - (int) (width * .5) - PLAYER_ITEM_TOUCH_PADDING;
int right = x + (int) (width * .5) + PLAYER_ITEM_TOUCH_PADDING;
Player[] players = world.players;
Player player;
int lx, ly, cx, cy;
for (int playerIndex = 0; playerIndex < players.length; playerIndex++) {
player = players[playerIndex];
lx = player.lastX;
ly = player.lastY;
cx = player.curX;
cy = player.curY;
if (cx == lx && (cx >= left && cx <= right)) {
// moving vertical within horizontal bounds
if ((ly >= bottom && cy < bottom) || (cy > top && ly <= top)) {
// collision
doCollision(player);
}
} else if (cy == ly && (cy >= top && cy <= bottom)) {
// moving horizontal within vertical bounds
if ((lx >= right && cx < right) || (cx > left && lx <= left)) {
// collision
doCollision(player);
}
}
}
}

The animation code is the same code I always use for animations

For now the game is just randomly dropping the item all over but once I have all 3 items in, I'll work out the balance of the item drop. For now it really added a new dynamic element to the game. I can see the game getting much more fun in the next few weeks!

Here's a video of the new item working.



Friday, January 8, 2010

Bắt đầu với google maps trên android

Trước tiên bạn phải đăng ký một cái maps API key của google bằng cách vào
http://code.google.com/android/maps-api-signup.html
Trước đó bạn phải kiếm cái MD5 của máy mình đã bằng cách
Kiểm tra thư mục C:Documents and Settings<UserName>.android
Của mình cụ thể là C:Documents and SettingsTanNM.android
Bạn sẽ thấy cái file debug.keystore
copy nó vào trong C:Program FilesJavajdkbin
trên dòng lệnh command bạn cd vào thư mục C:Program FilesJavajdkbin
Chạy câu lệnh:
keytool -list -alias androiddebugkey -keystore debug.keystore -storepass android -keypass android

Sẽ được một kq giống như:
   1. androiddebugkey, Oct 27, 2009, PrivateKeyEntry,
   2. Certificate fingerprint (MD5): E1:D0:F8:0C:F7:11:F0:04:F9:F7:2F:21:49:DD:F7:B3

Sau đó vào trang
http://code.google.com/android/maps-api-signup.html
điền cái mã MD5:
E1:D0:F8:0C:F7:11:F0:04:F9:F7:2F:21:49:DD:F7:B3

vào và submit, ta sẽ có một key giống như:
09BcO4uoQ0MIS9P9FU2G2MK2imfqIE5kvrJ8JRA

Ngon roài!!!!!!

Tiếp đến ta tạo một ứng dụng đơn giản trên myeclipse
file Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.ftl.map">
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
        <uses-permission android:name="android.permission.INTERNET" />
        <application android:label="@string/activity_sample_code"
               android:icon="@drawable/icon">
                <uses-library android:name="com.google.android.maps" />
                <activity android:name="com.ftl.map.view.MapViewDemo"
                       android:label="MapView">
                        <intent-filter>
                                <action android:name="android.intent.action.MAIN" />
                                <category android:name="android.intent.category.SAMPLE_CODE" />
                        </intent-filter>
                </activity>
        </application>
</manifest>

file main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:id="@+id/main" android:layout_width="fill_parent"
       android:layout_height="fill_parent">
        <com.google.android.maps.MapView
               android:layout_width="fill_parent" android:layout_height="fill_parent"
               android:enabled="true" android:clickable="true" android:apiKey="09BcO4uoQ0MIS9P9FU2G2MK2imfqIE5kvrJ8JRA" />
</LinearLayout>


file java Activity

package com.ftl.map.view;

import android.os.Bundle;

import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;

public class MapViewDemo extends MapActivity {

        private MapView myMapView;

        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(com.ftl.map.R.layout.main);
        }

        @Override
        protected boolean isRouteDisplayed() {
                return true;
        }
}

Chạy nào!!!!!

Lập trình database trên android

Để các bạn lập trình được với các ứng dụng cần dùng đến cơ sở dữ liệu trên android mình xin giới thiệu với các bạn sơ qua về lập trình database trên android:
Thật tuyệt vời khi mà trên android OS đã hỗ trợ chúng ta SQLite:
1, Việc đầu tiên bạn cần làm với database là tạo một cơ sở dữ liệu: Ở đây nó chỉ là một file cơ sở dữ liệu:
private final String MY_DATABASE_NAME = "myAndroidDB";
private final String MY_DATABASE_TABLE = "Member";
SQLiteDatabase myDB = null;
try {
/* Create the Database (no Errors if it already exists) */
this.createDatabase(MY_DATABASE_NAME, 1, MODE_PRIVATE, null);



2, Bạn đã tạo database roài giờ mở nó thế nào nhỉ:

/* Open the DB and remember it */
myDB = this.openDatabase(MY_DATABASE_NAME, null);



3, Tiếp đến bạn tạo một table đơn giản:

/* Create a Table in the Database. */
myDB.execSQL("CREATE TABLE IF NOT EXISTS "
+ MY_DATABASE_TABLE
+ " (LastName VARCHAR, FirstName VARCHAR,"
+ " Country VARCHAR, Age INT(3));");



4, Giờ ta insert thử vào database xem sao nhỉ

/* Add two DataSets to the Table. */
myDB.execSQL("INSERT INTO "
+ MY_DATABASE_TABLE
+ " (LastName, FirstName, Country, Age)"
+ " VALUES ('Nguyễn', 'Tân', 'VietNam', 24);");
myDB.execSQL("INSERT INTO "
+ MY_DATABASE_TABLE
+ " (LastName, FirstName, Country, Age)"
+ " VALUES ('Nguyễn', 'Dung', 'VietNam', 23);");



5, Có dữ liệu roài giờ truy vấn thử xem sao nhỉ:

/* Query for some results with Selection and Projection. */
Cursor c = myDB.query("SELECT FirstName,Age" +
" FROM " + MY_DATABASE_TABLE
+ " WHERE Age > 10 LIMIT 7;",
null);



6, Sử dụng hàm getColumnIndex(String) để lấy thông tin nhé:

/* Get the indices of the Columns we will need */
int firstNameColumn = c.getColumnIndex("FirstName");
int ageColumn = c.getColumnIndex("Age");

/* Check if our result was valid. */
if (c != null) {
/* Check if at least one Result was returned. */
if (c.first()) {
int i = 0;
/* Loop through all Results */
do {
i++;
/* Retrieve the values of the Entry
* the Cursor is pointing to. */
String firstName = c.getString(firstNameColumn);
int age = c.getInt(ageColumn);
/* We can also receive the Name
* of a Column by its Index.
* Makes no sense, as we already
* know the Name, but just to shwo we can Wink */
String ageColumName = c.getColumnName(ageColumn);

/* Add current Entry to results. */
results.add("" + i + ": " + firstName
+ " (" + ageColumName + ": " + age + ")");
} while (c.next());
}
}


7, Sau khi làm việc với database bạn hãy đóng nó lại nhé:

} catch (FileNotFoundException e) {
} finally {
if (myDB != null)
myDB.close();
}



8, Cuối cùng bạn lấy thông tin ra roài giờ hiển thị nó thui nhỉ:
this.setListAdapter(new ArrayAdapter(this,
android.R.layout.simple_list_item_1_small, results));
}
}