Android 向后兼容备份代理

Android 向后兼容备份代理,android,reflection,backwards-compatibility,android-backup-service,Android,Reflection,Backwards Compatibility,Android Backup Service,我正在考虑使用自Android 2.2以来提供的新备份API,但需要保持向后兼容性(确切地说是1.5) 文件规定: 备份服务和您必须使用的API仅在运行API级别8(Android 2.2)或更高版本的设备上可用,因此您还应将Android:minSdkVersion属性设置为“8”。但是,如果在应用程序中实现了适当的向后兼容性,则可以为运行API级别8或更高的设备支持此功能,同时保持与旧设备的兼容性 事实上,我使用级别3minSdkVersion构建了级别8targetSdkVersion,并

我正在考虑使用自Android 2.2以来提供的新备份API,但需要保持向后兼容性(确切地说是1.5)

文件规定:

备份服务和您必须使用的API仅在运行API级别8(Android 2.2)或更高版本的设备上可用,因此您还应将Android:minSdkVersion属性设置为“8”。但是,如果在应用程序中实现了适当的向后兼容性,则可以为运行API级别8或更高的设备支持此功能,同时保持与旧设备的兼容性

事实上,我使用级别3
minSdkVersion
构建了级别8
targetSdkVersion
,并尝试使用包装类(带反射)来克服如果实现扩展不存在的类的类,应用程序将无法运行的问题

问题是:因为我们自己没有实际调用
backupulper
类,所以我们无法预先检查该类是否确实存在。(正如Android向后兼容性文档中使用
checkAvailable()
方法所解释的那样。)因此,该类将被实例化并转换为
BackupAgent
。但由于我们使用反射,因此它实际上不会覆盖BackupAgent,并且在请求备份时会在运行时发生异常:

java.lang.RuntimeException: Unable to create BackupAgent org.transdroid.service.BackupAgent: java.lang.ClassCastException: org.transdroid.service.BackupAgent
下面是我实现向后兼容的
BackupAgent
:其中BackupAgent.java是“常规”BackupAgentHelper扩展类,BackupAgentHelperWrapper是基于反射的包装类


是否有人成功实现了向后兼容的备份代理?

您需要将minSDK版本设置为以下版本:

<uses-sdk android:minSdkVersion="3" android:targetSdkVersion="8"/>

现在要调用SDK 8中添加的新东西,您必须使用反射:

我遇到了相同的问题,下面是我解决这个问题的方法

您不使用包装器扩展BackupAgent,而是使用包装类扩展它。因此,您可以创建真正的备份类:

public class MyBackup extends BackupAgent {

@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
        ParcelFileDescriptor newState) throws IOException {
    // TODO Auto-generated method stub

}

@Override
public void onRestore(BackupDataInput data, int appVersionCode,
        ParcelFileDescriptor newState) throws IOException {
    // TODO Auto-generated method stub

}
好的,然后你制作一个包装器,就像android开发者向后兼容性文章所说的那样。请注意,此类不扩展BackupAgent:

public class WrapMyBackup {
private MyBackup wb;

static {
    try {
        Class.forName("MyBackup");
    }
    catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

/** call this wrapped in a try/catch to see if we can instantiate **/
public static void checkAvailable() {}

public WrapMyBackup() {
    wb = new MyBackup();
}

public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
        ParcelFileDescriptor newState) throws IOException {
    wb.onBackup(oldState, data, newState);

}

public void onRestore(BackupDataInput data, int appVersionCode,
        ParcelFileDescriptor newState) throws IOException {
    wb.onRestore(data, appVersionCode, newState);

}

public void onCreate() {
    wb.onCreate();
}

public void onDestroy() {
    wb.onDestroy();
}
}

最后,在清单中,您将包装器声明为备份代理:

    <application 
    android:label="@string/app_name"
    android:icon="@drawable/ic_launch_scale"
    android:backupAgent="WrapMyBackup"
    >


因为您的包装器定义了正确的方法,所以当备份管理器将其强制转换到备份代理时,您不会遇到问题。由于较低的API级别没有备份管理器,因此代码永远不会被调用,因此您也不会遇到任何运行时异常。

我不明白您为什么会遇到此问题

我也有同样的问题:我想用一个同样支持1.5(API 3)的应用程序来支持备份

创建我的
BackupAgentHelper
类没有问题,因为该类从不从我自己的代码调用,而是从
BackupManager
即系统本身调用。因此,我不需要包装它,我不明白你为什么要这样做:

 public class MyBackupAgentHelper extends BackupAgentHelper {
 @override onCreate()
 { 
       \\do something usefull
 }
但是,您确实希望运行备份,要做到这一点,您需要在数据更改时调用
BackupManager.dataChanged()
,并通知系统进行备份(使用
BackupAgent
backupagentper

您确实需要包装该类,因为您可以从应用程序代码中调用它


public class WrapBackupManager {
private BackupManager wrappedInstance;

static 
{
    try
    {
        Class.forName("android.app.backup.BackupManager");
    }
    catch (Exception e)
    {
        throw new RuntimeException(e);
    }
}
public static void checkAvailable() {}

public void dataChanged()
{
    wrappedInstance.dataChanged();
}

public WrapBackupManager(Context context)
{
    wrappedInstance = new BackupManager(context);
}

}
然后,当您更改首选项或保存一些数据时,可以从代码中调用它。 我的应用程序中的一些代码:


private static Boolean backupManagerAvailable = null;

    private static void postCommitAction() {


        if (backupManagerAvailable == null) {
            try {
                WrapBackupManager.checkAvailable();
                backupManagerAvailable = true;
            } catch (Throwable t) {
                backupManagerAvailable = false;
            }
        }

        if (backupManagerAvailable == true) {
            Log.d("Fretter", "Backup Manager available, using it now.");
            WrapBackupManager wrapBackupManager = new WrapBackupManager(
                    FretterApplication.getApplication());
            wrapBackupManager.dataChanged();
        } else {
            Log.d("Fretter", "Backup Manager not available, not using it now.");
        }
所以,希望这对你有用


(如果每次您想要模拟实际系统启动的备份过程时都调用adb shell bmgr run,则在重新安装应用程序时,它应该正确备份和还原。)

作为替代方法,您可以使用纯反射与备份管理器对话:

public void scheduleBackup() {
    Log.d(TAG, "Scheduling backup");
    try {
        Class managerClass = Class.forName("android.app.backup.BackupManager");
        Constructor managerConstructor = managerClass.getConstructor(Context.class);
        Object manager = managerConstructor.newInstance(context);
        Method m = managerClass.getMethod("dataChanged");
        m.invoke(manager);
        Log.d(TAG, "Backup requested");
    } catch(ClassNotFoundException e) {
        Log.d(TAG, "No backup manager found");
    } catch(Throwable t) {
        Log.d(TAG, "Scheduling backup failed " + t);
        t.printStackTrace();
    }
}

将android:backupAgent直接指向v2.2类;它永远不会加载到v2.2之前的虚拟机上,因此不会出现任何链接问题。

与其只调用BackupManager.dataChanged,不如先检查该类是否存在

   try {
            Class.forName("android.app.backup.BackupManager");
            BackupManager.dataChanged(context.getPackageName());
        } catch (ClassNotFoundException e) {
        }
怎么样

    if (android.os.Build.VERSION.SDK_INT >= 8) 
    {
        BackupManager bm = new BackupManager(this);
        bm.dataChanged();
    }

我可能不够清楚(我编辑了我的答案以明确地陈述),但这正是我所尝试的。问题是包装器类无法扩展BackupAgent(这不会针对1.5进行编译),而是由Android备份机制转换为BackupAgent。好的,我认为您可以使用java中的反射来扩展一个类:我相信在今年的Droidcon Barcamp上有关于这样做的讨论。我记不起那个人的名字,但值得一试。谢谢你的建议。这基本上是我自己做的。我的包装器也定义了所需的方法。由于在仿真器或真实设备上调试流程似乎无法使用bmgr工具,因此很难确定什么/什么时候它不起作用。我想我的包装中有一个bug,因为我缺少正确的构造函数。希望它现在能工作,但我会很快报告。看起来很有希望,我会尝试一下。你完全正确:这成功了!备份代理可以是一个扩展BackupAgentHelper的常规类,它是对dataChanged的调用,改为wrap。因此,我们在BackupManager周围做了一个包装。我的问题可以在Dead link上找到——在code.google.com=上发布实时项目链接的风险(注意一些1.5手机在启动时会出现ValidationError,因为您的类扩展了Dalvik 1.5中不存在的类。这在1.6中得到了修复,而不是WrapBackupManager.checkAvailable(),难道我们不能只检查api版本吗?这是一个很好的例子,说明了如何在Android中使用反射实现向后兼容性。感谢您发布这篇文章。为什么这个解决方案没有投票权?检查api版本不是比使用反射更好吗?
    if (android.os.Build.VERSION.SDK_INT >= 8) 
    {
        BackupManager bm = new BackupManager(this);
        bm.dataChanged();
    }