เหตุใด ContentResolver.requestSync จึงไม่ทริกเกอร์การซิงค์


112

ฉันกำลังพยายามใช้รูปแบบอะแดปเตอร์ Content-Provider-Sync ตามที่กล่าวไว้ในGoogle IO - สไลด์ 26 ผู้ให้บริการเนื้อหาของฉันใช้งานได้และการซิงค์ของฉันทำงานได้เมื่อฉันเรียกใช้จากแอปพลิเคชัน Dev Tools Sync Tester อย่างไรก็ตามเมื่อฉันเรียก ContentResolver requestSync (บัญชีผู้มีอำนาจบันเดิล) จาก ContentProvider การซิงค์ของฉันจะไม่ถูกทริกเกอร์

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

แก้ไข - เพิ่มตัวอย่างข้อมูล Manifest xml รายการของฉันประกอบด้วย:

<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>

--Edit

syncadapter.xml ของฉันที่เชื่อมโยงกับบริการซิงค์ของฉันประกอบด้วย:

<?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"
/>

ไม่แน่ใจว่าโค้ดอื่น ๆ จะมีประโยชน์อะไร บัญชีที่ส่งไปยัง requestSync เป็น "myaccounttype" และ AUTHORITY ที่ส่งไปยังการโทรตรงกับ xml อะแดปเตอร์ syc ของฉัน

ContentResolver.requestSync เป็นวิธีที่ถูกต้องในการร้องขอการซิงค์หรือไม่ ดูเหมือนว่าเครื่องมือทดสอบการซิงค์จะเชื่อมโยงโดยตรงกับบริการและการโทรเริ่มการซิงค์ แต่ดูเหมือนว่าจะเอาชนะจุดประสงค์ของการผสานรวมกับสถาปัตยกรรมการซิงค์

หากนั่นเป็นวิธีที่ถูกต้องในการร้องขอการซิงค์เหตุใดเครื่องทดสอบการซิงค์จึงใช้งานได้ แต่ไม่ใช่การเรียก ContentResolver.requestSync ของฉัน มีสิ่งที่ฉันต้องส่งในชุดนี้หรือไม่?

ฉันกำลังทดสอบในโปรแกรมจำลองบนอุปกรณ์ที่ใช้ 2.1 และ 2.2


3
ปัญหาของฉันคือไม่ถึงจุดพักของฉันในอะแดปเตอร์ซิงค์ ... จากนั้นฉันก็รู้ว่าฉันกำลังพยายามดีบักบริการ ... หวังว่านี่จะช่วยคนอื่น ๆ เช่นฉัน
dangalg

2
เบรกพอยต์ในบริการอะแดปเตอร์ซิงค์จะทริกเกอร์ล้มเหลว นั่นเป็นเพราะบริการซิงค์อะแดปเตอร์ทำงานในกระบวนการแยกต่างหาก นี่คือสิ่งที่ @danglang บอกเป็นนัย ๆ ดูคำถามนี้ด้วย: stackoverflow.com/questions/8559458/…
Konstantin Schubert

1
ในกรณีของฉันลบออกandroid:process=":sync"จากบริการซิงค์ให้ดีบักเกอร์กดจุดจะงอยปาก บริการซิงค์นั้นใช้งานได้ก่อนหน้านั้นเนื่องจากฉันเห็นข้อความบันทึกจากonPerformSyncวิธีการในนามของกระบวนการอื่น
Sergey

อีกสาเหตุหนึ่งคือ WiFi ถูกปิดดังนั้นหากคุณพยายามซิงค์กับข้อมูลจำลองให้ตรวจสอบการเชื่อมต่อ
Allan Veloso

คำตอบ:


280

การโทรrequestSync()จะใช้ได้เฉพาะกับคู่ {Account, ContentAuthority} ที่ระบบรู้จัก แอปของคุณต้องทำตามขั้นตอนหลายขั้นตอนเพื่อบอก Android ว่าคุณสามารถซิงโครไนซ์เนื้อหาบางประเภทโดยใช้บัญชีประเภทใดประเภทหนึ่งได้ มันทำสิ่งนี้ใน AndroidManifest

1. แจ้ง Android ว่าแพคเกจแอปพลิเคชันของคุณมีการซิงค์

ก่อนอื่นใน AndroidManifest.xml คุณต้องประกาศว่าคุณมีบริการซิงค์:

<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>

แอตทริบิวต์ชื่อของ<service>แท็กคือชื่อชั้นเรียนของคุณที่จะเชื่อมต่อการซิงค์ ... ฉันจะคุยกับมันในไม่ช้า

การตั้งค่าที่ส่งออกจริงทำให้ส่วนประกอบอื่น ๆ มองเห็นได้ (จำเป็นจึงContentResolverสามารถเรียกมันได้)

ตัวกรองความตั้งใจช่วยให้สามารถตรวจจับเจตนาที่ร้องขอการซิงค์ได้ ( IntentมาจากContentResolverเวลาที่คุณโทรหาContentResolver.requestSync()หรือวิธีการจัดตารางเวลาที่เกี่ยวข้อง)

<meta-data>แท็กจะได้รับการกล่าวถึงด้านล่าง

2. ให้บริการ Android เพื่อค้นหา SyncAdapter ของคุณ

ดังนั้นชั้นเรียนเอง ... นี่คือตัวอย่าง:

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

ชั้นเรียนของคุณต้องขยายServiceหรือหนึ่ง subclasses ของตนจะต้องดำเนินการpublic IBinder onBind(Intent)และต้องส่งคืนSyncAdapterBinderเมื่อที่เรียกว่า ... AbstractThreadedSyncAdapterคุณต้องตัวแปรประเภท อย่างที่คุณเห็นนั่นคือทุกอย่างในชั้นเรียนนั้น เหตุผลเดียวที่ให้บริการคือมีอินเทอร์เฟซมาตรฐานสำหรับ Android เพื่อสอบถามชั้นเรียนของคุณว่าSyncAdapterตัวเองเป็นอย่างไร

3. ระบุclass SyncAdapterเพื่อทำการซิงค์จริง

mySyncAdapter เป็นที่เก็บตรรกะการซิงค์จริง ใช้onPerformSync()วิธีการได้รับเรียกว่าเมื่อมันถึงเวลาที่จะซิงค์ ฉันคิดว่าคุณมีสิ่งนี้อยู่แล้ว

4. สร้างความผูกพันระหว่างประเภทบัญชีและหน่วยงานเนื้อหา

เมื่อมองย้อนกลับไปที่ AndroidManifest อีกครั้ง<meta-data>แท็กแปลก ๆในบริการของเราเป็นส่วนสำคัญที่สร้างการเชื่อมโยงระหว่าง ContentAuthority และบัญชี มันอ้างอิงไฟล์ xml อื่นจากภายนอก (เรียกว่าอะไรก็ได้ที่คุณต้องการสิ่งที่เกี่ยวข้องกับแอปของคุณ) ลองดูที่ 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" /> 

โอเคแล้วมันทำอะไร? มันบอก Android ว่าอะแดปเตอร์การซิงค์ที่เรากำหนดไว้ (คลาสที่ถูกเรียกในองค์ประกอบชื่อของ<service>แท็กที่มี<meta-data>แท็กที่อ้างอิงไฟล์นี้ ... ) จะซิงค์ผู้ติดต่อโดยใช้บัญชีสไตล์ com.google

เนื้อหาทั้งหมดของคุณสตริงผู้มีอำนาจต้องตรงกันทั้งหมดและตรงกับสิ่งที่คุณกำลังซิงค์ - นี่ควรเป็นสตริงที่คุณกำหนดหากคุณกำลังสร้างฐานข้อมูลของคุณเองหรือคุณควรใช้สตริงอุปกรณ์ที่มีอยู่หากคุณรู้จักการซิงค์ ชนิดข้อมูล (เช่นรายชื่อติดต่อหรือกิจกรรมในปฏิทินหรือสิ่งที่คุณมี) ด้านบน ("com.android.contacts") เป็นสตริง ContentAuthority สำหรับข้อมูลประเภทผู้ติดต่อ (แปลกใจแปลกใจ)

accountType ยังต้องจับคู่ประเภทบัญชีที่รู้จักที่ป้อนแล้วหรือต้องตรงกับประเภทที่คุณสร้าง (ซึ่งเกี่ยวข้องกับการสร้างคลาสย่อยของ AccountAuthenticator เพื่อรับการรับรองความถูกต้องบนเซิร์ฟเวอร์ของคุณ ... คุ้มค่ากับบทความนั้นเอง) อีกครั้ง "com.google" คือสตริงที่กำหนดไว้ซึ่งระบุ ... ข้อมูลรับรองบัญชีสไตล์ google.com (อีกครั้งไม่น่าแปลกใจ)

5. เปิดใช้งานการซิงค์ในคู่บัญชี / ContentAuthority ที่กำหนด

สุดท้ายต้องเปิดใช้งานการซิงค์ คุณสามารถทำได้ในหน้าบัญชีและการซิงค์ในแผงควบคุมโดยไปที่แอพของคุณและตั้งค่าช่องทำเครื่องหมายถัดจากแอพของคุณภายในบัญชีที่ตรงกัน หรือคุณสามารถทำได้ในรหัสการตั้งค่าบางอย่างในแอปของคุณ:

ContentResolver.setSyncAutomatically(account, AUTHORITY, true);

เพื่อให้การซิงค์เกิดขึ้นคู่บัญชี / ผู้มีอำนาจของคุณต้องเปิดใช้งานเพื่อซิงค์ (เหมือนข้างบน) และต้องตั้งค่าสถานะการซิงค์ส่วนกลางโดยรวมบนระบบและอุปกรณ์ต้องมีการเชื่อมต่อเครือข่าย

หากการซิงค์บัญชี / ผู้มีอำนาจของคุณหรือการซิงค์ส่วนกลางถูกปิดใช้งานการเรียก RequestSync () จะมีผล - ตั้งค่าสถานะว่ามีการร้องขอการซิงค์และจะดำเนินการทันทีที่เปิดใช้งานการซิงค์

นอกจากนี้ตามmgvการตั้งค่าContentResolver.SYNC_EXTRAS_MANUALเป็นจริงในชุดพิเศษของ requestSync ของคุณจะขอให้ Android บังคับให้ซิงค์แม้ว่าการซิงค์ทั่วโลกจะปิดอยู่ก็ตาม (โปรดเคารพผู้ใช้ของคุณที่นี่!)

สุดท้ายคุณสามารถตั้งค่าการซิงค์ตามกำหนดเวลาเป็นระยะได้อีกครั้งด้วยฟังก์ชัน ContentResolver

6. พิจารณาความหมายของบัญชีหลายบัญชี

เป็นไปได้ที่จะมีบัญชีประเภทเดียวกันมากกว่าหนึ่งบัญชี (สองบัญชี @ gmail.com ตั้งค่าบนอุปกรณ์หนึ่งหรือสองบัญชี Facebook หรือสองบัญชี Twitter เป็นต้น) คุณควรพิจารณาถึงผลกระทบของแอปพลิเคชันในการทำเช่นนั้น .. หากคุณมีสองบัญชีคุณคงไม่อยากพยายามซิงค์ทั้งสองบัญชีลงในตารางฐานข้อมูลเดียวกัน บางทีคุณอาจต้องระบุว่าสามารถใช้งานได้ครั้งละหนึ่งรายการเท่านั้นและล้างตารางและซิงค์ใหม่หากคุณเปลี่ยนบัญชี (ผ่านหน้าคุณสมบัติที่สอบถามว่ามีบัญชีอะไรบ้าง) บางทีคุณอาจสร้างฐานข้อมูลที่แตกต่างกันสำหรับแต่ละบัญชีอาจเป็นตารางที่แตกต่างกันอาจจะเป็นคอลัมน์หลักในแต่ละตาราง แอปพลิเคชันทั้งหมดเฉพาะและคุ้มค่ากับความคิด ContentResolver.setIsSyncable(Account account, String authority, int syncable)อาจสนใจที่นี่ setSyncAutomatically()ควบคุมว่าจะตรวจสอบคู่บัญชี / ผู้มีอำนาจหรือไม่ได้ทำเครื่องหมายในขณะที่setIsSyncable()มีวิธีการยกเลิกการเลือกและทำให้เส้นเป็นสีเทาเพื่อให้ผู้ใช้ไม่สามารถเปิดได้ คุณอาจตั้งค่าให้บัญชีหนึ่งซิงค์ได้และอีกบัญชีหนึ่งไม่สามารถซิงค์ได้ (dsabled)

7. ระวัง ContentResolver.notifyChange ()

สิ่งที่ยุ่งยากอย่างหนึ่ง ContentResolver.notifyChange()เป็นฟังก์ชันที่ใช้ContentProviderเพื่อแจ้งให้ Android ทราบว่ามีการเปลี่ยนแปลงฐานข้อมูลภายในเครื่อง สิ่งนี้ทำหน้าที่สองฟังก์ชั่นอย่างแรกมันจะทำให้เคอร์เซอร์หลังจากที่เนื้อหานั้นอัปเดต uri และในทางกลับกันการร้องขอและทำให้ไม่ถูกต้องและวาดใหม่ListViewฯลฯ ... มันวิเศษมากฐานข้อมูลจะเปลี่ยนและListViewอัปเดตของคุณโดยอัตโนมัติ น่ากลัว นอกจากนี้เมื่อฐานข้อมูลมีการเปลี่ยนแปลง Android จะร้องขอการซิงค์ให้คุณแม้จะอยู่นอกกำหนดการปกติของคุณเพื่อให้การเปลี่ยนแปลงเหล่านั้นถูกลบออกจากอุปกรณ์และซิงค์กับเซิร์ฟเวอร์โดยเร็วที่สุด ยังน่ากลัว

แม้ว่าจะมีขอบกรณีเดียว หากคุณดึงออกจากเซิร์ฟเวอร์และผลักดันการอัปเดตเข้าสู่ระบบContentProviderจะเรียกตามหน้าที่notifyChange()และ Android จะไป "โอ้การเปลี่ยนแปลงฐานข้อมูลดีกว่าวางไว้บนเซิร์ฟเวอร์!" (Doh!) เขียนอย่างดีContentProvidersจะมีการทดสอบเพื่อดูว่าการเปลี่ยนแปลงมาจากเครือข่ายหรือจากผู้ใช้และจะตั้งค่าsyncToNetworkสถานะบูลีนเป็นเท็จหากเป็นเช่นนั้นเพื่อป้องกันการซิงค์สองครั้งที่สิ้นเปลือง หากคุณกำลังป้อนข้อมูลลงใน a ContentProviderคุณจะต้องคิดหาวิธีทำงานนี้ - มิฉะนั้นคุณจะต้องทำการซิงค์สองครั้งเสมอเมื่อต้องการเพียงอันเดียว

8. รู้สึกมีความสุข!

เมื่อคุณมีข้อมูลเมตา xml ทั้งหมดนี้และเปิดใช้งานการซิงค์แล้ว Android จะรู้วิธีเชื่อมต่อทุกอย่างให้คุณและการซิงค์จะเริ่มทำงาน ณ จุดนี้หลายสิ่งที่ดีจะคลิกเข้าที่และมันจะรู้สึกเหมือนมีเวทมนตร์ สนุก!


11
ContentResolver.setSyncAutomatically (บัญชี AUTHORITY จริง);
jcwenger

1
ไม่มีปัญหาดีใจที่ช่วยได้ จากมุมมองของสไตล์อีกครั้งหาก "AUTHORITY" และ "myaccounttype" เป็นสตริงจริงที่คุณใช้อยู่ (และไม่ใช่แค่ตัวอย่างสำหรับการคัดลอกไปยังไซต์) คุณจะต้องทำความสะอาดรูปแบบการตั้งชื่อของคุณอย่างแน่นอน สตริงเหล่านี้จะต้องไม่ซ้ำกันในทุกอุปกรณ์และคุณจะประสบปัญหาจริงหากโปรแกรมเมอร์คนอื่นขี้เกียจสร้างแพ็คเกจที่มีสตริงที่ตรงกันสำหรับผู้มีอำนาจและคุณได้รับข้อขัดแย้ง ไชโย!
jcwenger

22
คุณสามารถขอการซิงค์ได้แม้ว่าการตั้งค่าการซิงค์ส่วนกลางจะปิดอยู่ เพียงเพิ่มContentResolver.SYNC_EXTRAS_MANUALชุดเป็น true ใน Bundle พิเศษและคุณจะบังคับให้ซิงค์ :)
mgv

2
@kaciula: ฉันไม่รู้อะไรเลย แต่อุปกรณ์จะจำได้ว่าต้องซิงค์และจะเริ่มทำงานทันทีที่เปิดการซิงค์ทั่วโลก คุณไม่ควรพยายามเอาชนะผู้ใช้ด้วยวิธีนี้โดยเฉพาะอย่างยิ่งเมื่อ "ปิดการซิงค์ทั่วโลก" เป็นหนึ่งในวิธีสำคัญในการประหยัดแบตเตอรี่ในสถานการณ์ที่เลวร้าย หากคุณกังวลจริงๆว่าข้อมูลจะไม่ได้รับการซิงค์ให้พิจารณาป๊อปอัปที่แจ้งให้ผู้ใช้ทราบว่าทำไมข้อมูลถึงไม่เคลื่อนไหวหากนั่งอยู่สักพัก ด้วยวิธีนี้คุณสามารถให้ความรู้แก่ผู้ใช้ที่กำหนดค่าอุปกรณ์ผิดโดยไม่ได้ตั้งใจและเตือนผู้ใช้ระดับสูงในกรณีที่ลืม
jcwenger

2
มันอาจจะคุ้มค่าที่จะเพิ่มว่าหากคุณต้องการใช้ addPeriodicSync () ดูเหมือนว่าจะใช้งานได้ก็ต่อเมื่อคุณ setSyncAutomatically () ด้วยเช่นกัน - ฉันได้เพิ่มสิ่งนั้นออกไปด้วยความสิ้นหวังพยายามทำให้บางอย่างทำงานได้ ฉันรู้ว่ามันไม่ได้เป็นส่วนหนึ่งของคำถามดั้งเดิม แต่นี่คือคำตอบที่สมบูรณ์!
android.weasel

0

ฉันกำลังสงบsetIsSyncableหลังจากsetAuthTokenเมธอดAccountManager แต่setAuthTokenกลับฟังก์ชั่นก่อนที่จะsetIsSyncableถึง หลังจากการเปลี่ยนแปลงคำสั่งทุกอย่างทำงานได้ดี!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.