6

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

文档状态:

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

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

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

java.lang.RuntimeException: Unable to create BackupAgent org.transdroid.service.BackupAgent: java.lang.ClassCastException: org.transdroid.service.BackupAgent

这是我向后兼容的方法BackupAgenthttp://code.google.com/p/transdroid/source/browse/#svn/trunk/src/org/transdroid/service其中 BackupAgent.java 是“常规” BackupAgentHelper -扩展类和 BackupAgentHelperWrapper 是基于反射的包装类。

有谁成功实现了BackupAgent向后兼容?

4

6 回答 6

10

作为替代方案,您可以只使用纯反射与 BackupManager 对话:

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 之前的 VM 上,因此不会有任何链接问题。

于 2010-12-17T07:34:13.053 回答
7

我不明白你为什么会遇到这个问题。

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

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

 public class MyBackupAgentHelper extends BackupAgentHelper {
 @override onCreate()
 { 
       \\do something usefull
 }

但是,您确实希望运行备份,为此您需要在BackupManager.dataChanged()数据更改时调用并通知系统进行备份(使用您的BackupAgentor BackupAgentHelper)。

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


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每次想要模拟实际系统启动的备份过程时都调用它,则它应该在您重新安装应用程序时正确备份和恢复。)

于 2010-10-17T12:54:58.750 回答
1

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

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

并将构建目标设置为 sdk 8(eclipse '.default.properties' 中的项目属性):

# Project target.
target=android-8

现在要调用 SDK 8 中添加的新内容,您必须使用反射:http: //developer.android.com/resources/articles/backward-compatibility.html

于 2010-06-21T10:30:37.600 回答
1

我遇到了同样的问题,这就是我为解决它所做的工作。

您不使用包装器扩展 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"
    >

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

于 2010-06-25T16:03:32.487 回答
1

怎么样

    if (android.os.Build.VERSION.SDK_INT >= 8) 
    {
        BackupManager bm = new BackupManager(this);
        bm.dataChanged();
    }
于 2013-10-02T09:24:51.927 回答
0

不只是调用 BackupManager.dataChanged,请先检查该类是否存在。

   try {
            Class.forName("android.app.backup.BackupManager");
            BackupManager.dataChanged(context.getPackageName());
        } catch (ClassNotFoundException e) {
        }
于 2012-04-13T11:21:25.507 回答