安卓基础学习 Day15 |内容提供者

目录

  • 写在前面的话
  • 一、内容提供者
    • (一)概述
    • (二)测试
  • 二、内容观察者
    • (一)概述
    • (二)测试
  • 三、补充

写在前面的话

1、参考自:https://b23.tv/0VmCjN
2、内容如有不对的,希望可以指出或补充。
3、新知识。

一、内容提供者

(一)概述

内容提供者(ContentProvider):是Android系统四大组件之一,它是不同应用程序之间进行数据共享的标准API,通过内容解析者(ContentResolver)类可以访问内容提供者中共享的数据。

内容解析者(ContentResolver):提供一系列增删改查的方法对数据进行操作,并且这些方法以Uri的形式对外提供数据。

统一资源标志符(Uniform Resource Identifier,URI)):为内容提供者中的数据建立了唯一标识符。主要由:scheme(命名机制;这部分(内容提供者)固定为content://)、authorities(存资源的主机名;通常采取程序包名的方式来命令)和path(资源名称;代表资源或数据,可以动态改变)三部分组成。如:content://cn.luck.mycontentprovider/test

(二)测试

大概步骤(通过内容解析者访问内容提供者):获取相应操作的Uri→获取ContentResolver对象→通过ContentResolver对象查询数据

内容提供者的创建:同广播与服务的创建类似(在程序包名上右击选择【New】→【Other】→【Content Provider】→在弹出的窗口中输入类名称或默认,再填写好URI Authorities即可);若采用自行创建Java类继承ContentProvider类的方式创建服务,则需要手动在【项目清单文件】中进行注册。

例子(通过内容解析者访问内容提供者)-查看手机上的短信。

存短信数据的数据库:

找到如下位置的(Android Studio界面右下角)点击打开,在当前正在运行的设备信息中,找到 data→data→ com.android.providers.telephony 包→databases→mmssms.db文件 就是当前设备系统存短信数据的数据库文件。
安卓基础学习 Day15 |内容提供者_第1张图片
右击mmssms.db选择Save As…进行保存,通过navicat打开,文件内容如下。安卓基础学习 Day15 |内容提供者_第2张图片
1、布局

activity_main.xml


<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical"
    android:background="@mipmap/bg">

    
    <ImageButton
        android:id="@+id/btn"

        android:onClick="readSMS"
        tools:ignore="OnClick"

        android:layout_width="80dp"
        android:layout_height="50dp"
        android:background="@mipmap/btn_bg"
        android:layout_alignParentTop="true"
        android:layout_centerInParent="true"
        android:layout_marginTop="100dp" />
    
    <TextView
        android:id="@+id/tv_title"

        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/btn"
        android:layout_marginTop="30dp"
        android:paddingLeft="60dp"
        android:textSize="15sp"
        android:text="读取到的短信内容:"
        android:visibility="invisible"
        android:textColor="@color/black"/>
    <TextView
        android:id="@+id/tv_text"

        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/tv_title"
        android:paddingLeft="60dp"
        android:textSize="15sp"/>
RelativeLayout>

2、代码

MainActivity.java

package com.example.testcontentprovider;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
     
    private TextView tvTitle, tvText;
    private String content;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
     
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //获取控件
        tvTitle = findViewById(R.id.tv_title);
        tvText = findViewById(R.id.tv_text);
    }
    //按钮事件
    public void readSMS(View view){
     
        //查询系统信息的uri
        // Uri.parse()方法是将字符串转换成Uri对象
        //sms为系统短信的uri中的authorities地址
        Uri uri = Uri.parse("content://sms/");
        //获取到内容解析者对象
        ContentResolver resolver = getContentResolver();
        //查询数据
        Cursor cursor = resolver.query(uri, new String[]{
     "_id","address","type","body","date"},
                null,null,null);

        //创建一个集合遍历cursor(游标
        List<SmsInfo> smsInfos = new ArrayList<>();
        //判断cursor不为空且查出的内容大于0条数据
        if(cursor !=null && cursor.getCount()>0) {
     
            tvTitle.setVisibility(View.VISIBLE);//显示为可见
            //从cursor获取到相应内容
            while (cursor.moveToNext()){
     
                int _id = cursor.getInt(0);
                String address = cursor.getString(1);
                int type = cursor.getInt(2);
                String body = cursor.getString(3);
                long date = cursor.getLong(4);
                //创建SmsInfo对象
                SmsInfo smsInfo = new SmsInfo(_id,address,type,body,date);
                smsInfos.add(smsInfo);//添加到集合中
            }
            cursor.close();
        }
        //将查询到的内容显示到界面上
        for(int i = 0; i < smsInfos.size(); i++){
     
            content = "手机号码:"+smsInfos.get(i).getAddress()+"\n";
            content += "短信内容:"+smsInfos.get(i).getBody()+"\n";
            tvText.setText(content);
        }
    }
}

SmsInfo.java

package com.example.testcontentprovider;

//相当于短信信息的接口
public class SmsInfo {
     
    private int _id;//短信主键
    private String address;//发送地址
    private int type;//类型
    private String body;//短信内容
    private long date;//时间

    public SmsInfo(int _id, String address, int type, String body, long date) {
     
        this._id = _id;
        this.address = address;
        this.type = type;
        this.body = body;
        this.date = date;
    }

    public int get_id() {
     
        return _id;
    }

    public void set_id(int _id) {
     
        this._id = _id;
    }

    public String getAddress() {
     
        return address;
    }

    public void setAddress(String address) {
     
        this.address = address;
    }

    public int getType() {
     
        return type;
    }

    public void setType(int type) {
     
        this.type = type;
    }

    public String getBody() {
     
        return body;
    }

    public void setBody(String body) {
     
        this.body = body;
    }

    public long getDate() {
     
        return date;
    }

    public void setDate(long date) {
     
        this.date = date;
    }
}

3、效果

设置权限声明:
安卓基础学习 Day15 |内容提供者_第3张图片
运行效果如下:
安卓基础学习 Day15 |内容提供者_第4张图片

二、内容观察者

Day16内容。
新知识。

(一)概述

内容观察者(ContentObserver):用来观察指定Uri所代表的数据的。当内容观察者观察到指定Uri代表的数据发生变化时,就会触发onChange()方法,在该方法中使用内容观察者可以查询到变化的数据。

使用内容观察者(观察数据变化)的前提:必须在内容提供者(ContentProvider)的delete()、insert()、update()方法中调用内容解析者(ContentResolver)的notifyChange()方法。

(二)测试

ContentObserver的两个常用方法:

① public ContentObserver(Handler handler)

ContentObserver的派生类(子类)都需要调用这个构造方法。参数可以为线程(Handler),也可以为任何Handler(Android系统中线程间传递消息的一种机制)对象。

② public void onChange(boolean selfChange)

当观察的Uri代表的数据发生改变时,会触发这个方法。在这个方法中使用ContentResolver可以查询到变化的数据。

使用内容观察者来观察数据的大致步骤:

创建一个内容解析者→通过内容解析者调用registerContentObserver()这个方法,把某一个内容观察者注册到一个uri上

例子-检测数据库的变化

1、布局
安卓基础学习 Day15 |内容提供者_第5张图片
2、代码

SQLiteOpenHelper
安卓基础学习 Day15 |内容提供者_第6张图片
PersonProvider.java
-内容提供者

package com.example.testcontentobserver;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;

public class PersonProvider extends ContentProvider {
     
    public PersonProvider() {
     
    }

    private PersonDBOpenHelper helper;//成员变量

    //定义uri路径匹配器
    //路径匹配不成功返回-1
    private static UriMatcher uriMatcher = new UriMatcher(-1);
    //匹配成功返回1
    private static final int SUCCESS = 1;
    //静态代码块
    //添加具体的路径匹配规则
    static {
     
        uriMatcher.addURI("cn.luck.contentobserverdb","info",SUCCESS);
    }

    @Override
    //删除数据的操作
    public int delete(Uri uri, String selection, String[] selectionArgs) {
     
        // Implement this to handle requests to delete one or more rows.

        int code = uriMatcher.match(uri);//匹配查询的uri路径
        if (code == SUCCESS) {
     
            SQLiteDatabase db = helper.getReadableDatabase();//拿到数据库

            int count = db.delete("info",selection,selectionArgs);//删除了几行
            if(count > 0){
     //删除成功,数据发生改变
                //提示数据库数据发生改变了
                getContext().getContentResolver().notifyChange(uri,null);
            }
            db.close();
            return count;
        }else{
     
            //抛出非法参数不正常异常
            throw new IllegalArgumentException("路径错误,不删除数据");
        }
    }

    @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 Uri insert(Uri uri, ContentValues values) {
     
        // TODO: Implement this to handle requests to insert a new row.

        int code = uriMatcher.match(uri);//匹配查询的uri路径
        if (code == SUCCESS) {
     
            SQLiteDatabase db = helper.getReadableDatabase();//拿到数据库

            long rowId = db.insert("info",null,values);//插入到哪一行
            if(rowId > 0){
     //插入成功
                //获取到插入到对应行的Uri
                Uri insertedUri = ContentUris.withAppendedId(uri,rowId);

                //提示数据库数据发生改变了
                getContext().getContentResolver().notifyChange(insertedUri,null);

                return insertedUri;
            }
            db.close();
            return null;//插入失败
        }else{
     
            //抛出非法参数不正常异常
            throw new IllegalArgumentException("路径错误,不添加数据");
        }
    }

    @Override
    public boolean onCreate() {
     
        // TODO: Implement this to initialize your content provider on startup.
        helper = new PersonDBOpenHelper(getContext());
        return false;
    }

    @Override
    //查询数据的操作
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
     
        // TODO: Implement this to handle query requests from clients.

        int code = uriMatcher.match(uri);//匹配查询的uri路径
        if (code == SUCCESS) {
     
            SQLiteDatabase db = helper.getReadableDatabase();//拿到数据库
            //查询info表
            return db.query("info",projection,selection,selectionArgs,
                    null,null,sortOrder);
        }else{
     
            //抛出非法参数不正常异常
            throw new IllegalArgumentException("路径错误,不提供数据");
        }
    }

    @Override
    //更新数据的操作
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
     
        // TODO: Implement this to handle requests to update one or more rows.

        int code = uriMatcher.match(uri);//匹配查询的uri路径
        if (code == SUCCESS) {
     
            SQLiteDatabase db = helper.getReadableDatabase();//拿到数据库

            int count = db.update("info",values,selection,selectionArgs);//影响了几行
            if(count > 0){
     
                //提示数据库数据发生改变了
                getContext().getContentResolver().notifyChange(uri,null);
            }
            db.close();
            return count;
        }else{
     
            //抛出非法参数不正常异常
            throw new IllegalArgumentException("路径错误,不更新数据");
        }
    }
}

MainActivity.java

package com.example.testcontentobserver;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
     

    private Button addBtn,updateBtn,deleteBtn,selectBtn;
    private ContentResolver contentResolver;

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

        initView();//初始化界面
        createDB();//创建数据库
    }

    private void initView() {
     
        //获取到界面控件
        addBtn = findViewById(R.id.add_btn);
        updateBtn = findViewById(R.id.update_btn);
        deleteBtn = findViewById(R.id.delete_btn);
        selectBtn = findViewById(R.id.select_btn);
        //绑定监听器
        addBtn.setOnClickListener(this);
        updateBtn.setOnClickListener(this);
        deleteBtn.setOnClickListener(this);
        selectBtn.setOnClickListener(this);
    }

    private void createDB() {
     
        PersonDBOpenHelper helper = new PersonDBOpenHelper(this);
        SQLiteDatabase db = helper.getWritableDatabase();
        for (int i = 0; i < 4; i++) {
     
            ContentValues values = new ContentValues();
            values.put("name","test"+i);//这样可循环出test0、test1...
            db.insert("info",null,values);//调用insert方法
        }
    }

    @Override
    //点击事件
    public void onClick(View v) {
     
        //得到内容提供者的解析对象
        contentResolver = getContentResolver();
        //uri路径
        Uri uri = Uri.parse("content://cn.luck.contentobserverdb/info");
        //创建contentValues对象
        ContentValues values = new ContentValues();
        switch (v.getId()){
     
            case R.id.add_btn:
                values.put("name","add_test");
                Uri newUri = contentResolver.insert(uri,values);
                //提示
                Toast.makeText(this, "添加成功", Toast.LENGTH_SHORT).show();
                break;
            case R.id.update_btn:
                //将info表中name为test1这条记录更改为name是update_name
                values.put("name","update_test");
                int updateCount = contentResolver.update(uri,values,"name=?",new String[]{
     
                        "test2"
                });
                Toast.makeText(this,"成功更新"+updateCount+"行",Toast.LENGTH_SHORT).show();

                break;
            case R.id.delete_btn:
                int deleteCount = contentResolver.delete(uri,"name=?",new String[]{
     
                        "test0"
                });
                Toast.makeText(this,"成功删除了"+deleteCount+"行",Toast.LENGTH_SHORT).show();
                break;
            case R.id.select_btn:
                //集合
                List<Map<String,String>> data = new ArrayList<>();
                //返回一个指向结果的游标
                Cursor cursor = contentResolver.query(uri,new String[]{
     "_id","name"},
                        null,null,null);
                while (cursor.moveToNext()){
     
                    Map<String,String> map = new HashMap<>();
                    map.put("_id",cursor.getString(0));
                    map.put("name",cursor.getString(1));
                    data.add(map);//添加到集合中
                }
                cursor.close();//关闭游标
                Toast.makeText(this,"查询成功",Toast.LENGTH_SHORT).show();
                Log.i("查询操作","查询到的结果为:"+data.toString());
                break;
        }
    }
}

3、效果展示
安卓基础学习 Day15 |内容提供者_第7张图片
4、新建一个模块,用于监测数据变化情况
安卓基础学习 Day15 |内容提供者_第8张图片
结果如下:
安卓基础学习 Day15 |内容提供者_第9张图片

三、补充

1、Android中的Uri详解

2、Android中visibility属性

你可能感兴趣的:(学习日志)