Android 使用VCard数据类型 异步进行联系人备份与恢复操作

本文主要是介绍Android 使用VCard数据类型 异步进行联系人备份与恢复操作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

生活中常有人因为更换手机而丢失联系人信息,又需要重新一个个去找亲戚朋友获取。
这样的情况下,联系人备份与恢复功能就显得非常实用。所以我学习了相关内容,并进行了适当整合,在这里整理出来。

本篇博客两个重点

  1. 使用VCard库进行联系人备份与恢复
  2. 异步进行备份与恢复操作

为什么要用VCard?

VCard是用于联系人数据存储的标准数据格式,且有一套已经成熟的库可以使用,通用于手机,也通用于邮件等,所以使用VCard进行联系人的数据存储与备份格式是非常好的选择。当然是用XML格式去存储也是可以的。

下载android-vcard.jar请猛戳这里

为什么要异步进行操作?

联系人备份与恢复都涉及到了数据库读写,而数据库的操作一向都是比较费时的,更何况是大量数据的情况下,所以将这些操作进行异步处理是一个非常有必要的事情,否则容易造成UI主线程卡顿。异步的另一个好处是同时能够在UI界面上给用户一些过程进度上的反馈,这个在重视产品交互体验的今天是非常重要的。

一个异步操作UI效果库,源自GitHub请猛戳这里

VCard本身库比较复杂,不再多说,有兴趣的可以研究源码。这里贴上一个已经做过一层封装的联系人处理类,直接集成调用即可。

封装好的联系人处理类model

model的属性包括基本的联系人姓名、电话、邮箱。其他属性如果有需求可以自己添加。

package com.pku.codingma.backupandrecovercontactdemo;import android.app.Activity;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.provider.ContactsContract;import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;import a_vcard.android.provider.Contacts;
import a_vcard.android.syncml.pim.VDataBuilder;
import a_vcard.android.syncml.pim.VNode;
import a_vcard.android.syncml.pim.vcard.ContactStruct;
import a_vcard.android.syncml.pim.vcard.VCardComposer;
import a_vcard.android.syncml.pim.vcard.VCardException;
import a_vcard.android.syncml.pim.vcard.VCardParser;/*** Created by ma on 2016/4/1.*/
public class ContactInfo {/*** MUST exist*/private String name; // 姓名/*** 联系人电话信息*/public static class PhoneInfo {/*** 联系电话类型*/public int type;/*** 联系电话*/public String number;}/*** 联系人邮箱信息*/public static class EmailInfo {/*** 邮箱类型*/public int type;/*** 邮箱*/public String email;}private List<PhoneInfo> phoneList = new ArrayList<PhoneInfo>(); // 联系号码private List<EmailInfo> email = new ArrayList<EmailInfo>(); // Email/*** 构造联系人信息** @param name 联系人姓名*/public ContactInfo(String name) {this.name = name;}/*** 姓名*/public String getName() {return name;}/*** 姓名*/public ContactInfo setName(String name) {this.name = name;return this;}/*** 联系电话信息*/public List<PhoneInfo> getPhoneList() {return phoneList;}/*** 联系电话信息*/public ContactInfo setPhoneList(List<PhoneInfo> phoneList) {this.phoneList = phoneList;return this;}/*** 邮箱信息*/public List<EmailInfo> getEmail() {return email;}/*** 邮箱信息*/public ContactInfo setEmail(List<EmailInfo> email) {this.email = email;return this;}@Overridepublic String toString() {return "{name: " + name + ", number: " + phoneList + ", email: " + email + "}";}
}

封装在ContactInfo内部的操作handler类

handler内部二次封装了通过VCard库对联系人操作的函数

/*** 联系人* 备份/还原操作**/public static class ContactHandler {private static ContactHandler instance_ = new ContactHandler();/*** 获取实例*/public static ContactHandler getInstance() {return instance_;}}
通过数据库获取手机中的联系人

第一步通过ContentResolver对所有联系人进行查询,获得Cursor
第二步将Cursor中的数据依次取出,构造成ContactInfo数组,为第三步做准备

        /*** 获取联系人指定信息** @param projection 指定要获取的列数组, 获取全部列则设置为null* @return* @throws Exception*/public Cursor queryContact(Activity context, String[] projection) {// 获取联系人的所需信息Cursor cur = context.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, projection, null, null, null);return cur;}/*** 获取联系人信息** @param context* @return*/public List<ContactInfo> getContactInfo(Activity context) {List<ContactInfo> infoList = new ArrayList<ContactInfo>();Cursor cur = queryContact(context, null);if (cur.moveToFirst()) {do {// 获取联系人id号String id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));// 获取联系人姓名String displayName = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));ContactInfo info = new ContactInfo(displayName);// 初始化联系人信息// 查看联系人有多少电话号码, 如果没有返回0int phoneCount = cur.getInt(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));if (phoneCount > 0) {Cursor phonesCursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + id, null, null);if (phonesCursor.moveToFirst()) {List<PhoneInfo> phoneNumberList = new ArrayList<PhoneInfo>();do {// 遍历所有电话号码String phoneNumber = phonesCursor.getString(phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));// 对应的联系人类型int type = phonesCursor.getInt(phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE));// 初始化联系人电话信息ContactInfo.PhoneInfo phoneInfo = new ContactInfo.PhoneInfo();phoneInfo.type = type;phoneInfo.number = phoneNumber;phoneNumberList.add(phoneInfo);} while (phonesCursor.moveToNext());// 设置联系人电话信息info.setPhoneList(phoneNumberList);}}// 获得联系人的EMAILCursor emailCur = context.getContentResolver().query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, ContactsContract.CommonDataKinds.Email.CONTACT_ID + "=" + id, null, null);if (emailCur.moveToFirst()) {List<EmailInfo> emailList = new ArrayList<EmailInfo>();do {// 遍历所有的emailString email = emailCur.getString(emailCur.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA1));int type = emailCur.getInt(emailCur.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE));// 初始化联系人邮箱信息ContactInfo.EmailInfo emailInfo = new ContactInfo.EmailInfo();emailInfo.type = type;    // 设置邮箱类型emailInfo.email = email;    // 设置邮箱地址emailList.add(emailInfo);} while (emailCur.moveToNext());info.setEmail(emailList);}//Cursor postalCursor = getContentResolver().query(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_URI, null, ContactsContract.CommonDataKinds.StructuredPostal.CONTACT_ID + "=" + id, null, null);infoList.add(info);} while (cur.moveToNext());}cur.close();return infoList;}
将获取到的联系人数据通过VCard数据格式备份

第三步在获取到ContactInfo数组后,将其写入VCard文件中

/*** 备份联系人*/public void backupContacts(Activity context, List<ContactInfo> infos) {try {String path = Environment.getExternalStorageDirectory() + "/contacts.vcf";OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(path), "UTF-8");VCardComposer composer = new VCardComposer();for (ContactInfo info : infos) {ContactStruct contact = new ContactStruct();contact.name = info.getName();// 获取联系人电话信息, 添加至 ContactStructList<PhoneInfo> numberList = info.getPhoneList();for (ContactInfo.PhoneInfo phoneInfo : numberList) {contact.addPhone(phoneInfo.type, phoneInfo.number,null, true);}// 获取联系人Email信息, 添加至 ContactStructList<EmailInfo> emailList = info.getEmail();for (ContactInfo.EmailInfo emailInfo : emailList) {contact.addContactmethod(Contacts.KIND_EMAIL,emailInfo.type, emailInfo.email, null, true);}String vcardString = composer.createVCard(contact,VCardComposer.VERSION_VCARD30_INT);writer.write(vcardString);writer.write("\n");writer.flush();}writer.close();} catch (UnsupportedEncodingException e) {e.printStackTrace();} catch (FileNotFoundException e) {e.printStackTrace();} catch (VCardException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}
获取VCard文件中的联系人信息

反之如果要从VCard中恢复联系人数据的话,也先是将其转化为ContactInfo数组

/*** 获取vCard文件中的联系人信息** @return List<ContactInfo>*/public List<ContactInfo> restoreContacts() throws Exception {List<ContactInfo> contactInfoList = new ArrayList<ContactInfo>();VCardParser parse = new VCardParser();VDataBuilder builder = new VDataBuilder();String file = Environment.getExternalStorageDirectory() + "/contacts2.vcf";BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));String vcardString = "";String line;while ((line = reader.readLine()) != null) {vcardString += line + "\n";}reader.close();boolean parsed = parse.parse(vcardString, "UTF-8", builder);if (!parsed) {throw new VCardException("Could not parse vCard file: " + file);}List<VNode> pimContacts = builder.vNodeList;for (VNode contact : pimContacts) {ContactStruct contactStruct = ContactStruct.constructContactFromVNode(contact, 1);// 获取备份文件中的联系人电话信息List<ContactStruct.PhoneData> phoneDataList = contactStruct.phoneList;List<PhoneInfo> phoneInfoList = new ArrayList<PhoneInfo>();for (ContactStruct.PhoneData phoneData : phoneDataList) {ContactInfo.PhoneInfo phoneInfo = new ContactInfo.PhoneInfo();phoneInfo.number = phoneData.data;phoneInfo.type = phoneData.type;phoneInfoList.add(phoneInfo);}// 获取备份文件中的联系人邮箱信息List<ContactStruct.ContactMethod> emailList = contactStruct.contactmethodList;List<EmailInfo> emailInfoList = new ArrayList<EmailInfo>();// 存在 Email 信息if (null != emailList) {for (ContactStruct.ContactMethod contactMethod : emailList) {if (Contacts.KIND_EMAIL == contactMethod.kind) {ContactInfo.EmailInfo emailInfo = new ContactInfo.EmailInfo();emailInfo.email = contactMethod.data;emailInfo.type = contactMethod.type;emailInfoList.add(emailInfo);}}}ContactInfo info = new ContactInfo(contactStruct.name).setPhoneList(phoneInfoList).setEmail(emailInfoList);contactInfoList.add(info);}return contactInfoList;}
将从VCard中获取的联系人数据插入到手机中

获取到ContactInfo数组后,再通过ContentResolver类将ContactInfo数组依次插入到系统联系人数据库中

/*** 向手机中录入联系人信息** @param info 要录入的联系人信息*/public void addContacts(Activity context, ContactInfo info) {ContentValues values = new ContentValues();//首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactIdUri rawContactUri = context.getContentResolver().insert(ContactsContract.RawContacts.CONTENT_URI, values);long rawContactId = ContentUris.parseId(rawContactUri);//往data表入姓名数据values.clear();values.put(ContactsContract.RawContacts.Data.RAW_CONTACT_ID, rawContactId);values.put(ContactsContract.RawContacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, info.getName());context.getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values);// 获取联系人电话信息List<PhoneInfo> phoneList = info.getPhoneList();/** 录入联系电话 */for (ContactInfo.PhoneInfo phoneInfo : phoneList) {values.clear();values.put(ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);values.put(ContactsContract.RawContacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);// 设置录入联系人电话信息values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phoneInfo.number);values.put(ContactsContract.CommonDataKinds.Phone.TYPE, phoneInfo.type);// 往data表入电话数据context.getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values);}// 获取联系人邮箱信息List<EmailInfo> emailList = info.getEmail();/** 录入联系人邮箱信息 */for (ContactInfo.EmailInfo email : emailList) {values.clear();values.put(ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);values.put(ContactsContract.RawContacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);// 设置录入的邮箱信息values.put(ContactsContract.CommonDataKinds.Email.DATA, email.email);values.put(ContactsContract.CommonDataKinds.Email.TYPE, email.type);// 往data表入Email数据context.getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values);}}}

异步处理与功能使用DEMO

使用了基本的线程去实现异步,通过Handler传递结果消息,并更新按钮状态。

package com.pku.codingma.backupandrecovercontactdemo;import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;import com.dd.CircularProgressButton;import java.util.List;/*** A placeholder fragment containing a simple view.*/
public class BackupAndRecoverContactActivityFragment extends Fragment implements View.OnClickListener{CircularProgressButton mBackupContactButton;CircularProgressButton mRecoverContactButton;//标记消息的来源public final int BACKUP_WHAT = 0;public final int RECOVER_WHAT = 1;//标记成功还是失败public final int SUCCESS_FLAG = 1;public final int FAIL_FLAG = 0;//用于进行备份和还原操作的ContactHandler内部类ContactInfo.ContactHandler handler = ContactInfo.ContactHandler.getInstance();public BackupAndRecoverContactActivityFragment() {}Handler mBackupAndRecoverProcessHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);if (msg.what == BACKUP_WHAT){//add your actionif (msg.arg1 == SUCCESS_FLAG){mBackupContactButton.setProgress(100);}else {mBackupContactButton.setProgress(-1);}}else if (msg.what == RECOVER_WHAT){//add your actionif (msg.arg1 == SUCCESS_FLAG){mRecoverContactButton.setProgress(100);}else {mRecoverContactButton.setProgress(-1);}}ShowTipTool.showTip(getActivity(), msg.obj.toString());}};@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {View view =  inflater.inflate(R.layout.fragment_backup_and_recover_contact, container, false);mBackupContactButton = (CircularProgressButton) view.findViewById(R.id.backup_contact_button);mRecoverContactButton = (CircularProgressButton) view.findViewById(R.id.recover_contact_button);initEvent();return view;}private void initEvent() {mRecoverContactButton.setOnClickListener(this);mBackupContactButton.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.backup_contact_button:backup_contact();break;case R.id.recover_contact_button:recover_contact();break;default:break;}}public void backup_contact(){//让按钮进入工作状态mBackupContactButton.setIndeterminateProgressMode(true);mBackupContactButton.setProgress(50);new Thread(new Runnable() {@Overridepublic void run() {//新建一条Handler处理的消息Message message = new Message();try{// 进行备份联系人信息动作handler.backupContacts(getActivity(), handler.getContactInfo(getActivity()));//如果顺利,则将消息的参数设置为成功message.obj = "backup success";message.arg1 = SUCCESS_FLAG;}catch (Exception e){//如果出现异常,则将消息的参数设置为失败message.obj = "backup fail";message.arg1 = FAIL_FLAG;e.printStackTrace();}finally {//最后设置消息来源并发送message.what = BACKUP_WHAT;mBackupAndRecoverProcessHandler.sendMessage(message);}}}).start();}//与backup基本相同,不再注释public void recover_contact(){mRecoverContactButton.setIndeterminateProgressMode(true);mRecoverContactButton.setProgress(50);new Thread(new Runnable() {@Overridepublic void run() {Message message = new Message();try {// 获取要恢复的联系人信息List<ContactInfo> infoList = handler.restoreContacts();for (ContactInfo contactInfo : infoList) {// 恢复联系人handler.addContacts(getActivity(), contactInfo);}message.obj = "recover success";message.arg1 = SUCCESS_FLAG;} catch (Exception e) {message.obj = "recover fail";message.arg1 = FAIL_FLAG;e.printStackTrace();}finally {message.what = RECOVER_WHAT;mBackupAndRecoverProcessHandler.sendMessage(message);}}}).start();}
}

添加读写权限

需要使用到读写联系人权限和读写外部存储权限,否则会报错

<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

效果图若干张

依次为初始状态,处理状态,完成状态,异常出错状态
联系人数据的具体变化就不再贴了
这里写图片描述

获取Demo完整代码方法

  1. 访问我的GitHub进行下载,如果觉得有帮助,请点个赞,谢谢请猛戳这里
  2. 通过CSDN下载站进行积分下载请猛戳这里

文章原始来源与转载请标明的出处http://blog.csdn.net/u012145166/article/details/51052880

参考http://www.cnblogs.com/lw900320/archive/2013/01/10/2855145.html

这篇关于Android 使用VCard数据类型 异步进行联系人备份与恢复操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/275071

相关文章

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

电脑桌面文件删除了怎么找回来?别急,快速恢复攻略在此

在日常使用电脑的过程中,我们经常会遇到这样的情况:一不小心,桌面上的某个重要文件被删除了。这时,大多数人可能会感到惊慌失措,不知所措。 其实,不必过于担心,因为有很多方法可以帮助我们找回被删除的桌面文件。下面,就让我们一起来了解一下这些恢复桌面文件的方法吧。 一、使用撤销操作 如果我们刚刚删除了桌面上的文件,并且还没有进行其他操作,那么可以尝试使用撤销操作来恢复文件。在键盘上同时按下“C

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]