Android安卓四大组件之内容提供者

Posted woodwhale

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android安卓四大组件之内容提供者相关的知识,希望对你有一定的参考价值。

android】安卓四大组件之内容提供者

1、关于内容提供者

1.1 什么是内容提供者

内容提供者就是contentProvider,作用有如下:

  1. 给多个应用提供数据
  2. 类似一个接口
  3. 可以和多个应用分享数据

1.2 为什么要有内容提供者

作为一个APP,自己的数据会在某些条件下提供给其他APP,但是,APP的数据是私有的。

例如,APP A的数据库内容是不可以被APP B进行读取的

这个时候,我们就需要一个内容提供者,将APP A中的数据信息提供给APP B。

1.3 使用场景

就贴近生活一些吧,拿某宝、拼夕夕等购物软件举例子,下面几种场景你肯定见过:

  • 获取通讯录中的联系人,申请好友。
  • 获取其他软件搜索记录,大数据计算,进行产品推送。
  • 预约直播,将预约信息写入手机备忘录

2、如何自定义内容提供者

2.1 写一个提供内容的APP

首先,在我们提供内容的APP中的manifest中,写入provider:

  • authorities可以是包名
  • name就是自己定义的名字
  • exported=true可以让其他的APP来访问自己提供的内容
<provider
            android:authorities="top.woodwhale.picgo"
            android:name=".test.contentprovider.provider.UserProvider"
            android:exported="true"
            android:enabled="true"
            android:grantUriPermissions="true"/>

其次,我们操作的这个提供内容的APP,得有初始化后的数据库

  • 关于数据库,提几句:
  • 相关的内容,在之前的SQLite学习章节中说过了
  • 在一个项目中,该有的架构还是有的,如下图
  • 其中dao是专门执行数据库操作的,有相关接口和实现类
  • db文件夹是存放数据库helper的,它的作用就是初始化数据库,并且可以返回数据操作对象
  • pojo就是从数据中转为对象的类
  • provider就是我们要写的内容提供者的类
  • utils中就是常用的工具类

提完了数据库,我们继续说内容提供者

一个具有内容提供者的APP中必须得有如下的类:

  • 该类继承ContentProvider,并且重写其中的方法(增删改查)
  • 赋予一个UriMatcher对象的成员变量
  • 进行一个Uri的匹配,authorities要和manifest中的一致,并且可以选择表进行内容共提供。这些都在静态代码块中实现,使用addURI方法即可
  • 重写增删改查方法,前提是Uri匹配!
public class UserProvider extends ContentProvider 

    private static final String TAG = "UserProvider";
    private UserDatabaseHelper dbh;
    private static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    private static final int USER_MATCH_CODE = 1;
    static 
        uriMatcher.addURI("top.woodwhale.picgo","user",USER_MATCH_CODE);
    

    @Override
    public boolean onCreate() 
        dbh = new UserDatabaseHelper(getContext());
        return false;
    

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) 
        int res = uriMatcher.match(uri);
        // 匹配规则
        if (res == USER_MATCH_CODE) 
            SQLiteDatabase db = dbh.getWritableDatabase();
            return db.query(Constants.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
         else 
            throw new IllegalArgumentException("参数错误!");
        
    

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) 
        return null;
    

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) 
        int res = uriMatcher.match(uri);
        if (res == USER_MATCH_CODE) 
            SQLiteDatabase db = dbh.getWritableDatabase();
            long insertRes = db.insert(Constants.TABLE_NAME, null, values);
            Uri resUri = Uri.parse("content://top.woodwhale.picgo/user/"+insertRes);
            Log.d(TAG,"insertRes --> "+ insertRes);
            // 插入数据成功,数据变化了,需要通知其他地方
            getContext().getContentResolver().notifyChange(resUri,null);
            return resUri;
         else 
            throw new IllegalArgumentException("参数错误!");
        
    

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) 
        return 0;
    

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

2.2 使用其他的APP来调用上述内容

我们写其他的一个APP来调用上述的内容:

首先是查询:

  • 注意Uri.parse(“content://top.woodwhale.picgo/user”)是我们在上面APP中写的:
    • top.woodwhale.picgoauthorities
    • user是表名
    • 对应上面的uriMatcher.addURI("top.woodwhale.picgo","user",USER_MATCH_CODE)
/**
     * 测试通过内容提供者,获取picgo项目中搞得数据库内容
     * @param view this activity
     */
public void getContent(View view) 
    ContentResolver contentResolver = this.getContentResolver();
    Uri uri = Uri.parse("content://top.woodwhale.picgo/user");
    @SuppressLint("Recycle") Cursor cursor = contentResolver.query(uri, null, null, null, null);
    String[] columnNames = cursor.getColumnNames();
    Log.d(TAG, "columnNames.length --> "+String.valueOf(columnNames.length));
    Log.d(TAG,"=================================");
    while (cursor.moveToNext()) 
        for (String columnName : columnNames) 
            @SuppressLint("Range") String cursorString = cursor.getString(cursor.getColumnIndex(columnName));
            Log.d(TAG,"cursorString --> " + cursorString);
        
    
    Log.d(TAG,"=================================");

点击测试:

然后是插入:

还是非常简单的:

/**
     * 添加数据
     * @param view this activity
     */
public void insertContent(View view) 
    ContentResolver contentResolver = this.getContentResolver();
    Uri uri = Uri.parse("content://top.woodwhale.picgo/user");
    ContentValues values = new ContentValues();
    values.put(Constants.FIELD_USERNAME,"wyh");
    values.put(Constants.FIELD_PASSWORD,"114514");
    values.put(Constants.FIELD_AGE,3);
    values.put(Constants.FIELD_SEX,"男");
    contentResolver.insert(uri,values);

我们可以在onCreate的时候就注册一个内容观察者,当我们内容提供者的数据发生改变的时候,就可以监听到,也就是,我们插入成功就可以监听到

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

    Uri uri = Uri.parse("content://top.woodwhale.picgo/user");
    ContentResolver contentResolver = getContentResolver();
    contentResolver.registerContentObserver(uri, true, new ContentObserver(new Handler()) 
        @Override
        public void onChange(boolean selfChange) 
            super.onChange(selfChange);
            Log.d(TAG,"用户数据发生变化");
        
    );

注册成功之后,我们调用插入方法,可以发现已经被监听了,并且我们查询数据库,确实添加了如上的信息

2.3 内容提供者的小结

其实使用内容提供者非常的简单和便捷,但是又有多少APP敢将自身的数据提供给他人呢?

所以自己写的APP中的内容提供者少之又少,基本上都是同一厂商敢和自家APP联动剽取用户的信息共享。

但是在安卓手机中,有很多自带的APP,他们都具有内容提供者的对应接口,用来让常用的APP进行内容的增删改查,下面我们来进行常见内容提供者的学习!

3、使用“日历”内容提供者

在很多的情况下,我们会将一些事情写入到我们手机中的“日历”中,当到了预定的时间就会提醒,那么设置一个日历提醒事件怎么做到呢?——我们可以使用安卓开发给定的CalendarContract进行完成

CalendarContract是日历内容提供者和APP之间的一个合同,当我们的APP获取了读、写日历的权限之后,就可以对手机自带的这个"日历APP"进行添加事件的操作,我们通过下面的代码来认识一下!

3.1 获取日历权限

在安卓6.0,也就是SDK>=23的版本后,我们的APP权限需要动态申请,首先在manifest中申请日历的读写权限

<uses-permission android:name="android.permission.READ_CALENDAR"/>
<uses-permission android:name="android.permission.WRITE_CALENDAR"/>

然后我们在activity中写一个方法来动态的获取权限

// 成员变量
private static final int PERMISSION_REQUEST_CODE = 1;

private void initPermission() 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
        String[] reqPermissions = new String[]Manifest.permission.READ_CALENDAR,Manifest.permission.WRITE_CALENDAR;
        for (String reqPermission : reqPermissions) 
            if (checkSelfPermission(reqPermission) != PackageManager.PERMISSION_GRANTED) 
                // 如果有没有授权的,就去提醒授权
                requestPermissions(reqPermissions,PERMISSION_REQUEST_CODE);
                break;
            
        
    

同时我们还可以重写一个回调方法onRequestPermissionsResult,也就是权限获取结果的回调,如果拒绝了我们的权限申请,那么久finish()当前页面

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) 
    if (requestCode == PERMISSION_REQUEST_CODE) 
        for (int grantResult : grantResults) 
            if (grantResult != PackageManager.PERMISSION_GRANTED) 
                Log.d(TAG,"somePermissionsWereNotGranted");
                finish();
                break;
            
        
        Log.d(TAG,"allPermissionsHaveBeenGiven...");
        Toast.makeText(this, "allPermissionsHaveBeenGiven...", Toast.LENGTH_SHORT).show();
    

3.2 有关日历的各种class和属性

下面我们正式开始,在开始之前,我们来看看有关日历的class的各种作用:

3.3 获取一个日历用户

在获取完权限之后,我们可以获取一个日历用户的ID(前提是,日历程序中有这个用户)

我们调用contentResolver的query()方法,获得一个cursor,再查询其中的CalendarContract.Calendars._ID,这个id就是我们的用户ID

@SuppressLint("Range")
private int getCalendarID() 
    Log.d(TAG,"getCalendarUserId...");
    ContentResolver contentResolver = this.getContentResolver();
    Uri uri = CalendarContract.Calendars.CONTENT_URI;
    Cursor cursor = contentResolver.query(uri, null, null, null, null, null);
    cursor.moveToFirst();
    int id = cursor.getInt(cursor.getColumnIndex(CalendarContract.Calendars._ID));
    Log.d(TAG,"anInt --> " + id);
    cursor.close();
    return id;

3.4 将内容写入日历

最后一步就是写入日历内容

  • 我们通过ContentValues对象的put方法,将我们的键值对写入其中

  • 有什么常量可以写入呢?

  • 写入规则

我们在写入成功之后,得到的Uri可以进行一个intent隐式意图的跳转,直接查看我们写入的事件

@RequiresApi(api = Build.VERSION_CODES.N)
public void writeCalendarEvent(View view) 
    long calID = getCalendarID();
    if (calID == -1) 
        // 如果没有账户,那么就终止这个方法
        return;
    
    // 设置开始时间,注意 month、date 从 0 开始
    Calendar beginTime = Calendar.getInstance();
    beginTime.set(2022,0,30,0,0);
    long beginTimeTimeInMillis = beginTime.getTimeInMillis();
    // 设置结束时间
    Calendar endTime = Calendar.getInstance();
    endTime.set(2022,0,30,23,59);
    long endTimeTimeInMillis = endTime.getTimeInMillis();
    // 设置内容values
    String timeZone = TimeZone.getDefault().getID();
    ContentValues values = new ContentValues();
    values.put(CalendarContract.Events.DTSTART,beginTimeTimeInMillis);
    values.put(CalendarContract.Events.DTEND,endTimeTimeInMillis);
    values.put(CalendarContract.Events.CALENDAR_ID, calID);
    values.put(CalendarContract.Events.EVENT_TIMEZONE,timeZone);
    values.put(CalendarContract.Events.TITLE,"准备过年!");
    values.put(CalendarContract.Events.DESCRIPTION,"冲就完了!");
    values.put(CalendarContract.Events.EVENT_LOCATION,"九江");
    // 插入数据
    Uri uri = CalendarContract.Events.CONTENT_URI;
    ContentResolver contentResolver = getContentResolver();
    Uri res = contentResolver.insert(uri, values);
    Log.d(TAG,"uriRes --> " + res);
    gotoCalendar(res);


private void gotoCalendar(Uri res) 
    Intent intent = new Intent(Intent.ACTION_VIEW)
        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        .setData(res);
    startActivity(intent);

3.5 设置日历事件提醒

在上面完成了之后,我们发现其实并没有开启提示模式,也就是说,到了当前并没有闹钟或者信息的通知

我们可以通过CalendarContract.Reminders来设置提醒

需要注意需要如下的常量value设置:

@RequiresApi(api = Build.VERSION_CODES.N)
public void writeCalendarEvent(View view) 
    long calID = getCalendarID();
    if (calID == -1) 
        // 如果没有账户,那么就终止这个方法
        return;
    
    // 设置开始时间,注意 month、date 从 0 开始
    Calendar beginTime = Calendar.getInstance();
    beginTime.set(2022,0,30,0,0);
    long beginTimeTimeInMillis = beginTime.getTimeInMillis();
    // 设置结束时间
    Calendar endTime = Calendar.getInstance();
    endTime.set(2022,0,30,23,59);
    long endTimeTimeInMillis = endTime.getTimeInMillis();
    // 设置内容values
    String timeZone = TimeZone.getDefault().getID();
    ContentValues eventValues = new ContentValues();
    eventValues.put(CalendarContract.Events.DTSTART,beginTimeTimeInMillis);
    eventValues.put(CalendarContract.Events.DTEND,endTimeTimeInMillis);
    eventValues.put(CalendarContract.Events.CALENDAR_ID, calID);
    eventValues.put(

以上是关于Android安卓四大组件之内容提供者的主要内容,如果未能解决你的问题,请参考以下文章

Android安卓四大组件之Activity

Android安卓四大组件之Activity

Android安卓四大组件之Activity

Android安卓四大组件之Activity

Android安卓四大组件之Service

Android安卓四大组件之Activity