Android開発でのSQLiteの使用。 ヒントとコツ



こんにちは、Habr!

私はしばらくの間Android向けに開発してきましたが、今日は1つの問題を解決することで得られた経験についてお話ししたいと思います。

警告:
経験豊富な開発者にとって、この記事は新しいものではないでしょう。


私にとって、このプロジェクトは、SQLiteを密接に使用する必要がある最初のプロジェクトでした( 選択<anything> <from somewhere>のためだけに必要でした)。



タスクは次のとおりです。商品のバーコードをスキャンし、それらを認識し、ディレクトリと照合して結果をユーザーに表示します。



決定の過程で、彼はいくつかの興味深い結論を出しました。




1)テーブルの主キーは「 _id 」と呼ばれる必要はありません。



これは、標準メカニズムを使用してテーブルを表示する場合にのみ必要です。

ListView-CursorAdapter-LoaderManager-ContentProviderここの注意を参照

原則として、ドキュメントに記載されている簡単なステートメントですが、何らかの形で(個人的には、とにかく)テーブル内の主キーフィールドは_idと呼ばれる必要があるという考えがありました。 回避するために、私はこれまで詳細を説明することなく常にこれを行いました。



以前に開発したテーブル構造をSQLiteにインポートする必要がある場合は、別のキー名が必要になる場合があります。

私の場合、ディレクトリテーブルには既に独自のフィールド[Something_ID]があり 、これらのテーブルによってこれらのテーブルが結合されています。 また、これらのフィールドは自動的にインデックス化されるため、これらのフィールドを主キーにすることは論理的です。



2)データベース構造を自動的に作成し、初期データを入力するという問題の解決策。



最初は、アプリケーションを初めて起動するときに、リモートサーバーからデータを受信し、参照テーブルの挿入を行うことを考えました。 大量のデータ(2Mbを少し超える)があるため、これは悪いオプションです。



もう少し良いのはbulkInsertを実行することです 。 単一のトランザクションにデータを挿入します。 動作は速くなりますが、基本的に元のバージョンと違いはありません。 この話題についてはすでに良い記事があります。



プロバイダーでのbulkInsertの実装のバリアント:

@Override public int bulkInsert(Uri uri, ContentValues[] values) { int numInserted = 0; final String table = selectTable(uri); database = databaseHandler.getWritableDatabase(); database.beginTransaction(); try { for (ContentValues cv : values) { if (database.insert(table, null, cv) <= 0) { throw new SQLException("Failed to insert row into " + uri); } } database.setTransactionSuccessful(); numInserted = values.length; } finally { database.endTransaction(); getContext().getContentResolver().notifyChange(uri, null); } return numInserted; }
      
      





そして、ユーザーがアプリケーションで作業を開始できるように、データが事前に入力されたテーブルを用意したいと思います。 そして、代替手段がありました-android-SQLite-asset-helper library



本質はこれです。データベースは作業時にデバイス上に作成されませんが、アプリケーションの開発プロセスでは、圧縮され、圧縮され、 アセットに入れられます。 さらにプロジェクトでは、データベースヘルパーは標準のSQLiteOpenHelperからではなく、 SQLiteAssetHelperから継承されます。 そして、それはすべてです。最初のユーザーの呼び出しで、データベースがアプリケーションにコピーされ、実装の詳細がヘルパーにカプセル化されます(そして、私はそれらに行くのが面倒でした)。



このアプローチは、その利点が非常に気に入りました。







3)SQLiteビューとAndroid LoaderManagerの相互作用の機能。

LoaderManagerの概要と使用方法について質問しませんが、個人的には素晴らしい一連の記事に助けられています。 リスト内の変更されたデータを自動的に更新するタスクを彼に任せるために、LoaderManagerを使用したいとしか言​​えません。



ただし、テーブルにデータを挿入し、それに関連付けられたビューから表示する必要があります。値はidフィールドに置き換えられます。

  CREATE TABLE [table_scan] ( [_id] INTEGER PRIMARY KEY AUTOINCREMENT, [NR_ID] INTEGER NOT NULL, [T_ID] INTEGER NOT NULL, [Color_ID] INTEGER NOT NULL, [R_ID] INTEGER NOT NULL, [Barcode] TEXT NOT NULL, [NumberSeat] INTEGER, [Date] DATETIME NOT NULL DEFAULT(DATETIME('now', 'localtime')), [Deleted] INTEGER NOT NULL DEFAULT '0', [Status] INTEGER NOT NULL DEFAULT '0', [Export] INTEGER NOT NULL DEFAULT '0'); CREATE VIEW [view_scan] AS SELECT _id, Barcode, Status, Deleted, NumberSeat, goods_catalog.T_Articul, colors_catalog.Color_Name, sizes_catalog.R_Name FROM table_scan INNER JOIN goods_catalog ON goods_catalog.T_ID = table_scan.T_ID INNER JOIN colors_catalog ON colors_catalog.Color_ID = table_scan.Color_ID INNER JOIN sizes_catalog ON sizes_catalog.R_ID = table_scan.R_ID WHERE Deleted = 0;
      
      





おでこに、このオプションは、判明したとおり、機能しません。 テーブル上のuriローダーとビュー上のuriの場合-2つの異なるuri :)

つまり table_scanの代わりにview_scanを初期化すると、テーブルに挿入してもリストは更新されません。

すべてがテーブルで完全に更新されますが、美しい値の代わりに、出力はIDキーであり、人々には理解できません。



SQLiteのドキュメントで最初に見つかっソリューションは、適切なソリューションであることが判明しました。 ビューにデータを直接挿入することはできません(これは想定されています)が、目的のテーブルにデータを自動的に挿入するトリガーを作成できます。



OK、不足しているidフィールドでビューを補完します

 CREATE VIEW [view_scan] AS SELECT table_scan._id, table_scan.NR_ID, table_scan.T_ID,table_scan.Color_ID, table_scan.R_ID, table_scan.Barcode, table_scan.NumberSeat, table_scan.Deleted, table_scan.Status, goods_catalog.T_Articul, colors_catalog.Color_Name, sizes_catalog.R_Name FROM table_scan INNER JOIN goods_catalog ON goods_catalog.T_ID = table_scan.T_ID INNER JOIN colors_catalog ON colors_catalog.Color_ID = table_scan.Color_ID INNER JOIN sizes_catalog ON sizes_catalog.R_ID = table_scan.R_ID WHERE Deleted = 0;
      
      





挿入トリガーを作成します。

 CREATE TRIGGER insert_view_scan instead of insert on view_scan begin insert into table_scan(NR_ID,T_ID,Color_ID,R_ID,Barcode,NumberSeat,Status) values(new.NR_ID, new.T_ID, new.Color_ID, new.R_ID, new.Barcode, new.NumberSeat, new.Status); end;
      
      





これですべてが機能します。 LoaderManagerでは、ビューuriが初期化され、挿入要求もビューに送られ、SQLiteが残りの作業を行います。 同時に、ローダーは必要なことを行います。 カーソルを監視し、変更されたデータをリストアダプタに自動的に転送します。



それだけです AndroidでSQLiteを操作するための高度な技術について他の記事を読むのは興味深いでしょう。

まあ、客観的な批判も興味深いです:)



All Articles