news 2026/6/23 16:41:08

安卓基础之《(15)—内容提供者(1)在应用之间共享数据》

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
安卓基础之《(15)—内容提供者(1)在应用之间共享数据》

一、通过ContentProvider封装数据

1、ContentProvider
ContentProvider为App存取内部数据提供统一的外部接口,让不同的应用之间得以共享数据

ContentProvider相当于一个窗口、一个门卫
一个应用读取另一个应用的数据,比如用户登录时,收到验证码自动读取

2、ContentProvider案例
Client App将用户的输入内容,通过ContentProvider跨进程通信传递给Server App

3、ContentProvider只是服务端App存取数据的抽象类,我们需要在其基础上实现一个完整的内容提供器,并重写下列方法
(1)onCreate:初始化资源
Provider在应用启动的时候就创建了
(2)insert:插入数据
(3)delete:删除数据
(4)update:更新数据
(5)query:查询数据
(6)getType:获取内容提供器支持的数据类型

4、Uri
Uri(通用资源标识符Universal Resource Identifer),代表数据操作的地址,每一个ContentProvider都会有唯一的地址,格式content://authority/data_path/id
说明:
(1)“content://”:通用前缀,表示该Uri用于ContentProvider定位资源
(2)“authority”:授权者名称,用来确定具体由哪一个ContentProvider提供资源。因此一般authority都由类的小写全称组成,以保证唯一性
(3)“data_path”:数据路径,用来确定请求的是哪个数据集
(4)id:数据编号,用来请求单条数据。如果是多条这个字段忽略

二、创建Server App

1、新建一个Module,chapter07-server

2、创建ContentProvider
【New】->【Other】->【Content Provider】,会自动生成文件

3、修改清单文件
authorities填生成的Provider文件的包名+类名

<provider android:name=".provider.UserInfoProvider" android:authorities="com.example.chapter07_server.provider.UserInfoProvider" android:enabled="true" android:exported="true" />

服务端要说明下访问客户端软件包

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

4、UserInfoProvider.java

package com.example.chapter07_server.provider; import android.content.ContentProvider; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.util.Log; public class UserInfoProvider extends ContentProvider { public UserInfoProvider() { } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { Log.d("sam", "UserInfoProvider delete"); return 0; } @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) { Log.d("sam", "UserInfoProvider insert"); Log.d("sam", "uri " + uri.toString()); Log.d("sam", "values " + values.toString()); return uri; } @Override public boolean onCreate() { Log.d("sam", "UserInfoProvider onCreate"); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Log.d("sam", "UserInfoProvider query"); return null; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { Log.d("sam", "UserInfoProvider update"); return 0; } }

5、清单文件

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 出于安全考虑,Android 11开始要求应用事先说明需要访问的其他软件包 --> <queries> <package android:name="com.example.chapter07_client" /> </queries> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.MyApplication"> <provider android:name=".provider.UserInfoProvider" android:authorities="com.example.chapter07_server.provider.UserInfoProvider" android:enabled="true" android:exported="true" /> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>

Server端暴露数据访问接口,提供其他Client访问

三、通过ContentResolver访问数据

1、ContentResolver
利用ContentProvider只实现服务端App的数据封装,如果客户端App想访问对方的内部数据,就要通过内容解析器ContentResolver访问

四、创建Client App

1、新建一个Module,chapter07-client

2、ContentWriteActivity.java

package com.example.chapter07_client; import androidx.appcompat.app.AppCompatActivity; import android.content.ContentValues; import android.os.Bundle; import android.view.View; import android.widget.CheckBox; import android.widget.EditText; public class ContentWriteActivity extends AppCompatActivity implements View.OnClickListener { private EditText et_name; private EditText et_age; private EditText et_height; private EditText et_weight; private CheckBox ck_married; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_content_write); et_name = findViewById(R.id.et_name); et_age = findViewById(R.id.et_age); et_height = findViewById(R.id.et_height); et_weight = findViewById(R.id.et_weight); ck_married = findViewById(R.id.ck_married); findViewById(R.id.btn_save).setOnClickListener(this); findViewById(R.id.btn_delete).setOnClickListener(this); findViewById(R.id.btn_update).setOnClickListener(this); findViewById(R.id.btn_query).setOnClickListener(this); } @Override public void onClick(View view) { String name = et_name.getText().toString(); String age = et_age.getText().toString(); String height = et_height.getText().toString(); String weight = et_weight.getText().toString(); if (view.getId() == R.id.btn_save) { ContentValues values = new ContentValues(); values.put("name", name); values.put("age", Integer.parseInt(age)); values.put("height", Float.parseFloat(height)); values.put("weight", Float.parseFloat(weight)); values.put("married", ck_married.isChecked()); // 通过ContentResolver访问数据 getContentResolver().insert(UserInfoContent.CONTENT_URI, values); } else if (view.getId() == R.id.btn_delete) { } else if (view.getId() == R.id.btn_update) { } else if (view.getId() == R.id.btn_query) { } } }

3、UserInfoContent.java

package com.example.chapter07_client; import android.net.Uri; public class UserInfoContent { public static final String AUTHORITIES = "com.example.chapter07_server.provider.UserInfoProvider"; // 访问内容提供器的URI // content://com.example.chapter07_server.provider.UserInfoProvider/user public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITIES + "/user"); }

4、布局文件activity_content_write.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".ContentWriteActivity"> <GridLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:columnCount="2" android:rowCount="4"> <TextView android:layout_width="60dp" android:layout_height="wrap_content" android:text="姓名:" android:textSize="17sp"/> <EditText android:id="@+id/et_name" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:inputType="text" android:hint="请输入姓名"/> <TextView android:layout_width="60dp" android:layout_height="wrap_content" android:text="年龄:" android:textSize="17sp"/> <EditText android:id="@+id/et_age" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:inputType="number" android:maxLength="3" android:hint="请输入年龄"/> <TextView android:layout_width="60dp" android:layout_height="wrap_content" android:text="身高:" android:textSize="17sp"/> <EditText android:id="@+id/et_height" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:inputType="numberDecimal" android:maxLength="6" android:hint="请输入身高"/> <TextView android:layout_width="60dp" android:layout_height="wrap_content" android:text="体重:" android:textSize="17sp"/> <EditText android:id="@+id/et_weight" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:inputType="numberDecimal" android:maxLength="6" android:hint="请输入体重"/> </GridLayout> <CheckBox android:id="@+id/ck_married" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="已婚" android:textSize="17sp"/> <Button android:id="@+id/btn_save" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="添加" android:textSize="17sp"/> <Button android:id="@+id/btn_delete" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="删除" android:textSize="17sp"/> <Button android:id="@+id/btn_update" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="修改" android:textSize="17sp"/> <Button android:id="@+id/btn_query" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="查询" android:textSize="17sp"/> </LinearLayout>

5、清单文件
出于安全考虑,Android 11开始要求应用事先说明需要访问的其他软件包
这里服务端也要说明下访问客户端软件包,否则一直运行不出来

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <queries> <package android:name="com.example.chapter07_server" /> </queries> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.MyApplication"> <activity android:name=".ContentWriteActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>

6、运行日志

以添加数据为例

2026-01-12 16:10:28.223 20546-20546 sam com.example.chapter07_server D UserInfoProvider onCreate 2026-01-12 16:11:00.935 20546-20557 sam com.example.chapter07_server D UserInfoProvider insert 2026-01-12 16:11:00.935 20546-20557 sam com.example.chapter07_server D uri content://com.example.chapter07_server.provider.UserInfoProvider/user 2026-01-12 16:11:00.936 20546-20557 sam com.example.chapter07_server D values height=170.0 weight=56.0 age=12 name=qwer married=true
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/20 15:14:03

项目编码与 WBS 编码规则的设置需通过事务码 OPSK(为项目定义特殊性)与 OPSJ(定义项目编码屏蔽)联动完成,核心是先定义编码分隔符、校验规则,再配置层级掩码与编号格式

项目编码与 WBS 编码规则的设置需通过事务码 OPSK&#xff08;为项目定义特殊性&#xff09;与 OPSJ&#xff08;定义项目编码屏蔽&#xff09;联动完成&#xff0c;核心是先定义编码分隔符、校验规则&#xff0c;再配置层级掩码与编号格式&#xff0c;最后在项目参数文件启用自…

作者头像 李华
网站建设 2026/6/14 22:43:36

SAP 编码掩码(OPSJ)修改 / 新增操作校验清单

SAP 编码掩码&#xff08;OPSJ&#xff09;修改 / 新增操作校验清单&#xff08;覆盖 PrjID、掩码结构、依赖关系全维度&#xff0c;避免 CJ611 等报错&#xff09;一、新增编码掩码前的必校验项校验项校验内容操作方式失败后果PrjID 唯一性新 PrjID 未被任何掩码占用用 SE16 查…

作者头像 李华
网站建设 2026/6/18 9:03:40

本地化部署+术语控制|用HY-MT1.5构建安全翻译流水线

本地化部署术语控制&#xff5c;用HY-MT1.5构建安全翻译流水线 在企业全球化运营、政府跨语言服务以及多语种内容生产等场景中&#xff0c;高质量、高安全性、可定制化的机器翻译需求日益迫切。然而&#xff0c;依赖云端API的通用翻译服务存在数据泄露风险、术语不一致、格式丢…

作者头像 李华
网站建设 2026/6/17 23:49:38

从理论到落地:基于GTE镜像的余弦相似度应用全解析

从理论到落地&#xff1a;基于GTE镜像的余弦相似度应用全解析 1. 引言 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;语义相似度计算是支撑信息检索、智能问答、推荐系统等核心任务的关键技术。传统方法依赖关键词匹配或词频统计&#xff0c;难以捕捉“同义表达…

作者头像 李华
网站建设 2026/6/17 22:12:50

2026年下半年的IT就业市场充满机遇,你准备好了吗?

2026年下半年的IT就业市场充满机遇&#xff0c;你准备好了吗&#xff1f; 随着2026年进入下半年&#xff0c;从AI大模型到网络安全&#xff0c;从芯片设计到云计算运维&#xff0c;各大科技企业纷纷开启**“抢人模式”**。小编将为你盘点2026年下半年最热门的IT就业方向&#…

作者头像 李华
网站建设 2026/6/22 14:18:39

StructBERT中文情感分析镜像:一键部署API与可视化界面

StructBERT中文情感分析镜像&#xff1a;一键部署API与可视化界面 1. 背景与需求&#xff1a;为什么需要轻量级中文情感分析服务&#xff1f; 在当前自然语言处理&#xff08;NLP&#xff09;广泛应用的背景下&#xff0c;中文情感分析已成为舆情监控、用户反馈挖掘、客服系统…

作者头像 李华