ContentProvider


ExportedContentProvider
检查是否未设置权限就共享了content provider
默认情况下content provider是共享的,任何应用程序都可以通过它来读写数据。如果content provider提供的是敏感数据,应该禁止共享或增加权限控制

        <provider
            android:name=".TestContentProvider"
            android:authorities="com.example.provider.student"
            tools:ignore="ExportedContentProvider"/>

 

这里介绍ContentProvider用法:

不多说,上代码:

package com.example.database;
/**
 *  DBHelper只负责创建数据库里面的表,它不负责创建数据库文件
 *  该类最好是个单例类
 */
public class StudentDBHelper extends SQLiteOpenHelper {
  
    public static final String STUDENT_DATABASE = "oma.db";
    public static final String AUTHORITY = "com.example.provider.student";    
    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/students");

    public static final String TABLE_STUDENT = "student";
    
    public static final String STUDENT_FIELD_ID = "id";
    public static final String STUDENT_FIELD_NAME = "name";
    public static final String STUDENT_FIELD_AGE = "age";
    public static final String STUDENT_FIELD_MODIFIEDDATE = "created";
    
    private static final int VERSION = 1;
    private static final String TABLE_STUDENT_CREATE = "CREATE TABLE "
            + TABLE_STUDENT + "(" + STUDENT_FIELD_ID
            + " INTEGER PRIMARY KEY NOT NULL," + STUDENT_FIELD_NAME + " TEXT,"
            + STUDENT_FIELD_AGE + " INTEGER, " + STUDENT_FIELD_MODIFIEDDATE + " TEXT)";
    private static final String TABLE_STUDENT_DROP = "DROP TABLE IF EXISTS "
            + TABLE_STUDENT;

    /**
     *  为了方便使用绝对路径,将context中的openOrCreateDatabase()重写
     *  SQLiteOpenHelper可能保存了这个context,保证每次调用自定义的方法
     *  openOrCreateDatabase()见openGrok
     */     
    public StudentDBHelper(Context context, CursorFactory factory,
            int version) {
        super(context, STUDENT_DATABASE, factory, version);
    }

    public StudentDBHelper(Context context, CursorFactory factory) {
        this(context, factory, VERSION);
    }

    public StudentDBHelper(Context context) {
        this(context, null);
    }

    /**
     * 这个函数在DBHelper对象创建的时候运行,但是此时数据库文件可能还没有创建
     */
    @Override
    public void onCreate(SQLiteDatabase sqliteDatabase) {
        sqliteDatabase.execSQL(TABLE_STUDENT_CREATE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqliteDatabase, int oldVersion,
            int newVersion) {
        if (oldVersion != newVersion) {
            sqliteDatabase.execSQL(TABLE_STUDENT_DROP);
            onCreate(sqliteDatabase);
        }
    }
}
package com.example.database;

public class StudentContentProvider extends ContentProvider {

    private static final UriMatcher sUriMatcher;

    private static final int STUDENTS = 1;
    private static final int STUDENT_ID = 2;

    private static StudentDBHelper mStudentDBHelper;

    static {
        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        sUriMatcher.addURI(StudentDBHelper.AUTHORITY, "students", STUDENTS);
        sUriMatcher.addURI(StudentDBHelper.AUTHORITY, "students/#", STUDENT_ID);
    }

    @Override
    public boolean onCreate() {
        mStudentDBHelper = new StudentDBHelper(getContext());
        return false;
    }

    @Override
    public int delete(Uri arg0, String arg1, String[] arg2) {
        return 0;
    }

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

    /**
     * 从实现上看,数据只能一条一条插入,如果想多条插入需要其它办法
     */
    @Override
    public Uri insert(Uri uri, ContentValues contentValues) {
        if (sUriMatcher.match(uri) != STUDENTS) {
            throw new IllegalArgumentException("Unknown URI: " + uri);
        }

        ContentValues cv;
        if (contentValues == null) {
            cv = new ContentValues();
        } else {
            cv = new ContentValues(contentValues);
        }

        Long now = Long.valueOf(System.currentTimeMillis());
        if (!cv.containsKey(StudentDBHelper.STUDENT_FIELD_MODIFIEDDATE)) {
            cv.put(StudentDBHelper.STUDENT_FIELD_MODIFIEDDATE, now);
        }

        SQLiteDatabase sqliteDatabase = mStudentDBHelper.getWritableDatabase();
        long rowId = sqliteDatabase.insert(StudentDBHelper.TABLE_STUDENT, null,
                cv);

        if (rowId > 0) {
            Uri studentUri = ContentUris.withAppendedId(
                    StudentDBHelper.CONTENT_URI, rowId);
            getContext().getContentResolver().notifyChange(studentUri, null);
            return studentUri;
        }

        throw new SQLException("Failed to insert row into " + uri);
    }

    @Override
    public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3,
            String arg4) {
        return null;
    }

    @Override
    public int update(Uri uri, ContentValues contentValues, String where,
            String[] whereArgs) {
        int count = 0;
        SQLiteDatabase sqliteDatabase = mStudentDBHelper.getWritableDatabase();

        switch (sUriMatcher.match(uri)) {
            //如果对同一张表操作,感觉分个类没啥意义,都用STUDENTS不就行了么?
            case STUDENTS:
                count = sqliteDatabase.update(StudentDBHelper.TABLE_STUDENT,
                        contentValues, where, whereArgs);
                break;
            case STUDENT_ID:
                // sqliteDatabase.execSQL("INSERT INTO master(key,value) values(\"321\",\"456\")");
                // 两种方式都可以插入数据库
                // sqliteDatabase.insert("master", null, contentValues);
                String id = uri.getPathSegments().get(1);
                count = sqliteDatabase.update(StudentDBHelper.TABLE_STUDENT,
                        contentValues, StudentDBHelper.STUDENT_FIELD_ID
                                + "="
                                + id
                                + (!TextUtils.isEmpty(where) ? " AND (" + where
                                        + ')' : ""), whereArgs);
                break;
            default:
                // 这是一个运行时异常,会将最上层调用的线程或进程结束
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
        
        getContext().getContentResolver().notifyChange(uri, null);
        return count;
    }
}
package com.example.test;

public class MainActivity extends Activity implements OnClickListener {

    private Button createDatabaseBtn;
    private Button insertDatabaseBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initialUI();
    }

    private void initialUI() {
        createDatabaseBtn = (Button) findViewById(R.id.createDatabase);
        createDatabaseBtn.setOnClickListener(this);

        insertDatabaseBtn = (Button) findViewById(R.id.insertDatabase);
        insertDatabaseBtn.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.createDatabase:
                onCreateDatabaseClick();
                break;
            case R.id.insertDatabase:
                onInsertClick();
                break;
            default:
                break;
        }
    }

    /**
     * onCreate()中mDataWorker被初始化就意味着数据库初始化,但并没有创建数据库文件,
     * 除非创建一个连接,此时才会检查数据库文件是否存在,如果不存在则创建该数据库文件
     */
    private void onCreateDatabaseClick() {
        // do nothing
    }

    private void onInsertClick() {
        ContentValues contentValues = new ContentValues();
        contentValues.put("id", "34");
        contentValues.put("name", "cm");
        contentValues.put("age", 12);

        try {
            getContentResolver().insert(StudentDBHelper.CONTENT_URI,
                    contentValues);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
//AndriodManifest.xml
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <provider
            android:name="com.example.database.StudentContentProvider"
            android:authorities="com.example.provider.student"
            tools:ignore="ExportedContentProvider" />

        <activity
            android:name="com.example.test.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

 

sqlite> .table
.table
android_metadata student
sqlite> select * from student;
select * from student;
34|cm|12|20074291300
sqlite>

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

StudentContentProvider类由系统启动时自动加载,一般只生成一个对应的实例。通过这个实例向用户提供数据库访问接口。
从而达到数据共享。
个人理解:ContentProvider需要使用时才起来,而且一个主进程,另外会开个子线程来处理上层的调用。调完返回结果ContentProvider就歇菜了。
posted @ 2015-03-15 15:42  牧 天  阅读(213)  评论(0)    收藏  举报