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

相关文章

SpringBoot中使用Flux实现流式返回的方法小结

《SpringBoot中使用Flux实现流式返回的方法小结》文章介绍流式返回(StreamingResponse)在SpringBoot中通过Flux实现,优势包括提升用户体验、降低内存消耗、支持长连... 目录背景流式返回的核心概念与优势1. 提升用户体验2. 降低内存消耗3. 支持长连接与实时通信在Sp

python使用库爬取m3u8文件的示例

《python使用库爬取m3u8文件的示例》本文主要介绍了python使用库爬取m3u8文件的示例,可以使用requests、m3u8、ffmpeg等库,实现获取、解析、下载视频片段并合并等步骤,具有... 目录一、准备工作二、获取m3u8文件内容三、解析m3u8文件四、下载视频片段五、合并视频片段六、错误

gitlab安装及邮箱配置和常用使用方式

《gitlab安装及邮箱配置和常用使用方式》:本文主要介绍gitlab安装及邮箱配置和常用使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1.安装GitLab2.配置GitLab邮件服务3.GitLab的账号注册邮箱验证及其分组4.gitlab分支和标签的

SpringBoot3应用中集成和使用Spring Retry的实践记录

《SpringBoot3应用中集成和使用SpringRetry的实践记录》SpringRetry为SpringBoot3提供重试机制,支持注解和编程式两种方式,可配置重试策略与监听器,适用于临时性故... 目录1. 简介2. 环境准备3. 使用方式3.1 注解方式 基础使用自定义重试策略失败恢复机制注意事项

nginx启动命令和默认配置文件的使用

《nginx启动命令和默认配置文件的使用》:本文主要介绍nginx启动命令和默认配置文件的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录常见命令nginx.conf配置文件location匹配规则图片服务器总结常见命令# 默认配置文件启动./nginx

在Windows上使用qemu安装ubuntu24.04服务器的详细指南

《在Windows上使用qemu安装ubuntu24.04服务器的详细指南》本文介绍了在Windows上使用QEMU安装Ubuntu24.04的全流程:安装QEMU、准备ISO镜像、创建虚拟磁盘、配置... 目录1. 安装QEMU环境2. 准备Ubuntu 24.04镜像3. 启动QEMU安装Ubuntu4

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

Windows下C++使用SQLitede的操作过程

《Windows下C++使用SQLitede的操作过程》本文介绍了Windows下C++使用SQLite的安装配置、CppSQLite库封装优势、核心功能(如数据库连接、事务管理)、跨平台支持及性能优... 目录Windows下C++使用SQLite1、安装2、代码示例CppSQLite:C++轻松操作SQ

一文详解MySQL如何设置自动备份任务

《一文详解MySQL如何设置自动备份任务》设置自动备份任务可以确保你的数据库定期备份,防止数据丢失,下面我们就来详细介绍一下如何使用Bash脚本和Cron任务在Linux系统上设置MySQL数据库的自... 目录1. 编写备份脚本1.1 创建并编辑备份脚本1.2 给予脚本执行权限2. 设置 Cron 任务2

Python常用命令提示符使用方法详解

《Python常用命令提示符使用方法详解》在学习python的过程中,我们需要用到命令提示符(CMD)进行环境的配置,:本文主要介绍Python常用命令提示符使用方法的相关资料,文中通过代码介绍的... 目录一、python环境基础命令【Windows】1、检查Python是否安装2、 查看Python的安