Обратно-совместимый BackupAgent - PullRequest
6 голосов
/ 21 июня 2010

Я изучаю возможность использования нового API резервного копирования, доступного начиная с Android 2.2, но необходимо поддерживать обратную совместимость (точнее до 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 с обратной совместимостью?

Ответы [ 6 ]

10 голосов
/ 17 декабря 2010

В качестве альтернативы вы можете просто использовать чистое отражение для общения с 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();
    }
}

Направьте андроида: backupAgent прямо на класс v2.2; он никогда не будет загружен на виртуальной машине, предшествующей v2.2, поэтому проблем с подключением не будет.

7 голосов
/ 17 октября 2010

Я не понимаю, почему вы столкнулись с этой проблемой.

У меня та же проблема: я хочу поддержать резервное копирование с приложением, которое поддерживает также 1.5 (API 3).

Нет проблем в создании моего BackupAgentHelper класса, так как этот класс никогда не вызывается из моего собственного кода, а из BackupManager, то есть самой системы.Поэтому мне не нужно оборачивать это, и я не понимаю, почему вы должны это делать:

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

Однако вы хотите запустить резервное копирование, для этого вам нужно позвонитьна BackupManager.dataChanged() всякий раз, когда ваши данные изменяются, и вы хотите сообщить системе о резервном копировании (используя BackupAgent или 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 каждый раз, когда вы хотите эмулировать реальное инициированное системой резервное копирование, оно должно правильно выполнять резервное копирование и восстановление при переустановке приложения.)

1 голос
/ 25 июня 2010

Я столкнулся с той же проблемой, и вот что я сделал для ее решения.

Вы не расширяете 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, код никогда не будет вызываться, поэтому вы не столкнетесь ни с какими исключениями времени выполнения.

1 голос
/ 21 июня 2010

Вам необходимо установить следующую версию 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

0 голосов
/ 02 октября 2013

Как насчет

    if (android.os.Build.VERSION.SDK_INT >= 8) 
    {
        BackupManager bm = new BackupManager(this);
        bm.dataChanged();
    }
0 голосов
/ 13 апреля 2012

Вместо того, чтобы просто вызывать BackupManager.dataChanged, проверьте, существует ли класс первым.

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