运行有问题或需要源码请点赞关注收藏后评论区留言
一、利用ContentResolver读写联系人
在实际开发中,普通App很少会开放数据接口给其他应用访问。内容组件能够派上用场的情况往往是App想要访问系统应用的通讯数据,比如查看联系人,短信,通话记录等等,以及对这些通讯数据及逆行增删改查。 首先要给AndroidMaifest.xml中添加响应的权限配置
<!-- 存储卡读写 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAG" /> <!-- 联系人/通讯录。包括读联系人、写联系人 --> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /> <!-- 短信。包括发送短信、接收短信、读短信 --> <uses-permission android:name="android.permission.SEND_SMS" /> <uses-permission android:name="android.permission.RECEIVE_SMS" /> <uses-permission android:name="android.permission.READ_SMS" /> <!-- 通话记录。包括读通话记录、写通话记录 --> <uses-permission android:name="android.permission.READ_CALL_LOG" /> <uses-permission android:name="android.permission.WRITE_CALL_LOG" /> <!-- 安装应用请求,Android8.0需要 --> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
下面是往手机通讯录添加联系人信息的例子 效果如下
分成三个步骤 先查出联系人的基本信息,然后查询联系人号码,再查询联系人邮箱
代码
ContactAddActivity类
package com.example.chapter07; import android.annotation.SuppressLint; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.EditText; import androidx.appcompat.app.AppCompatActivity; import com.example.chapter07.bean.Contact; import com.example.chapter07.util.CommunicationUtil; import com.example.chapter07.util.ToastUtil; @SuppressLint("DefaultLocale") public class ContactAddActivity extends AppCompatActivity implements View.OnClickListener { private static final String TAG = "ContactAddActivity"; private EditText et_contact_name; private EditText et_contact_phone; private EditText et_contact_email; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_contact_add); et_contact_name = findViewById(R.id.et_contact_name); et_contact_phone = findViewById(R.id.et_contact_phone); et_contact_email = findViewById(R.id.et_contact_email); findViewById(R.id.btn_add_contact).setOnClickListener(this); } @Override public void onClick(View v) { if (v.getId() == R.id.btn_add_contact) { Contact contact = new Contact(); // 创建一个联系人对象 contact.name = et_contact_name.getText().toString().trim(); contact.phone = et_contact_phone.getText().toString().trim(); contact.email = et_contact_email.getText().toString().trim(); // 方式一,使用ContentResolver多次写入,每次一个字段 CommunicationUtil.addContacts(getContentResolver(), contact); // 方式二,使用ContentProviderOperation一次写入,每次多个字段 //CommunicationUtil.addFullContacts(getContentResolver(), contact); ToastUtil.show(this, "成功添加联系人信息"); } } }
ContactReadActivity类
package com.example.chapter07; import androidx.appcompat.app.AppCompatActivity; import android.graphics.Color; import android.os.Bundle; import android.widget.LinearLayout; import android.widget.TextView; import com.example.chapter07.bean.Contact; import com.example.chapter07.util.CommunicationUtil; import com.example.chapter07.util.ToastUtil; import com.example.chapter07.util.Utils; import java.util.List; public class ContactReadActivity extends AppCompatActivity { private TextView tv_desc; private LinearLayout ll_list; // 联系人列表的线性布局 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_contact_read); tv_desc = findViewById(R.id.tv_desc); ll_list = findViewById(R.id.ll_list); showContactInfo(); // 显示所有的联系人信息 } // 显示所有的联系人信息 private void showContactInfo() { try { // 读取所有的联系人 List<Contact> contactList = CommunicationUtil.readAllContacts(getContentResolver()); String contactCount = String.format("当前共找到%d位联系人", contactList.size()); tv_desc.setText(contactCount); for (Contact contact : contactList){ String contactDesc = String.format("姓名为%s,号码为%s",contact.name, contact.phone); TextView tv_contact = new TextView(this); // 创建一个文本视图 tv_contact.setText(contactDesc); tv_contact.setTextColor(Color.BLACK); tv_contact.setTextSize(17); int pad = Utils.dip2px(this, 5); tv_contact.setPadding(pad, pad, pad, pad); // 设置文本视图的内部间距 ll_list.addView(tv_contact); // 把文本视图添加至联系人列表的线性布局 } } catch (Exception e) { e.printStackTrace(); ToastUtil.show(this, "请检查是否开启了通讯录权限"); } } }
activity_contact_addXML
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="5dp" > <RelativeLayout android:layout_width="match_parent" android:layout_height="40dp" > <TextView android:id="@+id/tv_contact_name" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center" android:text="联系人姓名:" android:textColor="@color/black" android:textSize="17sp" /> <EditText android:id="@+id/et_contact_name" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="3dp" android:layout_marginTop="3dp" android:layout_toRightOf="@+id/tv_contact_name" android:background="@drawable/editext_selector" android:gravity="left|center" android:hint="请输入联系人姓名" android:inputType="text" android:maxLength="12" android:textColor="@color/black" android:textSize="17sp" /> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="40dp" > <TextView android:id="@+id/tv_contact_phone" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center" android:text="联系人号码:" android:textColor="@color/black" android:textSize="17sp" /> <EditText android:id="@+id/et_contact_phone" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="3dp" android:layout_marginTop="3dp" android:layout_toRightOf="@+id/tv_contact_phone" android:background="@drawable/editext_selector" android:gravity="left|center" android:hint="请输入联系人手机号码" android:inputType="number" android:maxLength="11" android:textColor="@color/black" android:textSize="17sp" /> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="40dp" > <TextView android:id="@+id/tv_contact_email" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center" android:text="联系人邮箱:" android:textColor="@color/black" android:textSize="17sp" /> <EditText android:id="@+id/et_contact_email" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="3dp" android:layout_marginTop="3dp" android:layout_toRightOf="@+id/tv_contact_email" android:background="@drawable/editext_selector" android:gravity="left|center" android:hint="请输入联系人邮箱" android:inputType="textEmailAddress" android:textColor="@color/black" android:textSize="17sp" /> </RelativeLayout> <Button android:id="@+id/btn_add_contact" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="添加联系人" android:textColor="@color/black" android:textSize="17sp" /> </LinearLayout>
activity_contact_readXML
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/tv_desc" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="5dp" android:textColor="@color/black" android:textSize="17sp" /> <ScrollView android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:id="@+id/ll_list" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" /> </ScrollView> </LinearLayout>
二、利用ContentObserver监听短信
ContentRslover获取数据采用的是主动查询方式,有查询才有数据否则美哟。为了省事,这时用到了ContentObserver内容观察器,事先给目标内容注册一个观察器,目标内容的数据一旦发生变化,就马上触发观察器的监听事件,从而执行开发者预先定义的代码
内容观察器的用法与内容提供器类似,下面是交互方法说明
registerContentObserver 内容解析器要注册内容观察器
unregisterContentObserver 注销
notifyChange 通知内容观察器发生了数据变化
java类代码
MonitorSmsActivity
package com.example.chapter07; import android.annotation.SuppressLint; import android.app.AlertDialog; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.telephony.SmsManager; import android.util.Log; import android.view.View; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; @SuppressLint("DefaultLocale") public class MonitorSmsActivity extends AppCompatActivity implements View.OnClickListener { private static final String TAG = "MonitorSmsActivity"; private static TextView tv_check_flow; private static String mCheckResult; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_monitor_sms); tv_check_flow = findViewById(R.id.tv_check_flow); tv_check_flow.setOnClickListener(this); findViewById(R.id.btn_check_flow).setOnClickListener(this); initSmsObserver(); } @Override public void onClick(View v) { if (v.getId() == R.id.btn_check_flow) { //查询数据流量,移动号码的查询方式为发送短信内容“18”给“10086” //电信和联通号码的短信查询方式请咨询当地运营商客服热线 //跳到系统的短信发送页面,由用户手工发短信 //sendSmsManual("10086", "18"); //无需用户操作,自动发送短信 sendSmsAuto("10086", "18"); } else if (v.getId() == R.id.tv_check_flow) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("收到流量校准短信"); builder.setMessage(mCheckResult); builder.setPositiveButton("确定", null); builder.create().show(); } } // 跳到系统的短信发送页面,由用户手工编辑与发送短信 public void sendSmsManual(String phoneNumber, String message) { Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse("smsto:" + phoneNumber)); intent.putExtra("sms_body", message); startActivity(intent); } // 短信发送事件 private String SENT_SMS_ACTION = "com.example.storage.SENT_SMS_ACTION"; // 短信接收事件 private String DELIVERED_SMS_ACTION = "com.example.storage.DELIVERED_SMS_ACTION"; // 无需用户操作,由App自动发送短信 public void sendSmsAuto(String phoneNumber, String message) { // 以下指定短信发送事件的详细信息 Intent sentIntent = new Intent(SENT_SMS_ACTION); sentIntent.putExtra("phone", phoneNumber); sentIntent.putExtra("message", message); PendingIntent sentPI = PendingIntent.getBroadcast(this, 0, sentIntent, PendingIntent.FLAG_UPDATE_CURRENT); // 以下指定短信接收事件的详细信息 Intent deliverIntent = new Intent(DELIVERED_SMS_ACTION); deliverIntent.putExtra("phone", phoneNumber); deliverIntent.putExtra("message", message); PendingIntent deliverPI = PendingIntent.getBroadcast(this, 1, deliverIntent, PendingIntent.FLAG_UPDATE_CURRENT); // 获取默认的短信管理器 SmsManager smsManager = SmsManager.getDefault(); // 开始发送短信内容。要确保打开发送短信的完全权限,不是那种还需提示的不完整权限 smsManager.sendTextMessage(phoneNumber, null, message, sentPI, deliverPI); } private Handler mHandler = new Handler(); // 声明一个处理器对象 private SmsGetObserver mObserver; // 声明一个短信获取的观察器对象 private static Uri mSmsUri; // 声明一个系统短信提供器的Uri对象 private static String[] mSmsColumn; // 声明一个短信记录的字段数组 // 初始化短信观察器 private void initSmsObserver() { //mSmsUri = Uri.parse("content://sms/inbox"); //Android5.0之后似乎无法单独观察某个信箱,只能监控整个短信 mSmsUri = Uri.parse("content://sms"); // 短信数据的提供器路径 mSmsColumn = new String[]{"address", "body", "date"}; // 短信记录的字段数组 // 创建一个短信观察器对象 mObserver = new SmsGetObserver(this, mHandler); // 给指定Uri注册内容观察器,一旦发生数据变化,就触发观察器的onChange方法 getContentResolver().registerContentObserver(mSmsUri, true, mObserver); } // 在页面销毁时触发 protected void onDestroy() { super.onDestroy(); getContentResolver().unregisterContentObserver(mObserver); // 注销内容观察器 } // 定义一个短信获取的观察器 private static class SmsGetObserver extends ContentObserver { private Context mContext; // 声明一个上下文对象 public SmsGetObserver(Context context, Handler handler) { super(handler); mContext = context; } // 观察到短信的内容提供器发生变化时触发 public void onChange(boolean selfChange) { String sender = "", content = ""; // 构建一个查询短信的条件语句,移动号码要查找10086发来的短信 String selection = String.format("address='10086' and date>%d", System.currentTimeMillis() - 1000 * 60 * 1); // 查找最近一分钟的短信 // 通过内容解析器获取符合条件的结果集游标 Cursor cursor = mContext.getContentResolver().query( mSmsUri, mSmsColumn, selection, null, " date desc"); // 循环取出游标所指向的所有短信记录 while (cursor.moveToNext()) { sender = cursor.getString(0); // 短信的发送号码 content = cursor.getString(1); // 短信内容 Log.d(TAG, "sender="+sender+", content="+content); break; } cursor.close(); // 关闭数据库游标 mCheckResult = String.format("发送号码:%s\n短信内容:%s", sender, content); // 依次解析流量校准短信里面的各项流量数值,并拼接流量校准的结果字符串 String flow = String.format("流量校准结果如下:总流量为:%s;已使用:%s" + ";剩余流量:%s", findFlow(content, "总流量为"), findFlow(content, "已使用"), findFlow(content, "剩余")); if (tv_check_flow != null) { // 离开该页面后就不再显示流量信息 tv_check_flow.setText(flow); // 在文本视图显示流量校准结果 } super.onChange(selfChange); } } // 解析流量短信里面的流量数值 private static String findFlow(String sms, String begin) { String flow = findString(sms, begin, "GB"); String temp = flow.replace("GB", "").replace(".", ""); if (!temp.matches("\\d+")) { flow = findString(sms, begin, "MB"); } return flow; } // 截取指定头尾之间的字符串 private static String findString(String content, String begin, String end) { int begin_pos = content.indexOf(begin); if (begin_pos < 0) { return "未获取"; } String sub_sms = content.substring(begin_pos); int end_pos = sub_sms.indexOf(end); if (end_pos < 0) { return "未获取"; } if (end.equals(",")) { return sub_sms.substring(begin.length(), end_pos); } else { return sub_sms.substring(begin.length(), end_pos + end.length()); } } }
XML文件代码
activity_monitor_smsXML
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/btn_check_flow" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="发送校准短信" android:textColor="@color/black" android:textSize="17sp" /> <TextView android:id="@+id/tv_check_flow" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingLeft="5dp" android:textColor="@color/black" android:textSize="17sp" /> </LinearLayout>
创作不易 觉得有帮助请点赞关注收藏~~~~