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

相关文章

基于Python Playwright进行前端性能测试的脚本实现

《基于PythonPlaywright进行前端性能测试的脚本实现》在当今Web应用开发中,性能优化是提升用户体验的关键因素之一,本文将介绍如何使用Playwright构建一个自动化性能测试工具,希望... 目录引言工具概述整体架构核心实现解析1. 浏览器初始化2. 性能数据收集3. 资源分析4. 关键性能指

使用Redis快速实现共享Session登录的详细步骤

《使用Redis快速实现共享Session登录的详细步骤》在Web开发中,Session通常用于存储用户的会话信息,允许用户在多个页面之间保持登录状态,Redis是一个开源的高性能键值数据库,广泛用于... 目录前言实现原理:步骤:使用Redis实现共享Session登录1. 引入Redis依赖2. 配置R

使用Python的requests库调用API接口的详细步骤

《使用Python的requests库调用API接口的详细步骤》使用Python的requests库调用API接口是开发中最常用的方式之一,它简化了HTTP请求的处理流程,以下是详细步骤和实战示例,涵... 目录一、准备工作:安装 requests 库二、基本调用流程(以 RESTful API 为例)1.

Nginx进行平滑升级的实战指南(不中断服务版本更新)

《Nginx进行平滑升级的实战指南(不中断服务版本更新)》Nginx的平滑升级(也称为热升级)是一种在不停止服务的情况下更新Nginx版本或添加模块的方法,这种升级方式确保了服务的高可用性,避免了因升... 目录一.下载并编译新版Nginx1.下载解压2.编译二.替换可执行文件,并平滑升级1.替换可执行文件

Python标准库datetime模块日期和时间数据类型解读

《Python标准库datetime模块日期和时间数据类型解读》文章介绍Python中datetime模块的date、time、datetime类,用于处理日期、时间及日期时间结合体,通过属性获取时间... 目录Datetime常用类日期date类型使用时间 time 类型使用日期和时间的结合体–日期时间(

使用Python开发一个Ditto剪贴板数据导出工具

《使用Python开发一个Ditto剪贴板数据导出工具》在日常工作中,我们经常需要处理大量的剪贴板数据,下面将介绍如何使用Python的wxPython库开发一个图形化工具,实现从Ditto数据库中读... 目录前言运行结果项目需求分析技术选型核心功能实现1. Ditto数据库结构分析2. 数据库自动定位3

Python yield与yield from的简单使用方式

《Pythonyield与yieldfrom的简单使用方式》生成器通过yield定义,可在处理I/O时暂停执行并返回部分结果,待其他任务完成后继续,yieldfrom用于将一个生成器的值传递给另一... 目录python yield与yield from的使用代码结构总结Python yield与yield

Go语言使用select监听多个channel的示例详解

《Go语言使用select监听多个channel的示例详解》本文将聚焦Go并发中的一个强力工具,select,这篇文章将通过实际案例学习如何优雅地监听多个Channel,实现多任务处理、超时控制和非阻... 目录一、前言:为什么要使用select二、实战目标三、案例代码:监听两个任务结果和超时四、运行示例五

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的