Back-compatible BackupAgent

I am exploring the use of the new backup API available with Android 2.2, but it needs to support backward compatibility (up to 1.5, to be precise).

Status of documents:

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

I really build against level 8 of targetSdkVersion with level 3 minSdkVersion and try to use a wrapper class (with reflection) to overcome the problem that the application will not execute if you implement a class that extends a nonexistent class.

Here's the problem: since we ourselves do not make actual calls in the BackupHelper class, we cannot check upfront if the class really exists. (As explained in the Android backward compatibility documentation using the checkAvailable() method.) In this way, the class will be created and added to BackupAgent . But since we use reflection, it does not actually cancel BackupAgent, and an exception occurs at runtime when a 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 backward-compatible BackupAgent : http://code.google.com/p/transdroid/source/browse/#svn/trunk/src/org/transdroid/service , where BackupAgent.java is the "normal" BackupAgentHelper- archive the extension class and BackupAgentHelperWrapper are a reflection-based wrapper class.

Does anyone successfully implement BackupAgent with backward compatibility?

+6
android reflection backwards-compatibility android-backup-service
source share
6 answers

I do not understand why you ran into this problem.

I have the same problem: I want to support backup using an application that also supports 1.5 (API 3).

There is no problem creating my BackupAgentHelper class, since this class is never called from my own code, but from the BackupManager ie of the system itself. Therefore, I do not need to wrap it, and I do not understand why you should do this:

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

However, you want to start the backup to do this, you need to call BackupManager.dataChanged() whenever your data changes, and you want to inform the system about its backup (using BackupAgent or BackupAgentHelper ) ..

You need to wrap this class as you call it from your 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); } } 

Then you call it from your code when you change the preference or save some data. Some code from my application:

 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 will work for you!

(If you invoke adb shell bmgr run every time you want to emulate the actual system-initiated backup process, it should correctly back up and restore when you reinstall the application.)

+7
source share

Alternatively, you can simply use pure reflection to talk to 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(); } } 

Direct android: backupAgent directly to class v2.2; it will never be loaded on the pre-v2.2 VM, so there will be no binding problems.

+10
source share

You need to install the minSDK version as follows:

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

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

 # Project target. target=android-8 

Now, to invoke new material added in SDK 8, you must use reflection: http://developer.android.com/resources/articles/backward-compatibility.html

+1
source share

I ran into the same problem, and here is what I did to solve it.

You do not extend BackupAgent to the shell; you extend it with a wrapped class. Thus, you create 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 } 

Ok, and then you do the wrapper, as stated in the article on Android Developer Compatibility. 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 the manifest, you declare the wrapper as a backup agent:

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

Since your shell has certain methods, you will not encounter a problem when the backup manager passes it to BackupAgent. Since lower API levels will not have BackupManager, the code will never be called, so you will also not throw any exceptions at runtime.

+1
source share

Forcing a call to BackupManager.dataChanged, check if the first class exists.

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

What about

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

All Articles