シンプルなアプリを使用しているため、 vogella.comの手順に従っていくつかの変更を加えたシンプルな ContentProvider があります。android-support-v4.jar
ここでは、3 つのボタンと 1 つの ListFragment がすべてカスタム ContentProvider を使用しており、問題を探していますが、ボタンを高速にパルスしようとすると、ドキュメントに記載されているSQLiteMisuseExceptionが発生することがあります
このエラーは、アプリケーションが SQLiteStatement オブジェクトを作成し、アプリケーション内の複数のスレッドが同時にそれを使用できるようにした場合に発生する可能性があります
しかし、私は多くのスレッドを持っていません(少なくとも私は思います)。
更新:アプリがUIの動作を停止することがありますが、応答しません
更新 2 : Galaxy ACE (エラーあり) でテストしているデバイスの速度応答に問題がある可能性があり、Galaxy SIII は正常に動作します
MainActivity.java
package com.example.providertest;
import java.util.Random;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.SimpleCursorAdapter;
import android.view.Menu;
import android.view.View;
public class MainActivity extends FragmentActivity {
private List mLista;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLista = (List) getSupportFragmentManager().findFragmentById(R.id.la_lista);
getSupportLoaderManager().initLoader(0, null, mLista);
}
public static class List extends ListFragment implements LoaderCallbacks<Cursor>
{
private SimpleCursorAdapter mAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
final String[] from = new String[] { CommentHelper.COLUMN_COMMENT };
final int[] to = new int[] {android.R.id.text1 };
mAdapter = new SimpleCursorAdapter(
getActivity().getApplicationContext(), R.layout.lista_item, null, from, to, 0);
setListAdapter(mAdapter);
}
@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
final CursorLoader loader = new CursorLoader(
getActivity().getApplicationContext(),
CommentProvider.CONTENT_URI,
CommentDataSource.sAllColumns, null, null, null);
return loader;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
mAdapter.swapCursor(cursor);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void buttonPressed(View view)
{
final String[] comments = {"Excelente", "Bueno", "Que mas da", "Malo"};
Comment comment = null;
switch (view.getId()) {
case R.id.add:
comment = new Comment();
final int nextInt = new Random().nextInt(comments.length);
comment.setComment(comments[nextInt]);
getContentResolver().insert(CommentProvider.CONTENT_URI, CommentDataSource.toValues(comment));
break;
case R.id.delete_first:
deleteOne("ASC");
break;
case R.id.delete_last:
deleteOne("DESC");
break;
}
}
private void deleteOne(String order) {
final Cursor c = getContentResolver().query(CommentProvider.CONTENT_URI, null, null, null, "_ID " + order + " LIMIT 1");
c.moveToFirst();
if (!c.isAfterLast()) {
final String _id = c.getString(c.getColumnIndexOrThrow(CommentHelper.COLUMN_ID));
getContentResolver().delete(CommentProvider.CONTENT_URI, "_ID = ?", new String[] { _id });
}
c.close();
}
}
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context=".MainActivity" >
<LinearLayout
android:id="@+id/group"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="@+id/add"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="@string/add_new"
android:onClick="buttonPressed" />
<Button
android:id="@+id/delete_first"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="@string/delete_first"
android:onClick="buttonPressed" />
<Button
android:id="@+id/delete_last"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:onClick="buttonPressed"
android:text="@string/delete_last" />
</LinearLayout>
<fragment
android:id="@+id/la_lista"
class="com.example.providertest.MainActivity$List"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
データ
CommentHelper.java
package com.example.providertest;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
public class CommentHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "comments.db";
private static final int DATABASE_VERSION = 1;
public static final String TABLE_NAME = "comments";
public static final String COLUMN_ID = BaseColumns._ID;
public static final String COLUMN_COMMENT = "comment";
private static final String DATABASE_CREATE = "CREATE TABLE " +
TABLE_NAME + "(" +
COLUMN_ID + " INTEGER PRIMARY KEY, " +
COLUMN_COMMENT + " TEXT NOT NULL" +
");";
private static final String DATABASE_DROP = "DROP TABLE IF EXISTS " + TABLE_NAME;
public CommentHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(DATABASE_DROP);
onCreate(db);
}
}
CommentDataSource.java
package com.example.providertest;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
public class CommentDataSource {
private final CommentHelper mHelper;
public static String[] sAllColumns = {CommentHelper.COLUMN_ID, CommentHelper.COLUMN_COMMENT};
public CommentDataSource(Context context) {
mHelper = new CommentHelper(context);
}
public SQLiteDatabase getDatabase()
{
return getDatabase(false);
}
public SQLiteDatabase getDatabase(boolean writable)
{
return writable ? mHelper.getWritableDatabase() : mHelper.getReadableDatabase();
}
public Comment Save(Comment comment)
{
final ContentValues values = toValues(comment);
final long insertId = insert(values);
comment.setId(insertId);
return comment;
}
public long insert(ContentValues values)
{
final SQLiteDatabase db = mHelper.getWritableDatabase();
try {
final long insertId = db.insert(CommentHelper.TABLE_NAME, null, values);
return insertId;
} finally {
db.close();
}
}
public int update(ContentValues values, String selection, String[] selectionArgs)
{
final SQLiteDatabase db = mHelper.getWritableDatabase();
try {
final int rowsUpdated = db.update(CommentHelper.TABLE_NAME, values, selection, selectionArgs);
return rowsUpdated;
} finally {
db.close();
}
}
public int update(long id, ContentValues values)
{
final String whereClause = String.format("%s = %s", CommentHelper.COLUMN_ID, id);
return update(values, whereClause, null);
}
public int delete(String selection, String[] selectionArgs)
{
final SQLiteDatabase db = mHelper.getWritableDatabase();
try {
final int rowsDeleted = db.delete(CommentHelper.TABLE_NAME, selection, selectionArgs);
return rowsDeleted;
} finally {
db.close();
}
}
public int delete(long id)
{
final SQLiteDatabase db = mHelper.getWritableDatabase();
final String whereClause = String.format("%s = %s", CommentHelper.COLUMN_ID, id);
try {
final int rowsDeleted = db.delete(CommentHelper.TABLE_NAME, whereClause, null);
return rowsDeleted;
} finally {
db.close();
}
}
public int delete(Comment comment)
{
return delete(comment.getId());
}
public List<Comment> findAll()
{
final List<Comment> comments = new ArrayList<Comment>();
final SQLiteDatabase db = mHelper.getReadableDatabase();
final Cursor cursor = db.query(CommentHelper.TABLE_NAME, sAllColumns, null, null, null, null, null);
try {
cursor.moveToFirst();
while(!cursor.isAfterLast()) {
comments.add(fromCursor(cursor));
cursor.moveToNext();
}
return comments;
} finally {
cursor.close();
db.close();
}
}
public Comment fromCursor(Cursor cursor)
{
final Comment comment = new Comment();
comment.setId(cursor.getLong(0));
comment.setComment(cursor.getString(1));
return comment;
}
public static ContentValues toValues(Comment comment)
{
final ContentValues values = new ContentValues();
// Si el valor ho se ha definido es mejor no incluirlo
// Evita problemas con el AUTOINCREMENT de SQLite
if(comment.getId() > 0) {
values.put(CommentHelper.COLUMN_ID, comment.getId());
}
values.put(CommentHelper.COLUMN_COMMENT, comment.getComment());
return values;
}
}
CommentProvider.java
package com.example.providertest;
import java.util.Arrays;
import java.util.HashSet;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
public class CommentProvider extends ContentProvider {
private CommentDataSource mDataSource;
private static final String AUTHORITY = "com.example.comments";
private static final int COMMENT = 10;
private static final int COMMENT_ID = 20;
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/comments");
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sUriMatcher.addURI(AUTHORITY, "comments", COMMENT);
sUriMatcher.addURI(AUTHORITY, "comments/#", COMMENT_ID);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int rowsDeleted = 0;
switch (sUriMatcher.match(uri)) {
case COMMENT:
rowsDeleted = mDataSource.delete(selection, selectionArgs);
break;
case COMMENT_ID:
final long id = Long.parseLong(uri.getLastPathSegment());
if(TextUtils.isEmpty(uri.getLastPathSegment())) {
rowsDeleted = mDataSource.delete(id);
} else {
selection = CommentHelper.COLUMN_ID + " = " + id + " and " + selection;
rowsDeleted = mDataSource.delete(selection, selectionArgs);
}
break;
default:
throw new IllegalArgumentException("Unknow URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return rowsDeleted;
}
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
long insertId = 0;
switch (sUriMatcher.match(uri)) {
case COMMENT:
insertId = mDataSource.insert(values);
break;
}
getContext().getContentResolver().notifyChange(uri, null);
return Uri.parse("comments/" + insertId);
}
@Override
public boolean onCreate() {
mDataSource = new CommentDataSource(getContext());
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
final SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
checkColumns(projection);
builder.setTables(CommentHelper.TABLE_NAME);
switch (sUriMatcher.match(uri)) {
case COMMENT_ID:
builder.appendWhere(String.format("%s = %s", CommentHelper.COLUMN_ID, uri.getLastPathSegment()));
break;
case COMMENT:
break;
default:
throw new IllegalArgumentException("Unknow URI: " + uri);
}
final SQLiteDatabase db = mDataSource.getDatabase();
final Cursor cursor = builder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
private void checkColumns(String[] projection) {
if (projection != null) {
final HashSet<String> requestedColumns = new HashSet<String>(Arrays.asList(projection));
final HashSet<String> availableColumns = new HashSet<String>(Arrays.asList(CommentDataSource.sAllColumns));
if (!availableColumns.containsAll(requestedColumns)) {
throw new IllegalArgumentException("Unknow columns in projection");
}
}
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int rowsUpdated = 0;
switch (sUriMatcher.match(uri)) {
case COMMENT_ID:
final long id = Long.parseLong(uri.getLastPathSegment());
if(TextUtils.isEmpty(uri.getLastPathSegment())) {
rowsUpdated = mDataSource.update(id, values);
break;
} else {
selection = CommentHelper.COLUMN_ID + " = " + id + " and " + selection;
}
case COMMENT:
rowsUpdated = mDataSource.update(values, selection, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknow URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return rowsUpdated;
}
}
エラー
05-04 09:45:48.031: D/dalvikvm(19873): GC_CONCURRENT freed 327K, 48% free 3114K/5895K, external 1073K/1585K, paused 4ms+7ms
05-04 09:45:54.421: W/dalvikvm(19873): threadid=11: thread exiting with uncaught exception (group=0x40018578)
05-04 09:45:54.554: E/AndroidRuntime(19873): FATAL EXCEPTION: ModernAsyncTask #3
05-04 09:45:54.554: E/AndroidRuntime(19873): java.lang.RuntimeException: An error occured while executing doInBackground()
05-04 09:45:54.554: E/AndroidRuntime(19873): at android.support.v4.content.ModernAsyncTask$3.done(ModernAsyncTask.java:137)
05-04 09:45:54.554: E/AndroidRuntime(19873): at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:274)
05-04 09:45:54.554: E/AndroidRuntime(19873): at java.util.concurrent.FutureTask.setException(FutureTask.java:125)
05-04 09:45:54.554: E/AndroidRuntime(19873): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:308)
05-04 09:45:54.554: E/AndroidRuntime(19873): at java.util.concurrent.FutureTask.run(FutureTask.java:138)
05-04 09:45:54.554: E/AndroidRuntime(19873): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
05-04 09:45:54.554: E/AndroidRuntime(19873): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
05-04 09:45:54.554: E/AndroidRuntime(19873): at java.lang.Thread.run(Thread.java:1019)
05-04 09:45:54.554: E/AndroidRuntime(19873): Caused by: android.database.sqlite.SQLiteMisuseException: library routine called out of sequence: , while compiling: SELECT _id, comment FROM comments
05-04 09:45:54.554: E/AndroidRuntime(19873): at android.database.sqlite.SQLiteCompiledSql.native_compile(Native Method)
05-04 09:45:54.554: E/AndroidRuntime(19873): at android.database.sqlite.SQLiteCompiledSql.compile(SQLiteCompiledSql.java:92)
05-04 09:45:54.554: E/AndroidRuntime(19873): at android.database.sqlite.SQLiteCompiledSql.<init>(SQLiteCompiledSql.java:65)
05-04 09:45:54.554: E/AndroidRuntime(19873): at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:83)
05-04 09:45:54.554: E/AndroidRuntime(19873): at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:49)
05-04 09:45:54.554: E/AndroidRuntime(19873): at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:42)
05-04 09:45:54.554: E/AndroidRuntime(19873): at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1356)
05-04 09:45:54.554: E/AndroidRuntime(19873): at android.database.sqlite.SQLiteQueryBuilder.query(SQLiteQueryBuilder.java:330)
05-04 09:45:54.554: E/AndroidRuntime(19873): at android.database.sqlite.SQLiteQueryBuilder.query(SQLiteQueryBuilder.java:280)
05-04 09:45:54.554: E/AndroidRuntime(19873): at com.example.providertest.CommentProvider.query(CommentProvider.java:103)
05-04 09:45:54.554: E/AndroidRuntime(19873): at android.content.ContentProvider$Transport.query(ContentProvider.java:187)
05-04 09:45:54.554: E/AndroidRuntime(19873): at android.content.ContentResolver.query(ContentResolver.java:262)
05-04 09:45:54.554: E/AndroidRuntime(19873): at android.support.v4.content.CursorLoader.loadInBackground(CursorLoader.java:49)
05-04 09:45:54.554: E/AndroidRuntime(19873): at android.support.v4.content.CursorLoader.loadInBackground(CursorLoader.java:35)
05-04 09:45:54.554: E/AndroidRuntime(19873): at android.support.v4.content.AsyncTaskLoader.onLoadInBackground(AsyncTaskLoader.java:240)
05-04 09:45:54.554: E/AndroidRuntime(19873): at android.support.v4.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:51)
05-04 09:45:54.554: E/AndroidRuntime(19873): at android.support.v4.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:40)
05-04 09:45:54.554: E/AndroidRuntime(19873): at android.support.v4.content.ModernAsyncTask$2.call(ModernAsyncTask.java:123)
05-04 09:45:54.554: E/AndroidRuntime(19873): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
05-04 09:45:54.554: E/AndroidRuntime(19873): ... 4 more
05-04 09:45:54.578: D/dalvikvm(19873): GC_CONCURRENT freed 451K, 48% free 3109K/5959K, external 876K/1388K, paused 5ms+6ms