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就歇菜了。

浙公网安备 33010602011771号