内容提供者无法删除 SQLite 中的行

content providers cannot delete row in SQLite

这是我的第一个内容提供商, 我被卡住了,因为我无法删除一行。由于我仍然无法使用 Junit 测试,因此我尝试设置内容提供者每次传递一条指令。 不明白为什么不行,谢谢

为了轻松关注热点:从 FragmentAsList class 在 onContextItemSelected 方法中调用删除,通过 contentResolver 转到提供程序 class 调用 Contracts 值并连接到数据库并返回一个 int

我还使用 CRUD 方法附加了数据库 class,请注意,在我从 ContentResolver 执行的删除调用中,有一个方法 getAllRows 连接到数据库 class 而不是调用由内容提供商提供,因此代码可能看起来很混乱,但我目前无法使用 JUNIT。

FragmentAsList

    package ivano.android.com.ucanote;

import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.text.format.Time;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

import ivano.android.com.ucanote.ivano.android.com.ucanote.db.Db;
import ivano.android.com.ucanote.ivano.android.com.ucanote.db.UcanContentProvider;
import ivano.android.com.ucanote.ivano.android.com.ucanote.db.UcanContract;


/**
* Created by ivano on 3/25/2015.
*/
public class FragmentAsList extends Fragment implements  View.OnCreateContextMenuListener {
    private SimpleCursorAdapter myCursorAdapter;
    private ArrayAdapter<String> mValues;
    //brought out from OnCreateView, have to be in all the class
    List<String> tasks = new ArrayList<String>();

    ListView listView;

    Time today = new Time(Time.getCurrentTimezone());
    Db myDb;
    EditText etTasks;


    public FragmentAsList() {
        super();
    }

public void populateView(){
    Cursor cursor = myDb.getallRows();
    String[] fromFieldNames = new String[]{UcanContract.Tasks._ID,};
    int[] toViewId = new int[]{R.id.text_view};


    //SimpleCursorAdapter myCursorAdapter;

    myCursorAdapter = new SimpleCursorAdapter(getActivity(), R.layout.textview_pretty_cool, cursor, fromFieldNames,
            toViewId, 0);


}
    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
// Add this line in order for this fragment to handle menu events.
        setHasOptionsMenu(true);





//open Database

        myDb = new Db(getActivity());
        myDb.open();


    }


    @Override
    public String toString() {
        return super.toString();
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.menu_fragmentaslist, menu);

    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        int id = item.getItemId();

               // item + " " + item.getItemId());
        switch (id) {
            case R.id.add:

                intent_comunicate.putExtra(getActivity().getPackageName(), "CIao Cipollino");
                startActivityForResult(intent_comunicate, 123);

                break;

        case R.id.delete_all:

            myDb.deleteAll();

            Cursor cursor = myDb.getallRows();
            String[] fromFieldNames = new String[]{UcanContract.Tasks.COLUMN_TASKS};
            int[] toViewId = new int[]{R.id.text_view};


            SimpleCursorAdapter myCursorAdapter;
            myCursorAdapter = new SimpleCursorAdapter(getActivity(), R.layout.textview_pretty_cool, cursor, fromFieldNames, toViewId, 0);
            listView.setAdapter(myCursorAdapter);

        }
        return super.onOptionsItemSelected(item);

    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 123) {
            String string = data.getExtras().getString(getActivity().getPackageName());
            Log.d("FragmentAsList", "stringa dall altra activity: " + string);
            Toast.makeText(getActivity(), string, Toast.LENGTH_LONG).show();
            ///database insert
            today.setToNow();
            String timeCurrent = today.format("%Y-%m-%d %H:%M:%S");
            myDb.insertRow(string, timeCurrent, null, null);
            //database query
            Cursor cursor = myDb.getallRows();
            String[] fromFieldNames = new String[]{UcanContract.Tasks.COLUMN_TASKS};
            int[] toViewId = new int[]{R.id.text_view};


            SimpleCursorAdapter myCursorAdapter;
            myCursorAdapter = new SimpleCursorAdapter(getActivity(), R.layout.textview_pretty_cool, cursor, fromFieldNames, toViewId, 0);
            listView.setAdapter(myCursorAdapter);



        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        View hiddenList = inflater.inflate(R.layout.fragment_as_list, container, false);

        // Get a reference to the ListView, and attach this adapter to it.
        listView = (ListView) hiddenList.findViewById(R.id.list_hidden);


        registerForContextMenu(listView);
        populateView();
        listView.setAdapter(myCursorAdapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {



//        Intent explicitIntent =new Intent(getActivity(),DetailNote.class);
//        startActivity(explicitIntent);
            }

        });


        return hiddenList;







    }
    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {

        super.onCreateContextMenu(menu, v, menuInfo);

        MenuInflater mi = getActivity().getMenuInflater();


        mi.inflate(R.menu.context_menu, menu);
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {


        switch (item.getItemId()) {
            case R.id.delete:
                //TODO delete
                registerForContextMenu(listView);
                AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
                int index = info.position;
                String indexToString=Integer.toString(index);
               long b=info.id;

                Uri uri = Uri.parse(UcanContentProvider.CONTENT_URI + "/"
                        + info.id);


                getActivity().getContentResolver().delete(uri, indexToString, null);


                //String indexString = Integer.toString(index);
                Log.d("ivano.android.com.ucanote.FragmentAsList", "onContextItemSelected (line 160): the position is " + index);

//                 myDb.deleteRow(b);
                //database query
                Cursor cursor = myDb.getallRows();
                String[] fromFieldNames = new String[]{UcanContract.Tasks.COLUMN_TASKS};
                int[] toViewId = new int[]{R.id.text_view};
                SimpleCursorAdapter myCursorAdapter;
                myCursorAdapter = new SimpleCursorAdapter(getActivity(), R.layout.textview_pretty_cool, cursor,
                        fromFieldNames, toViewId, 0);
//                SimpleCursorAdapter myCursorAdapter;
//                myCursorAdapter = new SimpleCursorAdapter(getActivity(), R.layout.textview_pretty_cool, cursor, fromFieldNames, toViewId, 0);
                listView.setAdapter(myCursorAdapter);
                break;
            case R.id.edit:
                break;
            case R.id.share:
                break;

        }

        return super.onContextItemSelected(item);
    }

}

内容提供商

package ivano.android.com.ucanote.ivano.android.com.ucanote.db;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;


public class UcanContentProvider extends ContentProvider {

    public Db.DatabaseHelper myDBHelper;
    public static final String AUTHORITY = "ivano.android.com.ucanote.db.UcanContentProvider";

    public static final String PATH_TASKS="tasks";



    public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + AUTHORITY);
    public static final Uri CONTENT_URI =BASE_CONTENT_URI.buildUpon().appendPath(PATH_TASKS).build();



    @Override
    public boolean onCreate() {
        //TODO not sure i need it see Android for busy programmers page 979, I have it already as constructor in Db.java that has the Helper
        //as inner class
        myDBHelper = new Db.DatabaseHelper(getContext());


        return false;
    }
//
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        //Using SQLiteQueryBuilder instead of query() method
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
        qb.setTables(UcanContract.Tasks.DATABASE_TABLE);

        Cursor cursor = qb.query(myDBHelper.getReadableDatabase(), projection, selection, selectionArgs, null, null, sortOrder);
        cursor.setNotificationUri(getContext().getContentResolver(), uri);


        return cursor;
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {

        int count = myDBHelper.getWritableDatabase().delete(UcanContract.Tasks.DATABASE_TABLE,
                UcanContract.Tasks._ID + " = " + selection, selectionArgs);
        getContext().getContentResolver().notifyChange(uri, null);

        return count;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }
}

合同class

package ivano.android.com.ucanote.ivano.android.com.ucanote.db;

import android.provider.BaseColumns;

public  class UcanContract{





public static final class  Tasks implements BaseColumns{


    public static final String COLUMN_TASKS = "task";
    public static final String COLUMN_DATE = "date";
    public static final String COLUMN_URGENCY = "urgency";
    public static final String COLUMN_TAG = "tag";




    public static final String[] ALL_ROWS=new String[]{_ID, COLUMN_TASKS, COLUMN_DATE, COLUMN_URGENCY, COLUMN_TAG};
 public static final String DATABASE_TABLE="table1";
}


}

最后是 DB

package ivano.android.com.ucanote.ivano.android.com.ucanote.db;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

/**
 * Created by ivano on 4/8/2015.
 */
public class Db {
    private static final String TAG="Db";
//TODO check if all the fields are used,
// expecially TAG, ALL KEYS and columns, and what is the function of DB info?

    //fields


    //DB info
    public static final String DATABASE_NAME = "db";

    //always remember to change this if I are going to update the DB!
    public static final int    DATABASE_VERSION=1;

    //create

    private static final String DATABASE_CREATE_SQL=
            "CREATE TABLE " + UcanContract.Tasks.DATABASE_TABLE
            + " ("  + UcanContract.Tasks._ID+ " INTEGER PRIMARY KEY AUTOINCREMENT, "
            + UcanContract.Tasks.COLUMN_TASKS + " TEXT NOT NULL, "
            + UcanContract.Tasks.COLUMN_DATE + " TEXT, "
            + UcanContract.Tasks.COLUMN_URGENCY +" TEXT, "
            + UcanContract.Tasks.COLUMN_TAG +" TEXT " +
                    " );";


private final Context context;
    public DatabaseHelper myDBHelper;
    private SQLiteDatabase db;

public Db (Context ctx) {
    this.context = ctx;
    myDBHelper=new DatabaseHelper(context);
    }

    public Db open() {
    db= myDBHelper.getWritableDatabase();
        return this;
    }

    public void close(){
        myDBHelper.close();
    }

    public long insertRow (String task,String date, String urgency, String tag) {
        ContentValues initialValues = new ContentValues();
        initialValues.put(UcanContract.Tasks.COLUMN_TASKS, task);
        initialValues.put(UcanContract.Tasks.COLUMN_DATE, date);
        initialValues.put(UcanContract.Tasks.COLUMN_URGENCY, urgency);
        initialValues.put(UcanContract.Tasks.COLUMN_TAG, tag);

    return db.insert(UcanContract.Tasks.DATABASE_TABLE,null,initialValues);
    }
        //delete a row from the db by _id
        public boolean deleteRow(long rowId) {
            String where = UcanContract.Tasks._ID + "=" + rowId;


              //db.delete(DATABASE_TABLE, where, null) ;
             return db.delete(UcanContract.Tasks.DATABASE_TABLE, where, null) != 0;
        }




    public void deleteAll(){
        Cursor c=getallRows();
        long rowId=c.getColumnIndex(UcanContract.Tasks._ID);
        if (c.moveToFirst()) {
            do {
                deleteRow(c.getLong((int) rowId));

            }
            while (c.moveToNext());
        }
            c.close();

    }
    public Cursor getallRows(){
        String where=null;
        Cursor c=db.query(true, UcanContract.Tasks.DATABASE_TABLE, UcanContract.Tasks.ALL_ROWS,where,null,null,null,null,null);
   if (c!= null){
c.moveToFirst();
    }
      return c;


    }
public Cursor getRow(long rowId){
    String where =UcanContract.Tasks._ID+"=" +rowId;
    Cursor c=db.query(true, UcanContract.Tasks.DATABASE_TABLE, UcanContract.Tasks.ALL_ROWS,where,null,null,null,null,null);
    if(c!= null){
        c.moveToFirst();
    }
    return c;

}
    public boolean updateRow(long rowId,String task,String date, String urgency,String tag){
        String where =UcanContract.Tasks._ID+"="+rowId;
        ContentValues newValues =new ContentValues();
        newValues.put(UcanContract.Tasks.COLUMN_TASKS,task);

        newValues.put(UcanContract.Tasks.COLUMN_DATE, date);
        newValues.put(UcanContract.Tasks.COLUMN_URGENCY, urgency);
        newValues.put(UcanContract.Tasks.COLUMN_TAG, tag);
        return db.update(UcanContract.Tasks.DATABASE_TABLE,newValues,where,null)!=0;

    }

    public static class DatabaseHelper extends SQLiteOpenHelper{
        DatabaseHelper(Context context){
            super (context,DATABASE_NAME,null,DATABASE_VERSION);

        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL(DATABASE_CREATE_SQL);

        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            Log.w(TAG,"Upgrading application's database from version"
                    +oldVersion+ " to "+newVersion+
                    " ,which will destroy all the old data!");
            db.execSQL("DROP TABLE IF EXISTS "+ UcanContract.Tasks.DATABASE_TABLE);
            onCreate(db);
        }
    }
}

您对 class ContentProvider 的实现至少在删除方法中是错误的。

您的密码是

@Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {

        int count = myDBHelper.getWritableDatabase().delete(UcanContract.Tasks.DATABASE_TABLE,
                UcanContract.Tasks._ID + " = " + selection, selectionArgs);
        getContext().getContentResolver().notifyChange(uri, null);

        return count;
    }

但参数选择不包含 table 的 rowID。 引用 ContentPovider api 文档

The implementation is responsible for parsing out a row ID at the end of the URI, if a specific row is being deleted. That is, the client would pass in content://contacts/people/22 and the implementation is responsible for parsing the record number (22) when creating a SQL statement.

所以你应该专注于解析参数URI 我建议你阅读 http://developer.android.com/guide/topics/providers/content-provider-basics.html#ContentURIs

特别是行

Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);