内容提供者的使用
1. 内容提供者的使用
1) ContentResolver 类似于Socket的客户端部分
2) 需要提供 Uri 对象,可以看作网址 , Android 内部提供了很多常量类来访问
3) 访问Android内部的提供者,需要声明权限
4) 在Android API 23 以上,所有的内部的提供者都需要动态权限检查,否则执行不了
5) 总结:只要使用ContentResolver就可以直接调用 ContentProvider的内容
2. 联系人获取
1) 联系人使用 ContactsContract 常量类来获取地址和列字段
2) 使用SimpleCursorAdapter 时,最后一个参数是 代表Adapter可以自动
收到联系人的变化,当联系人发生任何变化的时候,能够自动刷新ListView
3) ContentProvider + Loader 就会自动刷新数据:仅限于Android自带的内容提供者
4) 获取联系人详情,需要使用 Data表,通过联系人的ID,来查找指定的信息
3. Adapter 支持Button 点击事件
1. getView(...)
复用:
CursorAdapter 没有复用,调用 newView() 加载布局 Button
Button.setOnClickListener(listener)
ViewHolder:
bindView() 直接设置内容
2. Adapter 内部需要一个成员变量,就是 OnClickListener的类型,只要Adapter增加一个set方法就好了
3. Activity 调用 Adapter 的setOnClickListener(l) -> Adapter -> Button
通过清单文件添加权限
<!-- 获取通话记录的权限 -->
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ContentResolver resolver = this.getContentResolver();
// 动态权限检查
if (Build.VERSION.SDK_INT >= 16) {
// state 代表是否允许
int state = ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG);
if (state == PackageManager.PERMISSION_GRANTED) {
// 允许的情况
// 所有常量类中的 CONTENT_URI 就可以用于内容提供者的访问
getCallLogs();
} else {
// 需要申请, 会回调当前Activity的方法
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.READ_CALL_LOG},
998
);
}
}
}
@Override
public void onRequestPermissionsResult(
int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode == 998) {
// 参数2 每一个元素代表一个权限,对应参数3中的权限设置,是否允许
if (permissions[0].equals(Manifest.permission.READ_CALL_LOG)) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
getCallLogs();
}
}
}
}
private void getCallLogs() {
int state = ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG);
// TODO: 获取通话记录
ContentResolver resolver = getContentResolver();
// 所有常量类中的 CONTENT_URI 就可以用于内容提供者的访问
Cursor cursor = resolver.query(
CallLog.Calls.CONTENT_URI,
null, // 需要返回的列
null, // where 语句
null, // where 条件参数
null // orderby
);
if (cursor != null) {
while (cursor.moveToNext()) {
int index = cursor.getColumnIndex(CallLog.Calls.NUMBER);
if (index != -1) {
String number = cursor.getString(index);
Log.d("MainActivity", "number = " + number);
}
}
cursor.close();
}
}
}
自定义的内容提供者
自定义的内容提供者
1. 继承 ContentProvider
2. 实现 增 删 改 查 方法 以及 onCreate 方法
3. 制定 Uri 的网址规则,形成特定的功能
4. 四大组件都需要在清单文件注册,来确认内容提供者是否可以被 其他程序访问
5. 内部需要进行数据的操作,增 删 改 查
5.1 确认使用哪种数据类型:SQLite, File, SharedPreferences
5.2 onCreate 方法,需要初始化 通常 DbHelper
5.3 每一个操作的方法,第一个参数都代表了 需要操作的哪一张表。通过Uri来标示;
5.4 使用 UriMatcher 类对象,来进行网址匹配,判断访问那个表
关于 Uri 与 authorities 说明
Uri 就是一个网址
规则: content://<authority>/path
authority 代表的就是 Android 系统中内容提供者的唯一标示,就像服务器的IP地址
清单文件里面声明内容提供者的authority
http://www.baidu.com/images
http://www.baidu.com/news
MainActivity.java
public class DbHelper extends SQLiteOpenHelper {
private static final String CREATE_TABLE_COST =
"create table cost(" +
"_id integer primary key autoincrement," +
"money double not null default 0" +
")";
public DbHelper(Context context) {
super(context, "myapp", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_COST);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
public class MoneyProvider extends ContentProvider {
private static final UriMatcher uriMatcher;
public static final int CODE_COST = 1;
public static final int CODE_INCOME = 2;
static{
uriMatcher = new UriMatcher(0);
// 添加 Uri的匹配规则,实现 uri 对应判断是哪个表的操作
uriMatcher.addURI("*", "/cost", CODE_COST);
uriMatcher.addURI("*", "/income", CODE_INCOME);
}
private DbHelper mDbHelper;
public MoneyProvider() {
}
@Override
public boolean onCreate() {
// 数据库的初始化
mDbHelper = new DbHelper(getContext());
// 必须返回true
return true;
}
/**
* 添加方法,对外提供接口,可以让其他应用程序向当前程序的数据库添加数据内容
* @param uri
* @param values
* @return
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
Uri ret = null;
// content://xxxxx/cost 支出表
// content://xxxxx/income 收入表
int code = uriMatcher.match(uri);
// 所有的数据库打开操作,都应该在 增删改查中完成,不允许在onCreate中。
SQLiteDatabase database = mDbHelper.getWritableDatabase();
switch (code) {
case CODE_COST:
long id = database.insert("cost", null, values);
// 数据库添加完成之后返回的 ID,必须和 Uri参数拼接在一起,再返回
ret = ContentUris.withAppendedId(uri, id);
break;
case CODE_INCOME:
break;
}
database.close();
return ret;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int ret = 0;
// content://xxxxx/cost 支出表
// content://xxxxx/income 收入表
int code = uriMatcher.match(uri);
// 所有的数据库打开操作,都应该在 增删改查中完成,不允许在onCreate中。
SQLiteDatabase database = mDbHelper.getWritableDatabase();
switch (code) {
case CODE_COST:
ret = database.delete("cost", selection, selectionArgs);
break;
case CODE_INCOME:
break;
}
database.close();
return ret;
}
@Override
public String getType(Uri uri) {
// TODO: Implement this to handle requests for the MIME type of the data
// at the given URI.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Cursor ret = null;
SQLiteDatabase database = mDbHelper.getReadableDatabase();
int code = uriMatcher.match(uri);
switch (code) {
case CODE_COST:
ret = database.query("cost", projection, selection, selectionArgs, null, null, sortOrder);
break;
case CODE_INCOME:
break;
}
return ret;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO: Implement this to handle requests to update one or more rows.
throw new UnsupportedOperationException("Not yet implemented");
}
}