วิธีการดึงไฟล์ฐานข้อมูลจากเครื่องจริง

อยู่ในระหว่างการปรับปรุงเนื้อหา

บ่อยครั้งที่เวลาเขียนแอพซักตัวที่มีการสร้างฐานข้อมูลขึ้นมา ก็มักจะต้องมีการลองดึงไฟล์ฐานข้อมูลมาเช็คบนคอมเพื่อตรวจสอบว่าโค๊ดทำงานได้อย่างถูกต้องหรือไม่

และผู้ที่หลงเข้ามาอ่านหลายๆคนก็มักจะประสบปัญหาดึงไฟล์ฐานข้อมูลจากแอพบนอุปกรณ์แอนดรอยด์ออกมาดูบนคอมไม่ได้ ซึ่งเป็นปัญหาที่เกิดขึ้นกับมือใหม่แทบจะทุกคน ดังนั้นเจ้าของบล็อกขอจัดบทความสั้นๆสำหรับเรื่องนี้เสียหน่อยดีกว่า

เป็นที่รู้กันอยู่แล้วว่าไฟล์ฐานข้อมูลของแต่ละแอพจะถูกเก็บไว้ในโฟลเดอร์ของแอพตัวเอง

/data/data/<package_name>/databases/

และส่วนใหญ่นั้นจะใช้วิธีดึงฐานข้อมูลออกมาโดยใช้ File Explorer ที่อยู่ใน DDMS หรือ Android Device Monitor แล้วเข้าไปยัง Path ดังกล่าว

แต่ทว่าก็มีผู้ที่หลงเข้ามาอ่านหลายๆคนเจอปัญหาว่าเปิดโฟลเดอร์ data ไม่ได้

ก็ดึงข้อมูลได้ปกตินี่ ไม่เห็นจะมีอะไร


สำหรับผู้ที่หลงเข้ามาอ่านที่ยังไม่เข้าใจว่าเปิดโฟลเดอร์ data ไม่ได้มันเป็นยังไง ให้ลองต่อกับอุปกรณ์แอนดรอยด์ของจริง แล้วใช้ Android Device Monitor หรือ DDMS เปิดโฟลเดอร์ดังกล่าวในเครื่องนั้นๆดู

เพราะว่าเครื่องที่เปิดโฟลเดอร์นั้นได้ จะมีแต่ Emulator เท่านั้น

เคยสังเกตกันมั้ย?


เวลาเปิด Android Device Monitor หรือ DDMS ตรงรายชื่ออุปกรณ์แอนดรอยด์ที่เชื่อมต่ออยู่ เฉพาะ Emulator เท่านั้นที่ตรงช่องเลขเวอร์ชันจะมีต่อท้ายว่า debug

ซึ่งเป็นการบอกให้รู้ว่าอุปกรณ์แอนดรอยด์ตัวนั้นๆเป็น Debug Device หรือมีไว้สำหรับทดสอบเท่านั้น แต่ทว่าเครื่องที่ผู้ที่หลงเข้ามาอ่านใช้ๆกันอยู่นั้น เป็น Production Device ไม่ใช่เครื่องทดสอบซักหน่อย (ก็ซื้อมาจากร้านค้าที่วางขายตามท้องตลาด)

ซึ่ง Debug Device จะมีความพิเศษตรงที่สามารถเข้าถึงไฟล์ได้ทุกที่ภายในเครื่อง (เพราะว่ามันใช้ทดสอบนั่นเอง) แต่ Production Device จะไม่สามารถเข้าถึงได้ เพื่อป้องกันอันตรายที่อาจจะเกิดขึ้นได้ ดังนั้นจึงทำให้เปิดเข้าไปยังโฟลเดอร์ดังกล่าวไม่ได้

แล้วจะแก้ปัญหายังไงดี?


วิธีแก้ปัญหาจะมีอยู่ด้วยกันสองวิธี คือ

วิธีแรก — Root เครื่องซะสิ


เมื่อปัญหาที่แท้จริงนั้นเกิดจากเรื่องความปลอดภัย ถ้างั้นก็ลองแหกความปลอดภัยดูสิ เพราะการ Root เครื่องจะทำให้สามารถเข้าถึงระดับ Root ได้ จึงสามารถเข้าถึงไฟล์ได้ทั้งเครื่อง เหมือนกับ Debug Device

พอ Root เรียบร้อยแล้วก็จะสามารถใช้แอพ File Explorer ที่รองรับ Root Access อย่าง ES File Explorer เพื่อเข้าไปก๊อปไฟล์ตามที่ต้องการได้เลย แต่ว่าต้องก๊อปผ่านแอพบนเครื่องแอนดรอยด์เท่านั้น แล้วต้องก๊อปลงคอมพิวเตอร์อีกที

วิธีที่สอง — เขียนโค๊ดให้ก๊อปไฟล์ออกมา


เนื่องจากโฟลเดอร์ดังกล่าวถูกป้องกันไม่ให้เข้าถึงได้ แต่ก็อย่าลืมว่าแอพที่เป็นเจ้าของโฟลเดอร์นั้นๆจะเข้าถึงโฟลเดอร์ได้อย่างปกติ ดังนั้นโค๊ดคำสั่งใดๆก็ตามที่อยู่ในแอพนั้น จะสามารถเข้าถึงโฟลเดอร์นั้นได้ทั้งหมด

ดังนั้นก็ไปเขียนให้แอพนั้นมันดึงฐานข้อมูลออกมาเองเลยสิ!!!

public boolean exportDatabaseFile(Context context, String dbName) { try { File dbFile = context.getDatabasePath(dbName); File exportFile = new File(Environment.getExternalStorageDirectory() + "/" + dbName); FileInputStream fileInputStream = new FileInputStream(dbFile); FileOutputStream fileOutputStream = new FileOutputStream(exportFile); FileChannel fileInputChannel = fileInputStream.getChannel(); FileChannel fileOutputChannel = fileOutputStream.getChannel(); fileInputChannel.transferTo(0, fileInputChannel.size(), fileOutputChannel); fileInputStream.close(); fileOutputStream.close(); return true; } catch (IOException e) { e.printStackTrace(); } return false; }


นี่คือคำสั่งก๊อปไฟล์จากฐานข้อมูลของแอพนั้นๆมาเก็บไว้ใน External Storage โดยเวลาใช้งานจริงๆจะเป็นแบบนี้

ก่อนอื่นต้องสมมติไฟล์ Database Helper ขึ้นมาก่อน

package com.akexorcist.exportdatabase; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; class MyDbHelper extends SQLiteOpenHelper { private static final String DB_NAME = "bakery_store"; private static final int DB_VERSION = 1; public static final String TABLE_NAME = "product"; public static final String COL_NAME = "name"; public static final String COL_PIECE_PRICE = "piece_price"; public static final String COL_CAKE_PRICE = "cake_price"; public MyDbHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + TABLE_NAME +" (_id INTEGER PRIMARY KEY AUTOINCREMENT, " + COL_NAME + " TEXT, " + COL_PIECE_PRICE + " INTEGER, " + COL_CAKE_PRICE + " INTEGER);"); db.execSQL("INSERT INTO " + TABLE_NAME + " (" + COL_NAME + ", " + COL_PIECE_PRICE + ", " + COL_CAKE_PRICE + ") VALUES ('Chocolate Fudge', 95, 750);"); db.execSQL("INSERT INTO " + TABLE_NAME + " (" + COL_NAME + ", " + COL_PIECE_PRICE + ", " + COL_CAKE_PRICE + ") VALUES ('Banana Choc Cake', 55, 500);"); db.execSQL("INSERT INTO " + TABLE_NAME + " (" + COL_NAME + ", " + COL_PIECE_PRICE + ", " + COL_CAKE_PRICE + ") VALUES ('Banoffee', 75, 700);"); db.execSQL("INSERT INTO " + TABLE_NAME + " (" + COL_NAME + ", " + COL_PIECE_PRICE + ", " + COL_CAKE_PRICE + ") VALUES ('Cheese Cake', 85, 800);"); db.execSQL("INSERT INTO " + TABLE_NAME + " (" + COL_NAME + ", " + COL_PIECE_PRICE + ", " + COL_CAKE_PRICE + ") VALUES ('Tiramisu', 85, 800);"); } public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); onCreate(db); } }


ใน Activity ที่ใช้งานฐานข้อมูลก็จะมีการเพิ่มคำสั่งก๊อปไฟล์ฐานข้อมูลเข้าไปด้วย (คืออยากก๊อปไฟล์ตอนไหนก็ใส่ตอนนั้นน่ะแหละ)

package com.akexorcist.exportdatabase; import android.content.Context; import android.os.Bundle; import android.os.Environment; import android.support.v7.app.ActionBarActivity; import android.util.Log; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.channels.FileChannel; public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyDbHelper helper = new MyDbHelper(this); helper.getWritableDatabase(); exportDatabaseFile(this, "bakery_store"); } public boolean exportDatabaseFile(Context context, String dbName) { try { File dbFile = context.getDatabasePath(dbName); File exportFile = new File(Environment.getExternalStorageDirectory() + "/" + dbName); FileInputStream fileInputStream = new FileInputStream(dbFile); FileOutputStream fileOutputStream = new FileOutputStream(exportFile); FileChannel fileInputChannel = fileInputStream.getChannel(); FileChannel fileOutputChannel = fileOutputStream.getChannel(); fileInputChannel.transferTo(0, fileInputChannel.size(), fileOutputChannel); fileInputStream.close(); fileOutputStream.close(); return true; } catch (IOException e) { e.printStackTrace(); } return false; } }


จากนั้นก็เปิดแอพซะ แล้วให้คำสั่งดังกล่าวมันทำงาน แล้วค่อยไปเปิดไฟล์ในเครื่องเพื่อโอนลงคอมพิวเตอร์

สรุปสั้นๆ — ใส่โค๊ดให้แอพมันก๊อปไฟล์ฐานข้อมูลของมันเองไว้ที่ External Storage จะได้ก๊อปลงคอมพิวเตอร์ได้ (ต้องแอพที่เป็นเจ้าของโฟลเดอร์เท่านั้น ไม่สามารถไปก๊อปของแอพตัวอื่นได้)

เพียงเท่านี้ก็ได้ไฟล์ฐานข้อมูลมาเปิดดูบนคอมพิวเตอร์แล้ว