36

多くの連絡先エントリを挿入する必要があるアプリケーションを開発しています。現時点では、合計 6000 の電話番号を持つ約 600 の連絡先があります。最大の連絡先には 1800 の電話番号があります。

今日の時点で、連絡先を保持するためのカスタム アカウントを作成したので、ユーザーは連絡先ビューで連絡先を表示することを選択できます。

しかし、コンタクトの挿入は非常に遅いです。ContentResolver.applyBatch を使用して連絡先を挿入します。さまざまなサイズの ContentProviderOperation リスト (100、200、400) で試してみましたが、合計実行時間は約 1 分です。同じ。すべての連絡先と番号を挿入するには、約 30 分かかります。

SQlite での遅い挿入に関して私が見つけたほとんどの問題は、トランザクションを引き起こします。しかし、私は ContentResolver.applyBatch メソッドを使用しているため、これを制御することはできず、ContentResolver がトランザクション管理を処理してくれると思います。

それで、私の質問に:私は何か間違ったことをしていますか、それともこれをスピードアップするためにできることはありますか?

アンダース

編集: @jcwenger: なるほど。良い説明!

そのため、最初に raw_contacts テーブルに挿入し、次に名前と番号を含むデータテーブルに挿入する必要があります。失うのは、applyBatch で使用する raw_id への後方参照です。

データ テーブルで外部キーとして使用するために、新しく挿入された raw_contacts 行のすべての ID を取得する必要がありますか?

4

7 に答える 7

51

ContentResolver.bulkInsert (Uri url, ContentValues[] values)の代わりに使用ApplyBatch()

ApplyBatch (1) トランザクションを使用し、(2) 操作ごとに 1 回ロック/ロック解除する代わりに、バッチ全体で ContentProvider を 1 回ロックします。このため、一度に 1 つずつ実行する (非バッチ処理) よりもわずかに高速です。

ただし、バッチ内の各操作は異なる URI などを持つことができるため、膨大な量のオーバーヘッドが発生します。「ああ、新しい操作だ! どのテーブルに入れるのかな... ここに 1 行挿入する... あ、新しい操作! どこのテーブルに入れるのかな...」 無限大. URI をテーブルに変換する作業のほとんどは、多数の文字列比較を伴うため、明らかに非常に低速です。

対照的に、bulkInsert は値の山全体を同じテーブルに適用します。「一括挿入...テーブルを見つけて、OK、挿入! 挿入! 挿入! 挿入! 挿入!」はるかに高速。

もちろん、ContentResolver で bulkInsert を効率的に実装する必要があります。自分で書いた場合を除き、ほとんどの場合はそうです。その場合、少しコーディングが必要になります。

于 2011-04-08T15:42:39.947 に答える
10

bulkInsert: 興味のある方のために、ここに私が実験できたコードを示します。int/long/floats の割り当てを回避する方法に注意してください :) これにより、さらに時間を節約できます。

private int doBulkInsertOptimised(Uri uri, ContentValues values[]) {
    long startTime = System.currentTimeMillis();
    long endTime = 0;
    //TimingInfo timingInfo = new TimingInfo(startTime);

    SQLiteDatabase db = mOpenHelper.getWritableDatabase();

    DatabaseUtils.InsertHelper inserter =
        new DatabaseUtils.InsertHelper(db, Tables.GUYS); 

    // Get the numeric indexes for each of the columns that we're updating
    final int guiStrColumn = inserter.getColumnIndex(Guys.STRINGCOLUMNTYPE);
    final int guyDoubleColumn = inserter.getColumnIndex(Guys.DOUBLECOLUMNTYPE);
//...
    final int guyIntColumn = inserter.getColumnIndex(Guys.INTEGERCOLUMUNTYPE);

    db.beginTransaction();
    int numInserted = 0;
    try {
        int len = values.length;
        for (int i = 0; i < len; i++) {
            inserter.prepareForInsert();

            String guyID = (String)(values[i].get(Guys.GUY_ID)); 
            inserter.bind(guiStrColumn, guyID);


            // convert to double ourselves to save an allocation.
            double d = ((Number)(values[i].get(Guys.DOUBLECOLUMNTYPE))).doubleValue();
            inserter.bind(guyDoubleColumn, lat);


            // getting the raw Object and converting it int ourselves saves
            // an allocation (the alternative is ContentValues.getAsInt, which
            // returns a Integer object)

            int status = ((Number) values[i].get(Guys.INTEGERCOLUMUNTYPE)).intValue();
            inserter.bind(guyIntColumn, status);

            inserter.execute();
        }
        numInserted = len;
        db.setTransactionSuccessful();
    } finally {
        db.endTransaction();
        inserter.close();

        endTime = System.currentTimeMillis();

        if (LOGV) {
            long timeTaken = (endTime - startTime);
            Log.v(TAG, "Time taken to insert " + values.length + " records was " + timeTaken + 
                    " milliseconds " + " or " + (timeTaken/1000) + "seconds");
        }
    }
    getContext().getContentResolver().notifyChange(uri, null);
    return numInserted;
}
于 2011-07-27T10:12:35.980 に答える
2

bulkInsert()複数の挿入を高速化するためにをオーバーライドする方法の例は、ここにあります

于 2011-12-22T10:51:52.343 に答える
1

Here is am example of inserting same data amount within 30 seconds.

 public void testBatchInsertion() throws RemoteException, OperationApplicationException {
    final SimpleDateFormat FORMATTER = new SimpleDateFormat("mm:ss.SSS");
    long startTime = System.currentTimeMillis();
    Log.d("BatchInsertionTest", "Starting batch insertion on: " + new Date(startTime));

    final int MAX_OPERATIONS_FOR_INSERTION = 200;
    ArrayList<ContentProviderOperation> ops = new ArrayList<>();
    for(int i = 0; i < 600; i++){
        generateSampleProviderOperation(ops);
        if(ops.size() >= MAX_OPERATIONS_FOR_INSERTION){
            getContext().getContentResolver().applyBatch(ContactsContract.AUTHORITY,ops);
            ops.clear();
        }
    }
    if(ops.size() > 0)
        getContext().getContentResolver().applyBatch(ContactsContract.AUTHORITY,ops);
    Log.d("BatchInsertionTest", "End of batch insertion, elapsed: " + FORMATTER.format(new Date(System.currentTimeMillis() - startTime)));

}
private void generateSampleProviderOperation(ArrayList<ContentProviderOperation> ops){
    int backReference = ops.size();
    ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null)
            .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null)
            .withValue(ContactsContract.RawContacts.AGGREGATION_MODE, ContactsContract.RawContacts.AGGREGATION_MODE_DISABLED)
            .build()
    );
    ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
                    .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, backReference)
                    .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
                    .withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, "GIVEN_NAME " + (backReference + 1))
                    .withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, "FAMILY_NAME")
                    .build()
    );
    for(int i = 0; i < 10; i++)
        ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
                        .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, backReference)
                        .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
                        .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MAIN)
                        .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, Integer.toString((backReference + 1) * 10 + i))
                        .build()
        );
}

The log: 02-17 12:48:45.496 2073-2090/com.vayosoft.mlab D/BatchInsertionTest﹕ Starting batch insertion on: Wed Feb 17 12:48:45 GMT+02:00 2016 02-17 12:49:16.446 2073-2090/com.vayosoft.mlab D/BatchInsertionTest﹕ End of batch insertion, elapsed: 00:30.951

于 2016-02-17T10:58:21.627 に答える