2011年1月10日月曜日

Android あらかじめ作成した SQLite database をアプリに取り込む

Android のサンプルやチュートリアルでは、アプリ実行時に SQLite データベースを作成してデータの追加や更新、削除などを行っているのがほとんどです。

しかし、あらかじめ作成しておいた SQLite database をアプリに仕込みたい場合があります。

そこで、ここでは sqlite3 など使って作成した自分の SQLite database ファイルを、アプリの asset に入れ、初回起動時にアプリのシステムデータベース領域にコピーする方法を紹介します。


1. SQLite database ファイルを用意する

私は Ubuntu 派なので普通に sqlite3 を使います。(Windows とか Mac はよくわかりません... これとか? SQLite Database Browser)

主テーブルの他に android_metadata という名前のテーブルを作成します。中身は locale です。主テーブルの primary key は _id という名前にします(Android が id を理解できる用にするためです)。

$ cat createTable.sql create table android_metadata ( locale text default 'en_US' ); create table sample_data ( _id integer primary key not null, name text, address text, tel text );
ちなみに日本は ja_JP です。


データベースとテーブルを作成します。
$ sqlite3 mysqlite_database.db < createTable.sql

作成したテーブルに CSVファイルをインポートします。
ここでは、

1,name1,address1,tel1
2,name2,address2,tel2
...

というデータを想定しています。

# 改行コードの "\r" や、各カラムの " 囲みを削除するには
#
# $ tr -d "\r" < old.csv | tr -d '"' > new.csv
#
# とかやると幸せになれるかも。
# 文字コードは nkf とかで。
# primary key 部分は awk とかで頑張ってみるとか。

sqlite3 -separator , mysqlite_database.db ".import mydata.csv sample_data"

なにも言われなければうまくいったかも
sqlite3 mysqlite_database.db "select * from sample_data where _id=1" とかで中身をチェックして入っていればOK。


2. 展開&コピーして Android アプリからアクセスできるようにする

1. で作った SQLite database ファイルを asset に入れ、SQLiteOpenHelper を継承したクラスを作成します。

public class DataBaseHelper extends SQLiteOpenHelper { private static String DB_NAME = "my_database"; private static String DB_NAME_ASSET = "my_database.db"; private static final int DATABASE_VERSION = 17; private SQLiteDatabase mDatabase; private final Context mContext; private final File mDatabasePath; public DataBaseHelper(Context context) { super(context, DB_NAME, null, DATABASE_VERSION); mContext = context; mDatabasePath = mContext.getDatabasePath(DB_NAME); } /** * asset に格納したデータベースをコピーするための空のデータベースを作成する */ public void createEmptyDataBase() throws IOException { boolean dbExist = checkDataBaseExists(); if (dbExist) { // すでにデータベースは作成されている } else { // このメソッドを呼ぶことで、空のデータベースがアプリのデフォルトシステムパスに作られる getReadableDatabase(); try { // asset に格納したデータベースをコピーする copyDataBaseFromAsset(); String dbPath = mDatabasePath.getAbsolutePath(); SQLiteDatabase checkDb = null; try { checkDb = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READWRITE); } catch (SQLiteException e) { } if (checkDb != null) { checkDb.setVersion(DATABASE_VERSION); checkDb.close(); } } catch (IOException e) { throw new Error("Error copying database"); } } } /** * 再コピーを防止するために、すでにデータベースがあるかどうか判定する * * @return 存在している場合 {@code true} */ private boolean checkDataBaseExists() { String dbPath = mDatabasePath.getAbsolutePath(); SQLiteDatabase checkDb = null; try { checkDb = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READONLY); } catch (SQLiteException e) { // データベースはまだ存在していない } if (checkDb == null) { // データベースはまだ存在していない return false; } int oldVersion = checkDb.getVersion(); int newVersion = DATABASE_VERSION; if (oldVersion == newVersion) { // データベースは存在していて最新 checkDb.close(); return true; } // データベースが存在していて最新ではないので削除 File f = new File(dbPath); f.delete(); return false; } /** * asset に格納したデーだベースをデフォルトのデータベースパスに作成したからのデータベースにコピーする */ private void copyDataBaseFromAsset() throws IOException{ // asset 内のデータベースファイルにアクセス InputStream mInput = mContext.getAssets().open(DB_NAME_ASSET); // デフォルトのデータベースパスに作成した空のDB OutputStream mOutput = new FileOutputStream(mDatabasePath); // コピー byte[] buffer = new byte[1024]; int size; while ((size = mInput.read(buffer)) > 0) { mOutput.write(buffer, 0, size); } // Close the streams mOutput.flush(); mOutput.close(); mInput.close(); } public SQLiteDatabase openDataBase() throws SQLException { return getReadableDatabase(); } @Override public void onCreate(SQLiteDatabase db) { } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } @Override public synchronized void close() { if(mDataBase != null) mDataBase.close(); super.close(); } }


あとは、普通にアクセスする

private DataBaseHelper mDbHelper; private SQLiteDatabase db; private StationCodeDao mStationCodeDao; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setDatabase(); } @Override public void onDestroy() { db.close(); super.onDestroy(); } private void setDatabase() { mDbHelper = new DataBaseHelper(this); try { mDbHelper.createEmptyDataBase(); db = mDbHelper.openDataBase(); } catch (IOException ioe) { throw new Error("Unable to create database"); } catch(SQLException sqle){ throw sqle; } } private static final String[] COLUMNS = {"_id", "name", "address", "tel"}; private Cursor findData(int id) { Cursor cursor = db.query(TABLE_NAME, COLUMNS, "where _id=" + id, null, null, null, COLUMN_ID); return cursor; }

---------------------------------------

2011年2月4日 追記

assets に入れた db が 1MB 以上の場合、うまくいかないことがあるみたいです。
そんなときは、itog さんのブログ

apkに1MB以上のデータベースファイルを含める - Hacking My Way ~ itogのhack日記 -

を参考に、db を圧縮するなり分割するなりすると、うまくいくようです。


---------------------------------------

2013年10月22日 追記

データベースのパスを固定から Context の getDatabasePath() を使うように修正しました。
バージョン番号を使って、アップデート時にデータベースを再度 Asset からコピーできるようにしました。



 

27 件のコメント:

  1. はじめまして、平塚と申します
    すみません、質問させてください。

    上記の方法で、実装してるのですが、
    while ((size = mInput.read(buffer)) > 0){
    のところでIOExceptionをはいて、落ちてしまいます。

    この場合に考えられる原因ってなんでしょうか??
    教えていただけると幸いです。
    よろしくお願いいたします

    返信削除
  2. はじめまして、塚原と申します。
    お聞きしたいことがあり、書き込みしました。

    上記の方法で実装しているのですが、携帯によって何事もなく動く携帯と強制終了する携帯があるのですが、原因がわからずこまっています。教えて頂けると幸いです。
    よろしくお願いいたします。

    動く携帯はGALAXYS SC-02B

    動かない携帯はARROWS X LTE F-05D

    返信削除
  3. 私もArrowsシリーズで動かなかったので調べたところ、Arrowsでは
    "/data/data/YOUR_PACKAGE/databases/" フォルダへのファイルアクセスがパーミッションで制限されているようです。
    databases を使わずに、以下のようにfilesフォルダを利用するようにしたら動きました。


    public void createEmptyDataBase() throws IOException {
    boolean dbExist = checkDataBaseExists();

    if (dbExist) {
    // すでにデータベースは作成されている
    } else {
    try {
    // asset に格納したデータベースをコピーする
    copyDataBaseFromAsset();
    } catch (IOException e) {
    throw new Error("Error copying database");
    }
    }
    }

    private boolean checkDataBaseExists() {
    SQLiteDatabase checkDb = null;

    try {
    File fileObj = mContext.getFileStreamPath(DATABASE_NAME);
    String dbPath = fileObj.getAbsolutePath();
    checkDb = SQLiteDatabase.openDatabase(dbPath, null,
    SQLiteDatabase.OPEN_READONLY);
    } catch (SQLiteException e) {
    // データベースはまだ存在していない
    }

    if (checkDb != null) {
    checkDb.close();
    }
    return checkDb != null ? true : false;
    }

    private void copyDataBaseFromAsset() throws IOException {
    // asset 内のデータベースファイルにアクセス
    InputStream mInput = mContext.getAssets().open(DATABASE_NAME);
    OutputStream mOutput = mContext.openFileOutput(DATABASE_NAME, Context.MODE_PRIVATE);

    // コピー
    byte[] buffer = new byte[1024];
    int size;
    while ((size = mInput.read(buffer)) > 0) {
    mOutput.write(buffer, 0, size);
    }

    // Close the streams
    mOutput.flush();
    mOutput.close();
    mInput.close();
    }

    @Override
    public SQLiteDatabase getWritableDatabase() {
    SQLiteDatabase db = null;

    try {
    File fileObj = mContext.getFileStreamPath(DATABASE_NAME);
    String dbPath = fileObj.getAbsolutePath();
    db = SQLiteDatabase.openDatabase(dbPath, null,
    SQLiteDatabase.OPEN_READWRITE);
    } catch (SQLiteException e) {
    throw new Error("Error opening database");
    }

    return db;
    }

    @Override
    public SQLiteDatabase getReadableDatabase() {
    SQLiteDatabase db = null;

    try {
    File fileObj = mContext.getFileStreamPath(DATABASE_NAME);
    String dbPath = fileObj.getAbsolutePath();
    db = SQLiteDatabase.openDatabase(dbPath, null,
    SQLiteDatabase.OPEN_READONLY);
    } catch (SQLiteException e) {
    throw new Error("Error opening database");
    }

    return db;
    }

    返信削除
    返信
    1. 唐沢 様

      タロイです。
      ありがとうございます。

      早速、その方法で作り直して動かしてみたいと思います。
      困り果てていたので、助かりました。本当にありがとうございます。(*・ω・)*_ _))ペコリン

      削除
  4. //The Android のデフォルトでのデータベースパス
    private static String DB_PATH = "/data/data/YOUR_PACKAGE/databases/"

    このパスにデータベースが置かれるかどうかは Androidのバージョンによって依存するでしょうから APIを利用して取得すべきでしょう

    返信削除
  5. はじめまして。
    参考にさせていただき、動かすことができました。
    ありがとうございました。

    しかし、つい先日まで、何の問題もなく動いていたのですが、

    メジャーアップデートした実機(android4.1.2)で
    android.database.sqlite.SQliteException:no such table・・・のエラーを吐いて落ちるようになってしまいました。

    もちろんテーブルもちゃんと存在していますし、何より、全くプログラムは動いていたものから変わっていないのです。

    どう対応すればよいのか分からず困っています。
    どんなことが考えられるでしょうか。

    返信削除
  6. Thank you for your post. This is excellent information. It is amazing and wonderful to visit your site.
    mobile app developers

    返信削除
  7. いつもお世話になってます。

    DataBaseHelperの27行目
    getReadableDatabase();

    SQLiteDatabase db = getReadableDatabase();
    db.close();
    のようにしないと端末によっては例外を吐いて落ちますね。

    返信削除
  8. Develop performance-optimized, intuitive, and conversion-generating apps by hire react developer.

    返信削除
  9. Video Analytics Solutions uses video surveillance systems to extract accessible, usable, and measurable information from live or stored video footage.


    返信削除
  10. Take your web and mobile app development vision to the next level of innovation with high-quality and cost-effective services from our react js agency!

    返信削除
  11. このコメントは投稿者によって削除されました。

    返信削除
  12. The fili is a medicine, particularly intended to treat hypoactive sexual craving issue otherwise called (HSDD) in ladies who are pre-menopausal.
    Flibanserin 100mg

    返信削除
  13. The Chrome Hearts Hoodie truly represents premium streetwear with its bold design and high-quality fabric. It’s a perfect choice for anyone looking to combine comfort with luxury fashion and a standout style.

    返信削除
  14. This post offers a straightforward explanation of using SQLite databases in Android development, which is especially helpful for beginners. I appreciate how the concepts are broken down in a logical sequence, making database handling feel less intimidating. Articles like this are valuable learning references, as they focus on practical understanding rather than overwhelming readers with unnecessary complexity. digital marketing company in Delhi

    返信削除
  15. I really like how the website is designed. The layout looks clean and professional, and it’s easy to understand what the platform is offering. The information is presented in a clear way, which makes it simple for new visitors to explore the services without feeling confused. Overall, the browsing experience feels smooth and well organized.

    https://www.reddyannaofficialls.com

    返信削除

  16. Lotus365offiicial.com is a very well-designed and informative platform. The website layout is clean, fast, and easy to navigate, which makes the user experience smooth even for new visitors. All the important sections like sports betting, casino games, login guidance, and support information are explained clearly. I really like how the content is simple yet detailed, making it easy to understand. Overall, it looks like a reliable and well-managed website that focuses on providing useful information to users.

    Lotus365

    返信削除
  17. This article does a great job breaking down how SQLite works on Android in a clear, step-by-step way. I appreciate how the explanation makes a technical topic accessible even if someone is new to mobile development. Posts like this help readers build foundational knowledge and feel more confident tackling database work in their own projects. Guidewire Training

    返信削除
  18. 123hero.co looks like a well-designed online gaming platform with a variety of options including sports betting, live casino, and slot games. The site appears organized and user-friendly, and the bonus offers seem attractive for new players. As always, it’s important to read the terms carefully and practice responsible gaming before getting started.
    123hero

    返信削除
  19. With a strong focus on Digital Marketing Miami, LearnAbout LLC supports brands through smart strategies, performance-driven content, and technical optimization. Their blog and resources guide users in improving website traffic, user experience, and long-term online success. Digital Marketing Miami

    返信削除
  20. The discussion here about Android SQLLine database topics is helpful for developers looking to understand how to manage data on mobile platforms. Practical insights into database handling make it easier to grasp technical workflows. Threads like this contribute to knowledge sharing within the development community. Guidewire Training in Delhi

    返信削除
  21. I like how everything is presented in a clear and straightforward way. Easy to understand and access.https://ee88.cat/

    返信削除
  22. Hãy truy cập ngay kèo nhà cái hôm nay để trải nghiệm "thiên đường bóng đá" đích thực với hơn 2.000 lượt truy cập mỗi ngày. Nền tảng tổng hợp mọi chỉ số Odds, bảng xếp hạng và tin chuyển nhượng từ những nguồn uy tín nhất thế giới trong vòng 24 giờ qua. Sử dụng dữ liệu từ kèo nhà cái hôm nay, game thủ dễ dàng sàng lọc các trận đấu có tỷ lệ ăn thưởng hấp dẫn và cảnh giác trước những thay đổi tỷ lệ đột ngột nhằm điều hướng rủi ro của nhà cái. Cập nhật đội hình phút chót và biến động thị trường ngay tại đây để tối ưu hóa lợi nhuận của bạn! kèo nhà cái hôm nay

    返信削除
  23. Trong bối cảnh thị trường số bùng nổ, việc xác định một cổng game đánh bài đổi thưởng an toàn là yếu tố then chốt đối với mọi bet thủ. Một sân chơi chất lượng phải hội đủ các tiêu chí về giấy phép hoạt động hợp pháp và cơ chế chống gian lận tuyệt đối. Tại đây, chúng tôi cung cấp cái nhìn tổng quan về các nền tảng game đánh bài đổi thưởng có lượng người chơi đông đảo nhất 2026, nơi quyền lợi của bạn luôn được đặt lên hàng đầu. Từ những vòng quay Nổ Hũ đến các ván bài cân não, tất cả đều được vận hành một cách công bằng, đảm bảo mang lại những giây phút giải trí thăng hoa và phần thưởng xứng đáng. ĐÁNH BÀI ĐỔI THƯỞNG

    返信削除
  24. Nếu bạn đang tìm kiếm một sân chơi cá cược đẳng cấp năm 2026, IWIN chính là điểm đến không thể bỏ qua. Hệ thống ghi điểm tuyệt đối nhờ sự kết hợp giữa các trò chơi bài truyền thống và công nghệ livestream hiện đại. Ngay khi gia nhập, IWIN chào đón bạn bằng món quà lì xì 88K, tạo điều kiện thuận lợi để khám phá kho game đồ sộ. Dựa trên nền tảng công nghệ mobile tiên tiến, IWIN đảm bảo mọi giao dịch nạp rút đều diễn ra an toàn và nhanh chóng. Hãy cùng tham gia cộng đồng IWIN để trải nghiệm đẳng cấp sòng bài quốc tế ngay trên chiếc điện thoại của mình. IWIN

    返信削除