Day 21 : Active Android

Day 21 : Active Android Cover Image

สวัสดีครับ บทความนี้เป็นบทความที่ 21 แล้วนะครับ ที่ผมจะมาเขียน ในซีรีย์ Learn 30 Android Libraries in 30 days

สำหรับบทความทั้งหมด อ่านได้จากด้านล่างครับ

สำหรับวันนี้ขอนำเสนอเรื่อง ActiveAndroid เป็น Library ที่เอาไว้จัดการข้อมูล SQLite ในสไตล์ ORM ครับ ลักษณะคล้ายๆกับ Day 19 : GreenDAO ครับ โดยเมธอดที่ใช้งาน ค่อนข้างจำง่ายครับ เช่น save(), delete()

Installation

ขั้นตอนการติดตั้ง ต้องดาวน์โหลดตัวโปรเจ็คมาก่อนครับ ทำการ clone จาก github

git clone https://github.com/pardom/ActiveAndroid.git

หรือจะดาวน์โหลดจากลิงค์นี้ ActiveAndroid-Master ทั้งสองวิธี เมื่อดาวน์โหลดมาแล้ว ก็ให้แตกซิปออก แล้วไปยังโฟลเดอร์ของโปรเจ็ค จากนั้นสั่งรันสคริป ant เพื่อให้มัน gen ไฟล์ ActiveAndroid.jar มาให้ครับ

sudo apt-get install ant
ant

sudo apt-get install ant คือการติดตั้ง ant ลงเครื่อง Ubuntu นะครับ สำหรับ Windows ผมไม่รู้วิธีการติดตั้งนะครับ ใครติดปัญหาตรงนี้ ก็หาวิธีกันเองนะครับ

หรือวิธีการ ข้างบนมันยากไป? ผมได้สร้างไฟล์ ActiveAndroid.jar ไว้ให้แล้ว ดาวน์โหลดไปใส่ที่โฟลเดอร์ libs ของโปรเจ็คได้เลย

เมื่อเซฟไฟล์ jar ไว้ที่โฟลเดอร์ libs ก็เปิดไฟล์ build.gradle ขึ้นมา หาก compile fileTree แล้วก็ไม่ต้องแก้ไร ให้กด Sync Gradle ใหม่แค่นั้น

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

แต่ถ้าหากใครไม่ได้สั่งให้ compile fileTree ก็ระบุตำแหน่งของ libs ไปเลยครับ เช่นเซฟไว้ที่โฟลเดอร์ libs ก็เป็นแบบนี้

dependencies {
    compile files('libs/ActiveAndroid.jar')
}

กด Sync Gradle เป็นอันเรียบร้อย

ผมเห็นใน Docs มีเขียนไว้ว่าสามารถโหลดจาก maven ได้ แต่ว่าไปเสริจหาดูแล้ว ไม่เจอใน maven แถมไม่รู้เวอร์ชันของ Library อีก เลยต้องโหลดไฟล์ jar แทน

Usage

มาถึงขั้นตอนการใช้งาน ActiveAndroid กันครับ

[1] ต้องทำการสร้างคลาส ที่ extends Application ครับ ผมทำการตั้งชื่อให้มันว่า AAApplication.java

package com.devahoy.learn30androidlibraries.day21;

import android.app.Application;
import com.activeandroid.ActiveAndroid;

public class AAApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        ActiveAndroid.initialize(this);
    }

    @Override
    public void onTerminate() {
        super.onTerminate();
        ActiveAndroid.dispose();
    }
}

ที่เมธอด onCreate() มีการเรียกใช้ ActiveAndroid.initialize() เพื่อสั่งให้มันทำงาน และเมธอด onTerminate() ก็สั่งให้หยุดทำงาน ด้วย ActiveAndroid.dispose() เมื่อปิดแอพครับ

[2] แก้ไข AndroidManifest.xml โดยเพิ่ม meta-data และ application

<manifest ...>
    <application 
        ...
        android:name=".AAApplication">

        ...

        <meta-data android:name="AA_DB_NAME" android:value="Devahoy-AA.db" />
        <meta-data android:name="AA_DB_VERSION" android:value="1" />

    </application>
</manifest>

โดย AA_DB_NAME และ AA_DB_VERSION คือชื่อฐานข้อมูลและชื่อเวอร์ชันของฐานข้อมูลครับ หากใครเคยใช้ SQLiteOpenHelper ก็จะคุ้นๆกับ Constructor ที่มีการใช้ ชื่อฐานข้อมูล และชื่อเวอร์ชันครับ

Add Data

การเพิ่มข้อมูล ทำได้โดยการสร้างคลาสโมเดล ขึ้นมา แล้วทำการ extends com.activeandroid.Model ครับ อยากเช่นผมต้องการสร้าง ข้อมูลหนังสือ ก็จะได้คลาส Book แบบนี้

package com.devahoy.learn30androidlibraries.day21;

import com.activeandroid.Model;
import com.activeandroid.annotation.Column;
import com.activeandroid.annotation.Table;

@Table(name = "Book")
public class Book extends Model{

    @Column(name = "title")
    public String title;

    @Column(name = "author")
    public String author;

    @Column(name = "publisher")
    public String publisher;

    @Column(name = "release_date")
    public String releaseDate;

}

จะเห็นในโค๊ดมี Annotation ซึ่งตัว ActiveAndroid จะใช้ Annotation ไว้ระบุชื่อ Table และชื่อ Column ต่างๆ ในฐานข้อมูลครับ จากโค๊ด จะสังเกตเห็นว่า @Table(name = "Book") คือประกาศให้โมเดลเนี้ย เวลาเซฟลงฐานข้อมูลจะชื่อ Book ส่วน @Column() ก็ประกาศว่าแต่ละตัวแปร จะเซฟในฐานข้อมูลชื่อว่าอะไร เพื่อเวลา mapping จากฐานข้อมูลมาเป็นออปเจ็ค

อ้อถ้าอยากจะสร้าง Constructor ขึ้นมาเอง ก็ทำได้เช่นกัน แต่จำเป็นต้องสร้าง default constructor ขึ้นมาด้วย แบบนี้

package com.devahoy.learn30androidlibraries.day21;

import com.activeandroid.Model;
import com.activeandroid.annotation.Column;
import com.activeandroid.annotation.Table;

@Table(name = "Book")
public class Book extends Model{

    public Book() {
        super();
    }
    public Book(String title, String author, String publisher, String date) {
        super();
        this.title = title;
        this.author = author;
        this.publisher = publisher;
        this.releaseDate = date;
    }
}

ส่วนวิธีการเพิ่มข้อมูลลงฐานข้อมูล หลักจาที่มีโมเดล แล้วก็ทำได้ง่ายๆแบบนี้ครับ โดยการสร้าง new object จากนั้นก็ใช้เมธอด save() เช่น

Book book = new Book("A Song of Ice and Fire", 
                "George R. R. Martin", 
                "Bantam Books", 
                "August 1996");
book.save();

Query Data

สำหรับวิธีการ Query ข้อมูล ก็ไม่ยากเลยครับ ชื่อคลาส และเมธอด จะคล้ายๆกับ Syntax ของ SQL เลยครับ เช่น Select, From, Where เป็นต้นครับ เช่น ต้องการ query ข้อมูลหนังสือทั้งหมด ก็ทำได้ดังนี้

 Select().from(Book.class).execute();

จะเห็นได้ว่า มันมีค่าเท่ากับ Select * From Book คือเรียกคลาส Select() หากไม่ระบุ parameter ลงไปแสดงว่าเลือกทั้งหมด ต่อมาก็ .from() เป็นการระบุว่าจะเลือกจาก Table อะไร ในที่นี้คือคลาส Book ส่วนตรง execute() คือการ select แบบ List แต่ถ้าเลือกเป็น executeSingle() ก็จะ select เพียงแค่ 1 column อย่างเช่น ตัวอย่างต่อไป เป็นการค้นหาหนังสือ โดยระบุไอดี

Select()
    .from(Book.class)
    .where("id = ?", String.valueOf(id))
    .executeSingle();
}

หรืออีกวิธีคือ ใช้ load() ครับ โดยระบุ id ลงไป เช่น

Book book = Book.load(Book.class, 1); // select * from Book where id = 1

Update & Delete

การ update และ delete สามารถทำได้แบบนี้

การ update จะคล้ายๆกับการ insert คือใช้เมธอด save() อย่างเช่น เรา query ข้อมูลมาแล้ว แล้วต้องการอัพเดท ก็ทำได้แบบนี้

Book book = Book.load(Book.class, 1);
book.title = "Another name";
book.save();

การ delete ก็เช่นเดียวกัน เรียกออปเจ็คที่เราต้องการจะลบ จากนั้นก็ใช้เมธอด delete() เช่น

Book book = Book.load(Book.class, 1);
book.delete()

หรือจะลบแบบนี้ก็ได้

Book.delete(Book.class, 1);

หรืออีกแบบ

new Delete().from(Book.class).where("Id = ?", 1).execute();

Create Project

ลองสร้างโปรเจ็คขึ้นมาซักหน่อยละกัน ตัวโปรเจ็คนี้ผมใ้ช้ไฟล์เดียวครับ คือ ActiveAndroidActivity.java โดยทำการ extends ListActivity เพื่อจะโชว์ ListView และไม่ต้องใช้ไฟล์ layout ครับประหยุดเวลาดี สำหรับหลักการในแอพนี้ก็คือ

  1. ทำการ insert ข้อมูลเริ่มแรก ผ่านเมธอด initSampleData()
  2. query ข้อมูลหนังสือทั้งหมด ด้วยเมธอด findBooks()
  3. นำข้อมูลที่ได้ มาแสดงใน ListView
  4. กดไอเท็มแต่ละ ListView จะโชว์ชื่อของหนังสือเล่มนั้นๆ

โค๊ดที่ได้จะได้แบบด้านล่างนี้

package com.devahoy.learn30androidlibraries.day21;

import android.app.ListActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

import com.activeandroid.ActiveAndroid;
import com.activeandroid.query.Delete;

import java.util.ArrayList;
import java.util.List;

public class ActiveAndroidActivity extends ListActivity {

    private List<Book> mBooks;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        initSampleData();
        mBooks = AAHelper.findBooks();

        StringBuilder builder = new StringBuilder();
        ArrayList<String> dataset = new ArrayList<String>();
        for (Book book : mBooks) {
            builder.setLength(0);
            builder.append("Name : " + book.title + "\n");
            builder.append("By   : " + book.author + "\n");
            builder.append("Publisher : " + book.publisher + "\n");
            builder.append("Release Date : " + book.releaseDate + "\n");
            dataset.add(builder.toString());
        }

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, dataset);
        setListAdapter(adapter);
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        Book book = AAHelper.findBookById(mBooks.get(position).getId());
        Toast.makeText(this, "You choose " + book.title, Toast.LENGTH_SHORT).show();
    }

    private void initSampleData() {
        new Delete().from(Book.class).execute();

        ActiveAndroid.beginTransaction();

        try {
            Book book1 = new Book("Android Cookbook",
                    "Ian F. Darwin",
                    "O'Reilly Media" ,
                    "April 2012");
            book1.save();

            Book book2 = new Book("Android Recipes, 3rd Edition",
                    "Dave Smith , Jeff Friesen",
                    "Apress",
                    "February 2014");
            book2.save();

            Book book3 = new Book("Expert Android",
                    "Satya Komatineni, Dave MacLean",
                    "Apress",
                    "July 2013");
            book3.save();

            Book book4 = new Book("50 Android Hacks",
                    "Carlos Sessa",
                    "Manning Publications",
                    "May 2013");
            book4.save();

            Book book5 = new Book("Learn Java for Android Development, 3rd Edition",
                    "Jeff Friesen",
                    "Apress",
                    "March 2014");
            book5.save();

            Book book6 = new Book("Learning Android, 2nd Edition",
                    "Marko Gargenta, Masumi Nakamura",
                    "O'Reilly Media, Inc.",
                    "January 2014");
            book6.save();
        } finally {
            ActiveAndroid.setTransactionSuccessful();
        }

        ActiveAndroid.endTransaction();
    }
}

ส่วนคลาส AAHelper ผมเอาไว้สำหรับสร้างเมธอด ไว้ query เฉยๆครับ มีโค๊ดนี้อยู่

package com.devahoy.learn30androidlibraries.day21;

import com.activeandroid.query.Select;
import java.util.List;

public class AAHelper {

    public static List<Book> findBooks() {
        return new Select()
                .from(Book.class)
                .execute();
    }

    public static Book findBookById(long id) {
        return new Select()
                .from(Book.class)
                .where("id = ?", String.valueOf(id))
                .executeSingle();
    }
}

สุดท้าย ทดสอบรันโปรแกรม ได้ผลแบบนี้

Result

สรุป

ลองเอาไปใช้กันดูนะครับ ส่วนตัวผมว่ามันใช้ง่ายกว่า GreenDAO อีกแฮะ แล้วการเรียกเมธอด หรือชื่อคลาส อะไรๆ ก็คล้ายๆ SQL Syntax เลย ทำให้มันแลดูเข้าใจง่าย สำหรับคนที่พอมีพื้นฐาน SQL มาบ้าง อ้ออีกอย่างมันรองรับ Transaction ด้วยเหมือนกัน แบบโค๊ดที่ผมเขียนด้านบน แต่ว่าลืมอธิบาย เอาเป็นว่า ลองๆดูโค๊ดประกอบละกันครับ ขอบคุณครับ

Happy Coding :D

Reference

Chai Chai Phonbopit : MEAN Stack @Nextzy • ผู้ชายธรรมดาๆ ที่ชื่นชอบ Android, JavaScript (Node.js) และ Open Source มีงานอดิเรกเป็น Acoustic Guitar และ Football

บทความล่าสุด