【达内课程】ContentProvider

本文深入讲解Android中的ContentProvider,包括其基本概念、如何创建及注册ContentProvider,以及如何通过ContentResolver进行数据的增删改查操作。此外,还介绍了如何解决Android 11中的Unknown URL问题,并展示了如何利用ContentProvider拓展音乐播放器功能。

介绍

【介绍】
ContentProvider,内容提供者,是 Android 系统的核心组件。用于向其他应用程序提供访问自身数据的机制。

注册 ContentProvider 时,必须配置 android:nameandroid:authoritiesandroid:exported=true属性。

ContentProvider 向外提供数据访问时,可以提供增删改查这 4 种访问方式中的多种,从实现成本上考虑,通常会结合 SQLite 一起使用。

Android 系统使用 ContentProvider 机制提供了各种系统应用产生的数据的共享方式,例如共享了联系人数据、媒体库数据…

【ContentResolver】
必要的工具类:ContentResolver,访问 ContentResolver 必须使用到 Uri 对象,可以通过 Uri.parse(String)方法将 String 转为 Uri 类型的数据,在访问 ContentProvider 时,Uri 的字符串格式必须是 content://??? 格式的。

Context 类定义了 getContentResolver()方法,用于获取 ContentResolver 对象,并且由 ContextWrapper 重写实现了该方法,所以,Activity 和 Service 均通过继承得到了 getContentResolver()方法。

增加功能的实现

在上一节的 联系人项目 基础上做如下修改:

增加 PersonProvider

public class PersonProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
        return null;
    }

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

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
        DBOpenHelper dbOpenHelper = new DBOpenHelper(getContext());
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase();

        String nullColumnHack = "_id";
        long id = db.insert("users", nullColumnHack, contentValues);

        //释放资源
        db.close();
        db = null;

        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
        return 0;
    }
}

AndroidManifest注册 Provider

<provider
	android:name=".provider.PersonProvider"
	android:authorities="hello_world"
	android:exported="true" />

运行程序,我们先多添加几个数据
在这里插入图片描述
然后新建一个项目 MyApplication2

MainActivity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //测试增加数据
        testInsert();
    }

    private void testInsert() {
        //准备ContentResolver
        ContentResolver cr = getContentResolver();
        //相当于网址
        Uri uri = Uri.parse("content://hello_world");
        //向数据库中增加的数据的值
        ContentValues values = new ContentValues();
        values.put("_name", "Susan");
        values.put("_age", 44);
        values.put("_phone", "14444444444");
        values.put("_email", "susan@qq.com");
        //执行增加
        cr.insert(uri, values);
    }
}

Unknown URL content://hello_world

这是在 Android 11 下才会出现的问题,简单来说,就是出于安全考虑,Android 11 要求应用事先说明需要访问的其他软件包。

解决办法:
在 My Application2 中的 AndroidManifest.xml 中增加

<queries>
	<package android:name="com.example.testapplication" />
</queries>

改好程序好,先运行第一个项目,然后运行 MyApplication2 ,然后再打开第一个项目看下数据,发现已经增加成功了
在这里插入图片描述

查询数据的功能实现

PersonProvider

@Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
        //获取SQLiteDatabase对象
        DBOpenHelper dbOpenHelper = new DBOpenHelper(getContext());
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
        //执行业务:查询数据
        Cursor c = db.query("users", strings, s, strings1, null, null, s1);
        return c;
    }

第二个项目中的 MainActivity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //测试增加数据
        //testInsert();
        //测试查询数据
        testQuery();
    }
    ......
    private void testQuery() {
        ContentResolver cr = getContentResolver();
        Uri uri = Uri.parse("content://hello_world");
        //执行查询
        Cursor c = cr.query(uri, null, null, null, null);
        for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
            int id = c.getInt(c.getColumnIndex("_id"));
            String name = c.getString(c.getColumnIndex("_name"));
            int age = c.getInt(c.getColumnIndex("_age"));
            String phone = c.getString(c.getColumnIndex("_phone"));
            String email = c.getString(c.getColumnIndex("_email"));
            Log.d("CONTENT_PROVIDER", "id:" + id + ";name:" + name + ";age:" + age + ":phone:" + phone + ";email:" + email);
        }
    }
}

先运行第一个项目,然后运行第二个项目,日志输出如下
在这里插入图片描述

拓展:修改音乐播放器3.0

我们打开 Device File Exploer,我们在 data/data 下会发现有很多包含 providers 的文件夹,这些是安卓内置的使用 ContentProvider 共享数据的程序
在这里插入图片描述
打开com.android.providers.media中 databases,导出 internal.db
在这里插入图片描述
歌曲信息既然存储在表中,我们可以改进音乐播放器3

新增 MediaStoreMusicDao


public class MediaStoreMusicDao implements IDao<Music> {
    private Context content;

    public MediaStoreMusicDao(Context content) {
        this.content = content;
    }

    @Override
    public List<Music> getData() {
        //声明返回值
        List<Music> musics = new ArrayList<>();
        //准备ContentResolver
        ContentResolver cr = content.getContentResolver();
        //准备Uri
        Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
        //读数据,获取Cursor对象
        String[] projection = {
                "_id",//0
                "_data",//1->path
                "title",//2
                "duration",//3
                "album",//4
                "artist",//5
                "album_artist"//
        };//读取的字段
        String selection = null;
        String[] selectionArgs = null;
        String sortOrder = null;
        Cursor c = cr.query(uri, projection, selection, selectionArgs, sortOrder);
        //遍历Cursor,向返回值中添加数据
        for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
            Music music = new Music();
            music.setId(c.getLong(0));
            music.setPath(c.getString(1));
            music.setTitle(c.getString(2));
            music.setDuration(c.getInt(3));
            music.setAlbum(c.getString(4));
            music.setArtist(c.getString(5));
            music.setAlbumArtist(c.getString(6));
            musics.add(music);
        }
        //释放资源
        c.close();
        c = null;
        return musics;
    }
}

修改 MusicDaoFactory

public class MusicDaoFactory {
    //如果是单例,就用getInstance();
    public static IDao<Music> newInstance(Context context) {
        return new MediaStoreMusicDao(context);
    }
}

修改 Music 类,我们要存储更多的数据

public class Music {
    private long id;
    private String title;
    private String path;
    private int duration;
    private String album;
    private String artist;
    private String albumArtist;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public int getDuration() {
        return duration;
    }

    public void setDuration(int duration) {
        this.duration = duration;
    }

    public String getAlbum() {
        return album;
    }

    public void setAlbum(String album) {
        this.album = album;
    }

    public String getArtist() {
        return artist;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }

    public String getAlbumArtist() {
        return albumArtist;
    }

    public void setAlbumArtist(String albumArtist) {
        this.albumArtist = albumArtist;
    }
}

我们之前的音乐播放器只显示了标题和路径,我们可以调整界面,显示更多的内容了

item_music.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="10dp">

    <TextView
        android:id="@+id/tv_music_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:maxLines="1"
        android:text="title"
        android:textColor="#222222"
        android:textSize="15dp" />

    <TextView
        android:id="@+id/tv_music_duration"
        android:layout_width="50dp"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_marginLeft="10dp"
        android:maxLines="1"
        android:text="00:00"
        android:textColor="#222222"
        android:textSize="15dp" />

    <TextView
        android:id="@+id/tv_music_album"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_below="@+id/tv_music_title"
        android:maxLines="1"
        android:text="path......"
        android:textColor="#999999"
        android:textSize="15dp" />

    <TextView
        android:id="@+id/tv_music_artist"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_below="@+id/tv_music_title"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@+id/tv_music_album"
        android:maxLines="1"
        android:text="path......"
        android:textColor="#999999"
        android:textSize="15dp" />
</RelativeLayout>

修改 MusicAdapter

public class MusicAdapter extends BaseAdapter<Music> {
    public MusicAdapter(Context context, List data) {
        super(context, data);
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        Music music = getData().get(i);

        ViewHolder holder;

        if (view == null) {
            view = getInflater().inflate(R.layout.item_music, null);
            holder = new ViewHolder();
            holder.title = view.findViewById(R.id.tv_music_title);
            holder.duration = view.findViewById(R.id.tv_music_duration);
            holder.album = view.findViewById(R.id.tv_music_album);
            holder.artist = view.findViewById(R.id.tv_music_artist);
            view.setTag(holder);
        } else {
            holder = (ViewHolder) view.getTag();
        }

        holder.title.setText(music.getTitle());
        holder.duration.setText(CommonUtils.getFormattedTime(music.getDuration()));
        holder.album.setText(music.getAlbum());
        holder.artist.setText(music.getArtist());

        return view;
    }

    class ViewHolder {
        TextView title;
        TextView duration;
        TextView album;
        TextView artist;
    }
}

运行程序:
在这里插入图片描述
同时之前通过 MediaPlayer 获得时长的代码player.getDuration() 可以全部替换成musics.get(currentMusicIndex).getDuration(),可以通过数据库读取了。

内容概要:本文介绍了ENVI Deep Learning V1.0的操作教程,重点讲解了如何利用ENVI软件进行深度学习模型的训练与应用,以实现遥感图像中特定目标(如集装箱)的自动提取。教程涵盖了从数据准备、标签图像创建、模型初始化与训练,到执行分类及结果优化的完整流程,并介绍了精度评价与通过ENVI Modeler实现一键化建模的方法。系统基于TensorFlow框架,采用ENVINet5(U-Net变体)架构,支持通过点、线、面ROI或分类图生成标签数据,适用于多/高光谱影像的单一类别特征提取。; 适合人群:具备遥感图像处理基础,熟悉ENVI软件操作,从事地理信息、测绘、环境监测等相关领域的技术人员或研究人员,尤其是希望将深度学习技术应用于遥感目标识别的初学者与实践者。; 使用场景及目标:①在遥感影像中自动识别和提取特定地物目标(如车辆、建筑、道路、集装箱等);②掌握ENVI环境下深度学习模型的训练流程与关键参数设置(如Patch Size、Epochs、Class Weight等);③通过模型调优与结果反馈提升分类精度,实现高效自动化信息提取。; 阅读建议:建议结合实际遥感项目边学边练,重点关注标签数据制作、模型参数配置与结果后处理环节,充分利用ENVI Modeler进行自动化建模与参数优化,同时注意软硬件环境(特别是NVIDIA GPU)的配置要求以保障训练效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值