6

Tôi đang xem xét việc sử dụng API sao lưu mới có sẵn từ Android 2.2, nhưng cần phải duy trì tính tương thích ngược (chính xác đến 1,5).BackupAgent tương thích ngược

Các tài liệu nhà nước:

Các dịch vụ sao lưu và các API bạn phải sử dụng là chỉ có sẵn trên các thiết bị chạy API Cấp 8 (Android 2.2) hoặc cao hơn, vì vậy bạn cũng nên thiết lập Android của bạn: thuộc tính minSdkVersion phải "số 8". Tuy nhiên, nếu bạn triển khai tính tương thích ngược thích hợp trong ứng dụng của mình, bạn có thể hỗ trợ tính năng này cho các thiết bị chạy API cấp 8 trở lên, trong khi vẫn tương thích với các thiết bị cũ hơn.

Tôi thực sự xây dựng so với mức 8 targetSdkVersion với mức 3 minSdkVersion và cố gắng sử dụng một lớp wrapper (với suy nghĩ) để khắc phục những vấn đề mà ứng dụng sẽ không chạy nếu bạn thực hiện một lớp học kéo dài một lớp nonexisting.

Đây là vấn đề: vì chúng tôi không thực hiện cuộc gọi thực tế đến lớp BackupHelper, chúng tôi không thể kiểm tra trả trước nếu lớp thực sự tồn tại. (Như được giải thích trong tài liệu Tương thích ngược của Android với phương thức checkAvailable().) Do đó, lớp sẽ được khởi tạo và truyền tới một số BackupAgent. Nhưng kể từ khi chúng tôi sử dụng phản chiếu, nó không thực sự ghi đè BackupAgent và một ngoại lệ xảy ra trong thời gian chạy khi sao lưu được yêu cầu:

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

Đây là cách tiếp cận của tôi đến một tương thích ngược BackupAgent: http://code.google.com/p/transdroid/source/browse/#svn/trunk/src/org/transdroid/service nơi BackupAgent.java là Lớp 'BackupAgentHelper' mở rộng và BackupAgentHelperWrapper 'thường xuyên' là lớp trình bao bọc dựa trên sự phản chiếu.

Bất kỳ ai thành công trong việc triển khai BackupAgent với tính tương thích ngược?

+0

Tôi tin rằng đã có một cuộc nói chuyện về việc này tại Barcamp năm nay tại Droidcon. Không thể nhớ tên của người đó, nhưng đáng để tìm kiếm. –

Trả lời

7

Tôi không hiểu tại sao bạn gặp sự cố này.

Tôi có cùng sự cố: Tôi muốn hỗ trợ sao lưu bằng ứng dụng hỗ trợ 1,5 (API 3).

Không có vấn đề gì trong việc tạo lớp BackupAgentHelper của tôi, vì lớp đó không bao giờ được gọi từ mã của riêng tôi, nhưng từ số BackupManager tức là bản thân hệ thống. Vì vậy, tôi không cần phải quấn nó, và tôi không thấy lý do tại sao bạn nên làm điều đó:

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

Tuy nhiên, bạn muốn nhận được một bản sao lưu chạy, để làm điều đó bạn cần phải gọi vào BackupManager.dataChanged() bất cứ khi nào dữ liệu của bạn thay đổi và bạn muốn thông báo cho hệ thống sao lưu nó (sử dụng số BackupAgent hoặc BackupAgentHelper) của bạn.

Bạn cần phải bọc lớp đó, vì bạn gọi nó từ mã ứng dụng của bạn.


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

} 

Sau đó bạn gọi từ mã của bạn khi bạn thay đổi tùy chọn hoặc lưu một số dữ liệu. Một số mã từ ứng dụng của tôi:


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

Vì vậy, hy vọng điều này phù hợp với bạn!

(Nếu bạn gọi adb shell bmgr run mỗi khi bạn muốn bắt chước hệ thống thực tế được khởi xướng backupprocess cần đúng cách sao lưu và khôi phục khi bạn cài đặt lại ứng dụng.)

+0

Có vẻ đầy hứa hẹn và tôi sẽ thử. –

+0

Bạn hoàn toàn đúng: điều này đã làm các trick! Các tác nhân sao lưu có thể là một lớp thường xuyên mở rộng BackupAgentHelper và nó là cuộc gọi đến dataChanged để bọc. Do đó, chúng tôi thực hiện một wrapper xung quanh BackupManager. Bạn có thể tìm thấy mỏ tại http://code.google.com/p/transdroid/source/browse/trunk/src/org/transdroid/service/BackupManagerWrapper.java –

+0

Liên kết chết - những rủi ro khi đăng liên kết lên các dự án trực tiếp trên code.google.com = ( –

1

Bạn cần phải thiết lập các phiên bản minSDK như sau:

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

và thiết lập mục tiêu xây dựng để sdk 8 (các thuộc tính dự án trong eclipse' .default.properties'):

# Project target. 
target=android-8 

Bây giờ để gọi công cụ mới được thêm vào SDK 8, bạn phải sử dụng phản ánh: http://developer.android.com/resources/articles/backward-compatibility.html

+0

Tôi có thể không đủ rõ ràng (tôi đã chỉnh sửa câu trả lời của tôi để nói rõ ràng), nhưng đó chính xác là những gì tôi đang cố gắng. Vấn đề là lớp wrapper không thể mở rộng BackupAgent (điều này sẽ không biên dịch với 1.5) nhưng đang được tạo ra cho một BackupAgent bởi cơ chế sao lưu Android. –

+0

Ok, tôi nghĩ bạn sẽ có thể mở rộng một lớp bằng cách sử dụng sự phản chiếu trong java: http://stackoverflow.com/questions/1886785/how-do-i-extend-java-classes-by-reflection – Moss

1

Tôi đã gặp phải vấn đề tương tự và đây là những gì tôi đã làm để giải quyết vấn đề.

Bạn không mở rộng BackupAgent bằng trình bao bọc, bạn mở rộng nó bằng lớp bọc. Vì vậy, bạn thực hiện thực lớp sao lưu của bạn:

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 

} 

Được rồi, và sau đó bạn thực hiện một wrapper như các nhà phát triển Android ngược bài viết tương thích cho biết phải làm gì.Lưu ý rằng lớp này không mở rộng 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(); 
} 

}

Cuối cùng, trong biểu hiện của bạn, bạn khai báo các wrapper làm đại lý sao lưu của bạn:

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

Kể từ wrapper của bạn có phương pháp thích hợp được xác định bạn sẽ không chạy vào một vấn đề khi người quản lý sao lưu phôi nó vào một BackupAgent. Vì các mức API thấp hơn sẽ không có một BackupManager, mã sẽ không bao giờ được gọi, vì vậy bạn sẽ không chạy vào bất kỳ ngoại lệ thời gian chạy nào ở đó.

+0

Cảm ơn đề xuất của bạn . Đó là bản chất những gì tôi đã làm bản thân mình. Trình bao bọc của tôi cũng có các phương thức cần thiết được định nghĩa. Vì gỡ lỗi quá trình trên trình mô phỏng hoặc thiết bị thực không có vẻ hoạt động với công cụ bmgr, thật khó để thiết lập những gì/khi nó không hoạt động. Tôi nghĩ rằng tôi đã có một lỗi trong wrapper của tôi, kể từ khi tôi đã thiếu nhà xây dựng thích hợp. Hy vọng rằng nó hoạt động ngay bây giờ, nhưng tôi sẽ báo cáo lại sớm về điều này. –

10

Là một thay thế, bạn chỉ có thể sử dụng phản ánh tinh khiết để nói chuyện với Trình sao lưu:

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

Chỉ android: backupTrực tiếp ngay tại lớp v2.2; nó sẽ không bao giờ được tải trên một VM phiên bản trước, vì vậy sẽ không có bất kỳ vấn đề liên kết nào.

+0

Một ví dụ tuyệt vời về cách làm tương thích ngược trong Android với sự phản ánh. Cảm ơn bạn đã đăng bài này. – Zulaxia

0

Được gọi là BackupManager.dataChanged, kiểm tra xem lớp có tồn tại trước không.

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

Làm thế nào về

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