向后兼容的BackupAgent

发布于 2024-09-06 17:36:27 字数 1220 浏览 4 评论 0原文

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

文档指出:

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

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

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

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

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

有人成功实现了具有向后兼容性的 BackupAgent 吗?

I am looking into using the new Backup API that available since Android 2.2, but need to maintain backwards compatibility (to 1.5 to be exact).

The docs state:

The backup service and the APIs you must use are available only on devices running API Level 8 (Android 2.2) or greater, so you should also set your android:minSdkVersion attribute to "8". However, if you implement proper backward compatibility in your application, you can support this feature for devices running API Level 8 or greater, while remaining compatible with older devices.

I indeed build against the level 8 targetSdkVersion with level 3 minSdkVersion and try to use a wrapper class (with reflection) to overcome the problem that the application will not run if you implement a class that extends an nonexisting class.

Here is the problem: since we don't make actual calls to the BackupHelper class ourselves, we can't check upfront if the class indeed exists. (As is explained in the Android Backwards Compatibility documentation with a checkAvailable() method.) The class will therefore be instantiated and cast to a BackupAgent. But since we use reflection, it doesn't actually override BackupAgent and an exception occurs at runtime when the backup is requested:

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

Here is my approach to a backwards compatible BackupAgent: http://code.google.com/p/transdroid/source/browse/#svn/trunk/src/org/transdroid/service where the BackupAgent.java is the 'regular' BackupAgentHelper-extending class and BackupAgentHelperWrapper is the reflection-based wrapper class.

Anyone successfull in implementing a BackupAgent with backwards compatibility?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

半世蒼涼 2024-09-13 17:36:27

作为替代方案,您可以只使用纯反射来与 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 上,因此不会出现任何链接问题。

As an alternative, you can just use pure reflection to talk to the 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();
    }
}

Point the android:backupAgent straight at a v2.2 class; it will never be loaded on a pre-v2.2 VM, so there won't be any linkage problems.

逐鹿 2024-09-13 17:36:27

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

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

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

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

但是,你确实想要运行备份,为此你需要调用 BackupManager.dataChanged() 每当您的数据发生变化并且您想要通知系统对其进行备份(使用您的 BackupAgentBackupAgentHelper)。

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


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,那么当您重新安装应用程序时,它应该正确备份和恢复。)

I don't see why you run into this problem.

I have the same issue: I want to support backup with a app that supports also 1.5 (API 3).

There is no problem in creating my BackupAgentHelper class, since that class is never called from my own code, but from the BackupManager i.e. the system itself. Therefore I don't need to wrap it, and I don't see why you should be doing that:

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

However, you do want to get a backup running, to do that you need to call on BackupManager.dataChanged() whenever your data changes and you want to inform the system to backup it (using your BackupAgent or BackupAgentHelper).

You do need to wrap that class, since you call it from you application code.


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);
}

}

You then call it from your code when you change a preference or save some data.
Some code from my app:


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.");
        }

So, hopefully this works for you!

(If you call adb shell bmgr run every time you want to emulate the actual system initiated backupprocess it should properly backup and restore when you reinstall the app.)

私藏温柔 2024-09-13 17:36:27

您需要将 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

You need to set the minSDK version to the following:

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

and setting the build target to sdk 8 (project properties in eclipse '.default.properties'):

# Project target.
target=android-8

Now to call new stuff added in SDK 8 you have to use reflection: http://developer.android.com/resources/articles/backward-compatibility.html

酸甜透明夹心 2024-09-13 17:36:27

我遇到了同样的问题,这就是我为解决该问题所做的事情。

您不使用包装器来扩展 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,因此代码永远不会被调用,因此您也不会遇到任何运行时异常。

I ran into the same problem and here's what I did to work it out.

You don't extend BackupAgent with the wrapper, you extend it with the wrapped class. So you make your real backup class:

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

}

Okay, and then you make a wrapper like the android developer backwards compatibility article said to do. Note that this class does not extend 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();
}

}

Finally, in your manifest, you declare the wrapper as your backup agent:

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

Since your wrapper has the proper methods defined you won't run into a problem when the backup manager casts it to a BackupAgent. Since lower API levels won't have a BackupManager the code will never get called, so you won't run into any runtime exceptions there either.

最终幸福 2024-09-13 17:36:27

怎么样

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

How about

    if (android.os.Build.VERSION.SDK_INT >= 8) 
    {
        BackupManager bm = new BackupManager(this);
        bm.dataChanged();
    }
蓝咒 2024-09-13 17:36:27

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

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

Insted of just calling BackupManager.dataChanged, check if the class exists first.

   try {
            Class.forName("android.app.backup.BackupManager");
            BackupManager.dataChanged(context.getPackageName());
        } catch (ClassNotFoundException e) {
        }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文