资讯专栏INFORMATION COLUMN

Android Data Backup

赵连江 / 1684人阅读

摘要:定义你的扩展重写扩展需要使用多个对象自动完成备份及恢复。在标签中声明默认值为设为则会在该应用的任何版本恢复数据。不同的设备对的支持不同。非线程安全,需要手动加锁恢复数据前会检查字段,如果待恢复的版本高于当前应用版本,不会调用来恢复数据。

The Basics

为了备份应用数据,你需要实现一个 backup agent. 它用来为BackupManager 提供你想要备份的数据,在你重新安装应用时恢复数据。BackupManager 通过backup transport 处理和云存储相关的数据业务,通过你的backup agent 处理设备上的数据相关的业务。

BackupAgent实现步骤:

在 manifest 文件中声明 android:backupAgent 属性。

通过backup服务注册你的应用。谷歌为大多数 Android 设备提供了 Android Backup Service,你需要通过这个服务注册你的应用。其他的云服务也需要注册。

定义你的 backup agent:

扩展 BackupAgent
重写 onBackup() onRestore()

扩展 BackupAgentHelper
需要使用多个helper对象自动完成备份及恢复。
会备份整个文件。

Declaring the Backup Agent in Your Manifest

application标签中声明android:backupAgent.


    ...
    
        
            ...
        
    

android:restoreAnyVersion
默认值为false, 设为true 则会在该应用的任何版本恢复数据。

Registering for Android Backup Service

不同的设备对 backup 的支持不同。


    ...
    

Extending BackupAgent

一般情况下,因为BackupAgentHelperBackupAgent更方便,所以尽可能使用前者。
然而,如果有以下需求,还是要通过BackupAgent来实现:

对数据进行版本控制

并不想备份整个数据文件

备份数据库中的数据

如果你想要备份SharedPreferences内部存储中的整个文件,简单地使用BackupAgentHelper即可。

Performing backup

oldState
上一次备份的状态,主要是FileDescriptor

// Get the oldState input stream
FileInputStream instream = new FileInputStream(oldState.getFileDescriptor());
DataInputStream in = new DataInputStream(instream);

try {
    // Get the last modified timestamp from the state file and data file
    long stateModified = in.readLong();
    long fileModified = mDataFile.lastModified();

    if (stateModified != fileModified) {
        // The file has been modified, so do a backup
        // Or the time on the device changed, so be safe and do a backup
    } else {
        // Don"t back up because the file hasn"t changed
        return;
    }
} catch (IOException e) {
    // Unable to read state file... be safe and do a backup
}

data
要备份的数据,通过byte字节流的方式存储

// Create buffer stream and data output stream for our data
ByteArrayOutputStream bufStream = new ByteArrayOutputStream();
DataOutputStream outWriter = new DataOutputStream(bufStream);
// Write structured data
outWriter.writeUTF(mPlayerName);
outWriter.writeInt(mPlayerScore);
// Send the data to the Backup Manager via the BackupDataOutput
byte[] buffer = bufStream.toByteArray();
int len = buffer.length;
data.writeEntityHeader(TOPSCORE_BACKUP_KEY, len);
data.writeEntityData(buffer, len);

newState
本次备份的状态,作为下次备份时的oldState

Performing restore

data

用来恢复数据的 `BackupDataInput`.

appVersionCode

android:versionCode

newState
恢复后,需要把data里的ParcelFileDescriptor 写到newState中,它会以下次onBackupoldState被用到。

@Override
public void onRestore(BackupDataInput data, int appVersionCode,
                      ParcelFileDescriptor newState) throws IOException {
    // There should be only one entity, but the safest
    // way to consume it is using a while loop
    while (data.readNextHeader()) {
        String key = data.getKey();
        int dataSize = data.getDataSize();

        // If the key is ours (for saving top score). Note this key was used when
        // we wrote the backup entity header
        if (TOPSCORE_BACKUP_KEY.equals(key)) {
            // Create an input stream for the BackupDataInput
            byte[] dataBuf = new byte[dataSize];
            data.readEntityData(dataBuf, 0, dataSize);
            ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf);
            DataInputStream in = new DataInputStream(baStream);

            // Read the player name and score from the backup data
            mPlayerName = in.readUTF();
            mPlayerScore = in.readInt();

            // Record the score on the device (to a file or something)
            recordScore(mPlayerName, mPlayerScore);
        } else {
            // We don"t know this entity key. Skip it. (Shouldn"t happen.)
            data.skipEntityData();
        }
    }

    // Finally, write to the state blob (newState) that describes the restored data
    FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor());
    DataOutputStream out = new DataOutputStream(outstream);
    out.writeUTF(mPlayerName);
    out.writeInt(mPlayerScore);
}
Extending BackupAgentHelper

SharedPreferencesBackupHelper

FileBackupHelper

Backing up SharedPreferences
public class MyPrefsBackupAgent extends BackupAgentHelper {
    // The name of the SharedPreferences file
    static final String PREFS = "user_preferences";

    // A key to uniquely identify the set of backup data
    static final String PREFS_BACKUP_KEY = "prefs";

    // Allocate a helper and add it to the backup agent
    @Override
    public void onCreate() {
        SharedPreferencesBackupHelper helper =
                new SharedPreferencesBackupHelper(this, PREFS);
        addHelper(PREFS_BACKUP_KEY, helper);
    }
}

Note: SharedPreferences are threadsafe, so you can safely read and write the shared preferences file from your backup agent and other activities.

Backing up other files
public class MyFileBackupAgent extends BackupAgentHelper {
    // The name of the file
    static final String TOP_SCORES = "scores";
    static final String PLAYER_STATS = "stats";

    // A key to uniquely identify the set of backup data
    static final String FILES_BACKUP_KEY = "myfiles";

    // Allocate a helper and add it to the backup agent
    @Override
    public void onCreate() {
        FileBackupHelper helper = new FileBackupHelper(this,
                TOP_SCORES, PLAYER_STATS);
        addHelper(FILES_BACKUP_KEY, helper);
    }
}

非线程安全,需要手动加锁

// Object for intrinsic lock
static final Object sDataLock = new Object();
try {
    synchronized (MyActivity.sDataLock) {
        File dataFile = new File(getFilesDir(), TOP_SCORES);
        RandomAccessFile raFile = new RandomAccessFile(dataFile, "rw");
        raFile.writeInt(score);
    }
} catch (IOException e) {
    Log.e(TAG, "Unable to write to file");
}

@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
          ParcelFileDescriptor newState) throws IOException {
    // Hold the lock while the FileBackupHelper performs backup
    synchronized (MyActivity.sDataLock) {
        super.onBackup(oldState, data, newState);
    }
}

@Override
public void onRestore(BackupDataInput data, int appVersionCode,
        ParcelFileDescriptor newState) throws IOException {
    // Hold the lock while the FileBackupHelper restores the file
    synchronized (MyActivity.sDataLock) {
        super.onRestore(data, appVersionCode, newState);
    }
}
Checking the Restore Data Version

android:versionCode
BackupManager恢复数据前会检查versionCode字段,如果待恢复的版本高于当前应用版本,BackupManager不会调用onRestore()来恢复数据。
android:restoreAnyVersion
设定trueorfalse指明你是否希望通过应用的版本来决定是否恢复数据。设为true,那么BackupManager将不会去检查android:versionCode,而是调用你的onRestore()方法。
你可以通过下面的方法来获取appVersionCode.

PackageInfo info;
try {
    String name = getPackageName();
    info = getPackageManager().getPackageInfo(name,0);
} catch (NameNotFoundException nnfe) {
    info = null;
}

int version;
if (info != null) {
    version = info.versionCode;
}
Requesting Backup

使用dataChanged()来请求备份,这个不会立即执行,会在未来某个合适的时间内执行

开发时使用bmgr工具来随时备份

Requesting Restore

自动检测备份数据

requestRestore()

bmgr工具

Testing Your Backup Agent

安装应用

确定备份功能可用

使用模拟器
adb shell bmgr enable true

If using a device, open the system Settings, select Backup & reset, then enable Back up my data and Automatic restore.

在用户改变数据的地方调用dataChanged()
然后执行:adb shell bmgr backup your.package.name

初始化备份操作

adb shell bmgr run

卸载应用

adb uninstall your.package.name

重新安装

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/12739.html

相关文章

  • android常用名词收集

    摘要:以后你就能再次用这个备份来将手机恢复到这个状态。这个词语来自的用户,享有最高权限。通常不要去手机,如果操作不当,对手机系统造成损坏,可能会开不了机等。而一般是用来初始安装的首选途径。此外模式下还能解锁。 持续学习,持续更新...... AOSP AOSP stands for Android Open Source Project. Stock Stock Android is wh...

    bingchen 评论0 收藏0
  • 几条Nginx配置小技巧

    摘要:多台的反向代理通常一个的部署方式,比如就是启动一个开多个,或者用等多线程的。这样来充分利用服务器资源,不过一旦这台服务器挂了,有个便是极好的。串接做用串一个可以实现一个简单的方案完成规则划分,要写一些代码不需要所有流量都要走匹配规则。 多台app server的反向代理 upstream prod { server 192.168.1.1:6001; s...

    eternalshallow 评论0 收藏0
  • Android项目安全注意事项和解决方案

    摘要:还有就是项目发布后项目中一些数据安全,等方面,都值得我们考虑,思索。官方文档安全说明及其以上系统提供了为应用程序数据的备份和恢复功能,此功能的开关决定于该应用程序中文件中的属性值,其属性值默认是。解决方案自己进行数据加密。 在个人和公司开发的项目发布后,作为软件工程师最担心的就是代码安全(虽说现今而言,技术已没有什么秘密,聪明人很多,你的功能可能别人稍加研究就能不看代码也能实现(黑科技...

    laznrbfe 评论0 收藏0
  • Android项目安全注意事项和解决方案

    摘要:还有就是项目发布后项目中一些数据安全,等方面,都值得我们考虑,思索。官方文档安全说明及其以上系统提供了为应用程序数据的备份和恢复功能,此功能的开关决定于该应用程序中文件中的属性值,其属性值默认是。解决方案自己进行数据加密。 在个人和公司开发的项目发布后,作为软件工程师最担心的就是代码安全(虽说现今而言,技术已没有什么秘密,聪明人很多,你的功能可能别人稍加研究就能不看代码也能实现(黑科技...

    WalkerXu 评论0 收藏0
  • 小白解密安卓机上微信聊天记录

    摘要:安卓小白,出于调研如何迁移微信聊天记录而去破解了一下安卓机的微信聊天记录数据库文件,这个破解方式年就已经有人发表了文章,有兴趣可以去网上搜一搜,这篇文章主要是记录学习历程以及遇到的问题。 安卓小白,出于调研如何迁移微信聊天记录而去破解了一下安卓机的微信聊天记录数据库文件,这个破解方式2013年就已经有人发表了文章,有兴趣可以去网上搜一搜,这篇文章主要是记录学习历程以及遇到的问题。 具体...

    rozbo 评论0 收藏0

发表评论

0条评论

赵连江

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<