Tại sao ContentResolver.requestSync không kích hoạt đồng bộ hóa?


112

Tôi đang cố gắng triển khai mẫu Bộ điều hợp đồng bộ hóa nhà cung cấp nội dung như đã thảo luận tại Google IO - slide 26. Nhà cung cấp nội dung của tôi đang hoạt động và đồng bộ hóa của tôi hoạt động khi tôi kích hoạt nó từ ứng dụng Dev Tools Sync Tester, tuy nhiên khi tôi gọi ContentResolver. requestSync (tài khoản, quyền hạn, gói) từ ContentProvider của tôi, đồng bộ hóa của tôi không bao giờ được kích hoạt.

ContentResolver.requestSync(
        account, 
        AUTHORITY, 
        new Bundle());

Chỉnh sửa - thêm đoạn mã tệp kê khai Xml tệp kê khai của tôi chứa:

<service
    android:name=".sync.SyncService"
    android:exported="true">
    <intent-filter>
        <action
            android:name="android.content.SyncAdapter" />
    </intent-filter>
    <meta-data android:name="android.content.SyncAdapter"
    android:resource="@xml/syncadapter" />
</service>

--Biên tập

Syncadapter.xml của tôi được liên kết với dịch vụ đồng bộ hóa của tôi chứa:

<?xml version="1.0" encoding="utf-8"?>
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"  
    android:contentAuthority="AUTHORITY"
    android:accountType="myaccounttype"
    android:supportsUploading="true"
/>

Không chắc chắn mã nào khác sẽ hữu ích. Tài khoản được chuyển tới requestSync thuộc "myaccounttype" và AUTHORITY được chuyển cho cuộc gọi khớp với xml bộ điều hợp syc của tôi.

ContentResolver.requestSync có phải là cách chính xác để yêu cầu đồng bộ hóa không? Có vẻ như công cụ kiểm tra đồng bộ liên kết trực tiếp với dịch vụ và các cuộc gọi bắt đầu đồng bộ hóa, nhưng điều đó có vẻ như nó đánh bại mục đích tích hợp với kiến ​​trúc đồng bộ.

Nếu đó là cách chính xác để yêu cầu đồng bộ hóa thì tại sao trình kiểm tra đồng bộ lại hoạt động, nhưng không phải lệnh gọi ContentResolver.requestSync của tôi? Có thứ gì tôi cần chuyển trong gói không?

Tôi đang thử nghiệm trong trình giả lập trên các thiết bị chạy 2.1 và 2.2.


3
Vấn đề của tôi là không đạt được điểm ngắt trong bộ điều hợp đồng bộ ... Sau đó, tôi nhận ra rằng mình đang cố gỡ lỗi một dịch vụ ... Hy vọng điều này sẽ giúp những người khác như tôi.
dangalg

2
Các điểm ngắt trong dịch vụ bộ điều hợp đồng bộ sẽ KHÔNG kích hoạt được. Đó là bởi vì dịch vụ bộ điều hợp đồng bộ chạy trong một quy trình riêng biệt. Đây là những gì @danglang đang ám chỉ. Xem thêm câu hỏi này: stackoverflow.com/questions/8559458/…
Konstantin Schubert

1
Trong trường hợp của tôi, việc xóa android:process=":sync"khỏi dịch vụ đồng bộ hóa, hãy để trình gỡ lỗi đạt được các điểm mấu chốt. Bản thân dịch vụ đồng bộ đã hoạt động trước đó, vì tôi có thể thấy thông báo nhật ký từ onPerformSyncphương pháp thay mặt cho một quy trình khác.
Sergey

Một nguyên nhân khác là do WiFi bị tắt, vì vậy nếu bạn đang cố đồng bộ hóa với dữ liệu giả mạo, hãy kiểm tra kết nối của mình.
Allan Veloso

Câu trả lời:


280

Tính năng gọi requestSync()sẽ chỉ hoạt động trên một cặp {Account, ContentAuthority} mà hệ thống đã biết. Ứng dụng của bạn cần phải thực hiện một số bước để cho Android biết rằng bạn có khả năng đồng bộ hóa một loại nội dung cụ thể bằng một loại tài khoản cụ thể. Nó thực hiện điều này trong AndroidManifest.

1. Thông báo cho Android biết rằng gói ứng dụng của bạn cung cấp đồng bộ hóa

Trước hết, trong AndroidManifest.xml, bạn phải khai báo rằng bạn có Dịch vụ đồng bộ hóa:

<service android:name=".sync.mySyncService" android:exported="true">
   <intent-filter>
      <action android:name="android.content.SyncAdapter" /> 
    </intent-filter>
    <meta-data 
        android:name="android.content.SyncAdapter" 
        android:resource="@xml/sync_myapp" /> 
</service>

Thuộc tính name của <service>thẻ là tên lớp của bạn để kết nối đồng bộ hóa ... Tôi sẽ nói chuyện với điều đó trong giây lát.

Cài đặt được xuất true làm cho nó hiển thị với các thành phần khác (cần thiết để ContentResolvercó thể gọi nó).

Bộ lọc ý định cho phép nó bắt một ý định yêu cầu đồng bộ hóa. (Điều này Intentđến từ ContentResolverkhi bạn gọi ContentResolver.requestSync()hoặc các phương pháp lập lịch liên quan.)

Các <meta-data>thẻ sẽ được thảo luận dưới đây.

2. Cung cấp cho Android một dịch vụ được sử dụng để tìm SyncAdapter của bạn

Vì vậy, chính lớp ... Đây là một ví dụ:

public class mySyncService extends Service {

    private static mySyncAdapter mSyncAdapter = null;

    public SyncService() {
        super();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        if (mSyncAdapter == null) {
            mSyncAdapter = new mySyncAdapter(getApplicationContext(), true);
        }
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return mSyncAdapter.getSyncAdapterBinder();
    }
}

Lớp của bạn phải mở rộng Servicehoặc một trong các lớp con của nó, phải triển khai public IBinder onBind(Intent), và phải trả về một SyncAdapterBinderkhi được gọi ... Bạn cần một biến kiểu AbstractThreadedSyncAdapter. Vì vậy, như bạn có thể thấy, đó là khá nhiều thứ trong lớp đó. Lý do duy nhất để nó cung cấp một Dịch vụ, cung cấp một giao diện tiêu chuẩn cho Android để truy vấn lớp học của bạn xem SyncAdapterbản thân bạn là gì.

3. Cung cấp một class SyncAdapterđể thực sự thực hiện đồng bộ hóa.

mySyncAdapter là nơi lưu trữ logic đồng bộ thực. onPerformSync()Phương thức của nó được gọi khi đến lúc đồng bộ hóa. Tôi nghĩ bạn đã có cái này tại chỗ.

4. Thiết lập ràng buộc giữa Loại tài khoản và Cơ quan nội dung

Nhìn lại AndroidManifest một lần nữa, <meta-data>thẻ kỳ lạ đó trong dịch vụ của chúng tôi là phần quan trọng thiết lập ràng buộc giữa ContentAuthority và tài khoản. Nó tham chiếu bên ngoài một tệp xml khác (gọi nó là bất cứ thứ gì bạn thích, một cái gì đó liên quan đến ứng dụng của bạn.) Hãy xem sync_myapp.xml:

<?xml version="1.0" encoding="utf-8" ?> 
<sync-adapter 
    xmlns:android="http://schemas.android.com/apk/res/android"   
    android:contentAuthority="com.android.contacts"
    android:accountType="com.google" 
    android:userVisible="true" /> 

Được rồi, vậy cái này làm được gì? Nó cho Android biết rằng bộ điều hợp đồng bộ mà chúng tôi đã xác định (lớp được gọi ra trong phần tử tên của <service>thẻ bao gồm <meta-data>thẻ tham chiếu đến tệp này ...) sẽ đồng bộ hóa các liên hệ bằng tài khoản kiểu com.google.

Tất cả nội dung của bạn Chuỗi quyền hạn phải khớp và khớp với những gì bạn đang đồng bộ hóa - Đây phải là chuỗi bạn xác định, nếu bạn đang tạo cơ sở dữ liệu của riêng mình hoặc bạn nên sử dụng một số chuỗi thiết bị hiện có nếu bạn đang đồng bộ hóa. loại dữ liệu (như danh bạ hoặc sự kiện lịch hoặc những gì có bạn.) Ở trên ("com.android.contacts") là chuỗi ContentAuthority cho dữ liệu loại liên hệ (bất ngờ, ngạc nhiên.)

accountType cũng phải khớp với một trong những loại tài khoản đã biết đã được nhập hoặc nó phải khớp với loại tài khoản bạn đang tạo (Điều này liên quan đến việc tạo một lớp con của AccountAuthenticator để nhận xác thực trên máy chủ của bạn ... Bản thân nó cũng đáng là một bài báo.) Một lần nữa, "com.google" là chuỗi được xác định xác định ... thông tin đăng nhập tài khoản kiểu google.com (một lần nữa, điều này không gây ngạc nhiên.)

5. Bật Đồng bộ hóa trên một cặp Tài khoản / Nội dungAuthority nhất định

Cuối cùng, đồng bộ hóa phải được bật. Bạn có thể thực hiện việc này trong trang Tài khoản và đồng bộ hóa trong bảng điều khiển bằng cách đi tới ứng dụng của bạn và đặt hộp kiểm bên cạnh ứng dụng của bạn trong tài khoản phù hợp. Ngoài ra, bạn có thể làm điều đó trong một số mã thiết lập trong ứng dụng của mình:

ContentResolver.setSyncAutomatically(account, AUTHORITY, true);

Để đồng bộ hóa diễn ra, cặp tài khoản / quyền hạn của bạn phải được bật để đồng bộ hóa (như ở trên) cờ đồng bộ hóa toàn cầu tổng thể trên hệ thống phải được đặt thiết bị phải có kết nối mạng.

Nếu đồng bộ hóa tài khoản / quyền hạn của bạn hoặc đồng bộ hóa toàn cầu bị vô hiệu hóa, việc gọi RequestSync () sẽ có tác dụng - Nó đặt cờ cho biết đồng bộ hóa đã được yêu cầu và sẽ được thực hiện ngay sau khi đồng bộ hóa được bật.

Ngoài ra, theo mgv , đặt ContentResolver.SYNC_EXTRAS_MANUALthành true trong gói bổ sung của requestSync của bạn sẽ yêu cầu android buộc đồng bộ hóa ngay cả khi đồng bộ hóa toàn cầu đang tắt (hãy tôn trọng người dùng của bạn tại đây!)

Cuối cùng, bạn có thể thiết lập đồng bộ hóa theo lịch định kỳ, một lần nữa với các chức năng ContentResolver.

6. Xem xét tác động của nhiều tài khoản

Có thể có nhiều tài khoản cùng loại (hai tài khoản @ gmail.com được thiết lập trên một thiết bị hoặc hai tài khoản facebook, hoặc hai tài khoản twitter, v.v.) Bạn nên xem xét ứng dụng của việc làm đó. .. Nếu bạn có hai tài khoản, bạn có thể không muốn cố gắng đồng bộ cả hai tài khoản vào cùng một bảng cơ sở dữ liệu. Có thể bạn cần chỉ định rằng mỗi lần chỉ có thể hoạt động một bảng và xóa các bảng và đồng bộ hóa lại nếu bạn chuyển đổi tài khoản. (thông qua trang thuộc tính truy vấn tài khoản nào hiện có). Có thể bạn tạo một cơ sở dữ liệu khác nhau cho mỗi tài khoản, có thể các bảng khác nhau, có thể một cột chính trong mỗi bảng. Tất cả các ứng dụng cụ thể và xứng đáng với một số suy nghĩ. ContentResolver.setIsSyncable(Account account, String authority, int syncable)có thể được quan tâm ở đây. setSyncAutomatically()kiểm soát xem cặp tài khoản / quyền hạn có được kiểm tra hay khôngbỏ chọn , ngược lại setIsSyncable()cung cấp một cách để bỏ chọn và tô màu xám dòng để người dùng không thể bật nó lên. Bạn có thể đặt một tài khoản Syncable và tài khoản kia không Syncable (bị vô hiệu hóa).

7. Lưu ý về ContentResolver.notifyChange ()

Một điều khó khăn. ContentResolver.notifyChange()là một chức năng được sử dụng bởi ContentProviders để thông báo cho Android rằng cơ sở dữ liệu cục bộ đã bị thay đổi. Điều này phục vụ hai chức năng, đầu tiên, nó sẽ khiến các con trỏ theo sau nội dung đó phải cập nhật, và lần lượt yêu cầu và làm mất hiệu lực và vẽ lại a ListView, v.v. Nó rất kỳ diệu, cơ sở dữ liệu thay đổi và bạn ListViewchỉ cập nhật tự động. Tuyệt vời. Ngoài ra, khi cơ sở dữ liệu thay đổi, Android sẽ yêu cầu Đồng bộ hóa cho bạn, ngay cả ngoài lịch trình bình thường của bạn, để những thay đổi đó được đưa ra khỏi thiết bị và đồng bộ hóa với máy chủ nhanh nhất có thể. Cũng tuyệt vời.

Tuy nhiên, có một trường hợp cạnh. Nếu bạn kéo từ máy chủ và đẩy bản cập nhật vào ContentProvider, nó sẽ gọi notifyChange()và android sẽ kêu , "Ồ, những thay đổi trong cơ sở dữ liệu, tốt hơn là hãy đưa chúng vào máy chủ!" (Doh!) Được viết tốt ContentProviderssẽ có một số bài kiểm tra để xem liệu các thay đổi đến từ mạng hay từ người dùng và sẽ đặt syncToNetworkcờ boolean là false nếu vậy, để ngăn chặn đồng bộ kép lãng phí này. Nếu bạn đang cung cấp dữ liệu vào a ContentProvider, bạn phải tìm cách làm cho điều này hoạt động - Nếu không, bạn sẽ luôn thực hiện đồng bộ hóa hai lần khi chỉ cần một lần đồng bộ.

8. Cảm thấy hạnh phúc!

Khi bạn có tất cả siêu dữ liệu xml này và đồng bộ hóa được bật, Android sẽ biết cách kết nối mọi thứ cho bạn và đồng bộ hóa sẽ bắt đầu hoạt động. Tại thời điểm này, rất nhiều thứ tốt đẹp sẽ chỉ cần nhấp vào vị trí và nó sẽ cảm thấy rất giống như ma thuật. Thưởng thức!


11
ContentResolver.setSyncAutomatically (tài khoản, AUTHORITY, true);
jcwenger

1
Không có vấn đề, rất vui vì tôi có thể giúp đỡ. Mặc dù vậy, từ quan điểm về phong cách, nếu "AUTHORITY" và "myaccounttype" là các chuỗi thực tế mà bạn đang sử dụng (Và không chỉ là các mẫu để sao chép quá khứ vào trang web), bạn chắc chắn cần phải xóa quy ước đặt tên của mình. Các chuỗi đó phải là duy nhất trên tất cả các thiết bị và bạn sẽ gặp rắc rối thực sự nếu một số lập trình viên khác lười biếng tạo một gói có chuỗi phù hợp cho quyền hạn và bạn nhận được xung đột. Chúc mừng!
jcwenger

22
Bạn có thể yêu cầu đồng bộ hóa ngay cả khi đã tắt cài đặt đồng bộ hóa chung. Chỉ cần thêm một ContentResolver.SYNC_EXTRAS_MANUALbộ thành true vào Gói tính năng bổ sung và bạn sẽ buộc đồng bộ hóa :)
mgv

2
@kaciula: Tôi không biết bất kỳ điều gì, nhưng thiết bị SẼ nhớ rằng nó cần phải đồng bộ hóa và nó sẽ bắt đầu ngay sau khi đồng bộ hóa toàn cầu được bật. Bạn thực sự không nên cố gắng đánh lừa người dùng về điều này - Đặc biệt vì "Tắt đồng bộ hóa toàn cầu" là một trong những cách quan trọng để tiết kiệm pin trong những tình huống nguy cấp. Nếu bạn thực sự lo lắng về việc dữ liệu không được đồng bộ hóa, hãy xem xét một cửa sổ bật lên cho người dùng biết TẠI SAO dữ liệu không di chuyển, nếu dữ liệu đã hoạt động trong một thời gian. Bằng cách đó, bạn có thể giáo dục những người dùng đã vô tình định cấu hình sai thiết bị của họ và nhắc nhở người dùng thành thạo trong trường hợp họ quên.
jcwenger 22/10/12

2
Có thể đáng nói thêm rằng nếu bạn muốn sử dụng addPeriodicSync (), nó CHỈ có vẻ hoạt động nếu bạn CŨNG có setSyncAutomatically () - Tôi đã thêm điều đó trong tuyệt vọng, cố gắng làm cho SOMETHING hoạt động. Tôi biết nó không phải là một phần của câu hỏi ban đầu, nhưng đây là một câu trả lời đầy đủ!
android.weasel

0

Tôi đã bình tĩnh lại setIsSyncablesau setAuthTokenphương pháp AccountManager . Nhưng đã đạt được setAuthTokenchức năng trả về trước setIsSyncableđó. Sau khi thay đổi đơn đặt hàng, mọi thứ đều hoạt động tốt!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.