这里需要先说一下适配问题。在android11上面,要访问其他非系统应用的数据时需要申请一下
<uses-permission android:name= "android.permission.QUERY_ALL_PACKAGES"/>
这个权限,如果不申请这个权限的会报出链接不上数据库的异常,因为在android 11上面出现了软件可见性这样一个东西,详情可查看官网
https://developer.android.google.cn/about/versions/11/privacy/package-visibility这里是官网描述 。
也可使用queries 标签添加到androidmainfest文件里面,但是我自己尝试放入之后会出现编译不过的情况,暂时没有找到解决方案。
下面开始介绍如何在android 11上面共享数据:
首先需要继承一个sqlhelper的类
public class DatabaseHelper extends SQLiteOpenHelper {
public static final String DB_NAME = "switchstate";
private static final int DB_VERSION = 1;
public DatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS "+DB_NAME);
onCreate(db);
}
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "create table if not exists " + DB_NAME + " (id integer primary key autoincrement, name varchar(200),age varchar(200))";
db.execSQL(sql);
}
}
这里需要注意的是在创建表的时候需要注意好语句格式,有的时候一个不小心就很容易把格式弄错然后导致花费了很长时间来调试找错。
这里有一个建表格的格式可以方便用来校对是否在格式上有错误
"create table if not exists " + DB_NAME + " (id integer primary key autoincrement," + name + " varchar(200)," + age + " varchar(200))"
接下来就是创建contentprovider了,基础的增删改查。
//这里的AUTHORITY就是我们在AndroidManifest.xml中配置的authorities,这里的authorities可以随便写
private static final String AUTHORITY = "com.example.myapplication";
public static final Uri NOTIFY_URI = Uri.parse("content://" + AUTHORITY + "/switchstate");
//匹配成功后的匹配码
private static final int MATCH_ALL_CODE = 1;
private static final int MATCH_CODE = 2;
private ContentResolver mContentResolver;
private DatabaseHelper mSqlHelper;
private SQLiteDatabase mSqlDatabase;
private static final UriMatcher mUriMatcher;
//在静态代码块中添加要匹配的 Uri
static {
//匹配不成功返回NO_MATCH(-1)
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mUriMatcher.addURI(AUTHORITY, "/" + DatabaseHelper.DB_NAME, MATCH_ALL_CODE);
mUriMatcher.addURI(AUTHORITY, "/" + DatabaseHelper.DB_NAME + "/#", MATCH_CODE);
}
@Override
public boolean onCreate() {
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
mSqlHelper = new DatabaseHelper(getContext());
mSqlDatabase = mSqlHelper.getWritableDatabase();
mContentResolver = getContext().getContentResolver();
mContentResolver.registerContentObserver(NOTIFY_URI, true, new ContentObserver(handler) {
String state = null;
String mode = null;
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
Cursor cursor = query(uri, null, null, null, null);
cursor.moveToFirst();//这里是为了防止不是从第一个开始查询
state = cursor.getString(1);
mode = cursor.getString(2);
while (cursor.moveToNext()) {
state = cursor.getString(1);
mode = cursor.getString(2);
}
}
@Override
public boolean deliverSelfNotifications() {
return super.deliverSelfNotifications();
}
});
return true;
}
@SuppressLint("Recycle")
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
Cursor cursor = null;
cursor = mSqlDatabase.query(DatabaseHelper.DB_NAME, projection, selection, selectionArgs, null, sortOrder, null);
return cursor;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
mSqlDatabase.insert(DatabaseHelper.DB_NAME, null, values);
//通知ContentObserver数据发生变化了
notifyDataChanged(uri);
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
mSqlDatabase.delete(DatabaseHelper.DB_NAME, selection, selectionArgs);
notifyDataChanged(uri);
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
mSqlDatabase.update(DatabaseHelper.DB_NAME, values, selection, selectionArgs);
notifyDataChanged(uri);
return 0;
}
//通知指定URI数据已改变
private void notifyDataChanged(Uri uri) {
mContentResolver.notifyChange(uri, null);
}
然后在manifest文件里面添加相应的注册信息就好了
<provider
android:name="com.example.myapplication.SwitchStateProvider"
android:authorities="com.example.myapplication"
android:enabled="true"
android:exported="true"
android:grantUriPermissions="true"/>
注册信息的这些属性含义如下:
exported:是否允许其他应用访问
grantUriPermissions:我记得是可以通过uri形式来进行链接找不到官方解释了,惭愧。
enabled:Android系统是否能够实例化该应用程序的组件,如果为true,每个组件的enabled属性决定那个组件是否可以被 enabled。如果为false,它覆盖组件指定的值。
name:填写你的provider路径
authorities:填写provider的包名路径
这样就准备好被访问应用的provider了。
接下来就要书写如何访问该应用的代码了,代码如下:
contentResolver = getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put("aaa", "123445");
contentValues.put("openstate", "123445");
Uri insertUri = contentResolver.insert(URI, contentValues);
contentResolver.registerContentObserver(URI, true, new MyContentObserver(null));
Cursor query1 = contentResolver.query(URI, null, null, null, null);
没有什么地方需要特别注意的东西,正常访问就好。如果需要监听数据的改动,就增加一个contentobserver 就好了。用法的话, new一个或者集成一个contentobserver,然后主要是实现onChange方法就好了,在数据有变动被通知的时候都会走这个方法。在这个方法里面去添加自己想要实现的逻辑就好了。这样就完成用contentprovider来共享数据了。
在写ContentObserver的时候发现如果数据库执行相应操作之后不执行notifyChange的话, observer就监听不到修改。暂时不知道原因,在写的时候需要注意一下。