建立同步處理轉換介面

注意:建議使用 WorkManager 是多數背景處理用途的建議解決方案。請參閱 背景處理指南,瞭解最適合您的解決方案。

應用程式中的同步轉換介面元件會封裝工作的程式碼 裝置與伺服器之間的資料通訊根據您在觸發條件中提供的排程和觸發條件 應用程式,同步處理轉換介面架構會在同步處理轉換介面元件中執行程式碼。如要新增 將同步轉換介面元件加入您的應用程式,您需要新增下列部分:

同步處理轉接程式類別。
這個類別可將資料移轉程式碼納入與同步轉換介面相容的介面 這個架構的重點在於
已繫結 Service
允許同步處理轉換介面架構的元件,可在同步轉換介面中執行程式碼 類別
同步介面卡 XML 中繼資料檔案。
包含同步轉換介面相關資訊的檔案。架構會將這個檔案 瞭解如何載入及安排資料轉移時程。
應用程式資訊清單中的宣告。
宣告繫結服務,並指向同步處理轉接器專屬中繼資料的 XML。

本課程將說明如何定義這些元素。

建立同步轉換介面類別

這部分課程將說明如何建立同步處理轉接程式類別,來封裝 資料移轉程式碼建立類別包括擴充同步處理轉接程式基本類別,並定義 以及實作可定義資料移轉的方法 機器學習程式庫提供一系列預先編寫的程式碼 可用來執行機器學習工作

擴充基本同步轉換介面類別

如要建立同步轉換介面元件,請先 AbstractThreadedSyncAdapter,並寫入其建構函式。使用 建構函式,以便執行設定工作,每次從中建立同步轉換介面元件 就像使用 Activity.onCreate() 設定 活動。舉例來說,如果應用程式使用內容供應器儲存資料,請使用建構函式 取得 ContentResolver 執行個體。由於第二種形式的 Android 平台 3.0 版新增了建構函式,用於支援 parallelSyncs 引數,您必須建立兩種形式的建構函式,才能維持相容性。

注意:同步轉換介面架構可與同步轉換介面搭配運作 屬於單例模式執行個體的元件將同步轉換介面元件執行個體化 本節將詳細說明 將同步處理轉接程式繫結至架構

以下範例說明如何實作 AbstractThreadedSyncAdapter 及其建構函式:

Kotlin

/**
 * Handle the transfer of data between a server and an
 * app, using the Android sync adapter framework.
 */
class SyncAdapter @JvmOverloads constructor(
        context: Context,
        autoInitialize: Boolean,
        /**
         * Using a default argument along with @JvmOverloads
         * generates constructor for both method signatures to maintain compatibility
         * with Android 3.0 and later platform versions
         */
        allowParallelSyncs: Boolean = false,
        /*
         * If your app uses a content resolver, get an instance of it
         * from the incoming Context
         */
        val mContentResolver: ContentResolver = context.contentResolver
) : AbstractThreadedSyncAdapter(context, autoInitialize, allowParallelSyncs) {
    ...
}

Java

/**
 * Handle the transfer of data between a server and an
 * app, using the Android sync adapter framework.
 */
public class SyncAdapter extends AbstractThreadedSyncAdapter {
    ...
    // Global variables
    // Define a variable to contain a content resolver instance
    ContentResolver contentResolver;
    /**
     * Set up the sync adapter
     */
    public SyncAdapter(Context context, boolean autoInitialize) {
        super(context, autoInitialize);
        /*
         * If your app uses a content resolver, get an instance of it
         * from the incoming Context
         */
        contentResolver = context.getContentResolver();
    }
    ...
    /**
     * Set up the sync adapter. This form of the
     * constructor maintains compatibility with Android 3.0
     * and later platform versions
     */
    public SyncAdapter(
            Context context,
            boolean autoInitialize,
            boolean allowParallelSyncs) {
        super(context, autoInitialize, allowParallelSyncs);
        /*
         * If your app uses a content resolver, get an instance of it
         * from the incoming Context
         */
        contentResolver = context.getContentResolver();
        ...
    }

新增資料移轉代碼

同步轉換介面元件不會自動進行資料移轉。相反地 封裝資料移轉程式碼,讓同步轉換介面架構可在 在背景中轉移資料,無須執行任何操作。架構準備就緒時 來同步處理應用程式的資料,將會叫用方法的實作 onPerformSync()

如要協助將資料從主要應用程式的程式碼轉移至同步轉換介面元件,請 同步處理轉換介面架構呼叫 onPerformSync(),其中包含

帳戶
與觸發事件相關聯的 Account 物件 同步處理轉換介面如果您的伺服器不需要使用帳戶,您就不必使用 這個物件中的資訊
額外項目
Bundle,包含觸發同步處理的事件所傳送的標記 轉接頭。
權威推薦
系統中的內容供應器授權。應用程式必須具備以下權限: 使用這個供應商的服務一般而言,主機名稱與您應用程式中的內容供應器相對應。
,瞭解如何調查及移除這項存取權。
內容供應器用戶端
指向內容供應器的 ContentProviderClient 主機名稱。ContentProviderClient 是輕量級的公共服務 可存取內容供應器的介面這與 ContentResolver。如果您使用內容供應器來儲存資料 使用此物件與供應商連結。否則,您可以忽略 基礎架構
同步處理結果
您用來將資訊傳送至同步處理作業的 SyncResult 物件 轉接程式架構。

下列程式碼片段顯示 onPerformSync():

Kotlin

/*
 * Specify the code you want to run in the sync adapter. The entire
 * sync adapter runs in a background thread, so you don't have to set
 * up your own background processing.
 */
override fun onPerformSync(
        account: Account,
        extras: Bundle,
        authority: String,
        provider: ContentProviderClient,
        syncResult: SyncResult
) {
    /*
     * Put the data transfer code here.
     */
}

Java

/*
 * Specify the code you want to run in the sync adapter. The entire
 * sync adapter runs in a background thread, so you don't have to set
 * up your own background processing.
 */
@Override
public void onPerformSync(
        Account account,
        Bundle extras,
        String authority,
        ContentProviderClient provider,
        SyncResult syncResult) {
    /*
     * Put the data transfer code here.
     */
}

雖然實際導入 onPerformSync() 是專用於 應用程式的資料同步處理需求和伺服器連線通訊協定等 您的實作資料應執行的一般工作如下:

連線至伺服器
雖然您可以假設資料移轉作業開始時可用的網路, 同步轉換介面架構不會自動連線至伺服器。
下載及上傳資料
同步轉換介面不會自動執行任何資料移轉工作。如何下載 擷取資料並將其儲存在內容供應器中,您必須提供 要求、下載資料,並插入供應器中同樣地,如果您想要 將資料傳送至伺服器,您必須從檔案、資料庫或供應器讀取資料,然後 必要的上傳要求您也必須處理 資料移轉作業執行中
處理資料衝突或判斷資料目前的狀況
同步轉接器不會自動處理伺服器中的資料與資料之間的衝突 應用程式。此外,也不會自動偵測伺服器上的資料時間是否比 清除裝置上的資料而必須自行提供演算法 來處理這種情況
清除所用資源。
一律關閉與伺服器的連線,並清除最後的暫存檔案和快取 以及資料移轉服務

注意:同步處理轉換介面架構會執行 onPerformSync() 因此您不必自行設定背景處理程序。

除了執行同步相關工作之外,建議您結合 然後將這些工作新增到 onPerformSync()。 使用此方法集中處理所有網路工作,可節省 以便啟動和停止網路介面。進一步瞭解如何提供進一步的網路存取權 請參閱訓練課程在不排除電池的情況下轉移資料,其中說明瞭幾種網路存取方式 可加入資料移轉程式碼的工作

將同步轉換介面繫結至架構

同步轉換介面元件現已封裝資料移轉程式碼,但您 ,為架構提供程式碼存取權。如要這麼做,您需要 Service 會從同步轉換介面傳遞特殊 Android 繫結器物件 加入這個架構透過此繫結器物件,架構可以叫用 onPerformSync() 方法和 傳遞資料

將同步轉換介面元件執行個體化,為 服務的 onCreate() 方法。建立例項 現在 onCreate() 中的元件 直到服務啟動為止,也就是架構首次嘗試執行 資料移轉。您需要以執行緒安全的方式將元件例項化,以防同步處理 轉接器架構會將同步轉接程式的多個執行作業排入佇列,以回應觸發條件或 排定時間

舉例來說,下列程式碼片段說明如何建立實作 繫結 Service、將同步轉換介面元件執行個體化,並取得 Android 繫結器物件:

Kotlin

package com.example.android.syncadapter
/**
 * Define a Service that returns an [android.os.IBinder] for the
 * sync adapter class, allowing the sync adapter framework to call
 * onPerformSync().
 */
class SyncService : Service() {
    /*
     * Instantiate the sync adapter object.
     */
    override fun onCreate() {
        /*
         * Create the sync adapter as a singleton.
         * Set the sync adapter as syncable
         * Disallow parallel syncs
         */
        synchronized(sSyncAdapterLock) {
            sSyncAdapter = sSyncAdapter ?: SyncAdapter(applicationContext, true)
        }
    }

    /**
     * Return an object that allows the system to invoke
     * the sync adapter.
     *
     */
    override fun onBind(intent: Intent): IBinder {
        /*
         * Get the object that allows external processes
         * to call onPerformSync(). The object is created
         * in the base class code when the SyncAdapter
         * constructors call super()
         *
         * We should never be in a position where this is called before
         * onCreate() so the exception should never be thrown
         */
        return sSyncAdapter?.syncAdapterBinder ?: throw IllegalStateException()
    }

    companion object {
        // Storage for an instance of the sync adapter
        private var sSyncAdapter: SyncAdapter? = null
        // Object to use as a thread-safe lock
        private val sSyncAdapterLock = Any()
    }
}

Java

package com.example.android.syncadapter;
/**
 * Define a Service that returns an <code><a href="/reference/android/os/IBinder.html">IBinder</a></code> for the
 * sync adapter class, allowing the sync adapter framework to call
 * onPerformSync().
 */
public class SyncService extends Service {
    // Storage for an instance of the sync adapter
    private static SyncAdapter sSyncAdapter = null;
    // Object to use as a thread-safe lock
    private static final Object sSyncAdapterLock = new Object();
    /*
     * Instantiate the sync adapter object.
     */
    @Override
    public void onCreate() {
        /*
         * Create the sync adapter as a singleton.
         * Set the sync adapter as syncable
         * Disallow parallel syncs
         */
        synchronized (sSyncAdapterLock) {
            if (sSyncAdapter == null) {
                sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
            }
        }
    }
    /**
     * Return an object that allows the system to invoke
     * the sync adapter.
     *
     */
    @Override
    public IBinder onBind(Intent intent) {
        /*
         * Get the object that allows external processes
         * to call onPerformSync(). The object is created
         * in the base class code when the SyncAdapter
         * constructors call super()
         */
        return sSyncAdapter.getSyncAdapterBinder();
    }
}

注意:如要查看更詳細的同步轉換介面繫結服務範例, 請參閱範例應用程式

新增架構所需的帳戶

同步轉換介面架構要求每個同步轉換介面都必須擁有帳戶類型。您已宣告 存取服務帳戶的帳戶類型值 新增 Authenticator 中繼資料檔案。現在,您必須在 Android 系統。如要設定帳戶類型,請新增採用帳戶類型的預留位置帳戶 呼叫 addAccountExplicitly()

如要呼叫這個方法,最好在 應用程式的 onCreate() 方法 開啟活動。下列程式碼片段說明如何執行這項作業:

Kotlin

...
// Constants
// The authority for the sync adapter's content provider
const val AUTHORITY = "com.example.android.datasync.provider"
// An account type, in the form of a domain name
const val ACCOUNT_TYPE = "example.com"
// The account name
const val ACCOUNT = "placeholderaccount"
...
class MainActivity : FragmentActivity() {

    // Instance fields
    private lateinit var mAccount: Account
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
       ...
        // Create the placeholder account
        mAccount = createSyncAccount()
       ...
    }
    ...
    /**
     * Create a new placeholder account for the sync adapter
     */
    private fun createSyncAccount(): Account {
        val accountManager = getSystemService(Context.ACCOUNT_SERVICE) as AccountManager
        return Account(ACCOUNT, ACCOUNT_TYPE).also { newAccount ->
            /*
             * Add the account and account type, no password or user data
             * If successful, return the Account object, otherwise report an error.
             */
            if (accountManager.addAccountExplicitly(newAccount, null, null)) {
                /*
                 * If you don't set android:syncable="true" in
                 * in your <provider> element in the manifest,
                 * then call context.setIsSyncable(account, AUTHORITY, 1)
                 * here.
                 */
            } else {
                /*
                 * The account exists or some other error occurred. Log this, report it,
                 * or handle it internally.
                 */
            }
        }
    }
    ...
}

Java

public class MainActivity extends FragmentActivity {
    ...
    ...
    // Constants
    // The authority for the sync adapter's content provider
    public static final String AUTHORITY = "com.example.android.datasync.provider";
    // An account type, in the form of a domain name
    public static final String ACCOUNT_TYPE = "example.com";
    // The account name
    public static final String ACCOUNT = "placeholderaccount";
    // Instance fields
    Account mAccount;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        // Create the placeholder account
        mAccount = CreateSyncAccount(this);
        ...
    }
    ...
    /**
     * Create a new placeholder account for the sync adapter
     *
     * @param context The application context
     */
    public static Account CreateSyncAccount(Context context) {
        // Create the account type and default account
        Account newAccount = new Account(
                ACCOUNT, ACCOUNT_TYPE);
        // Get an instance of the Android account manager
        AccountManager accountManager =
                (AccountManager) context.getSystemService(
                        ACCOUNT_SERVICE);
        /*
         * Add the account and account type, no password or user data
         * If successful, return the Account object, otherwise report an error.
         */
        if (accountManager.addAccountExplicitly(newAccount, null, null)) {
            /*
             * If you don't set android:syncable="true" in
             * in your <provider> element in the manifest,
             * then call context.setIsSyncable(account, AUTHORITY, 1)
             * here.
             */
        } else {
            /*
             * The account exists or some other error occurred. Log this, report it,
             * or handle it internally.
             */
        }
    }
    ...
}

新增同步介面卡中繼資料檔案

如要將同步轉換介面元件插入架構,您必須提供架構 說明元件的中繼資料,並提供其他標記。metadata 值會指定 您為同步轉換介面建立的帳戶類型,宣告內容供應器授權 就能控管與同步轉接程式相關的系統使用者介面部分 並宣告其他同步處理相關旗標將這項中繼資料宣告儲存在儲存位置的特殊 XML 檔案中 應用程式專案中的 /res/xml/ 目錄。您可以為檔案命名 但通常稱為 syncadapter.xml

這個 XML 檔案包含單一 XML 元素 <sync-adapter>,且具有 屬性如下:

android:contentAuthority
內容供應器的 URI 授權。如果您為 您的應用程式建立存根內容供應器,請使用您為 屬性 android:authorities 顯示在應用程式資訊清單中的 <provider> 元素中。此屬性為 本章節將詳細說明 在資訊清單中宣告提供者
如要使用同步轉接器將資料從內容供應器轉移到伺服器,則 值必須與您為該資料使用的內容 URI 授權相同。這個值 也是您在 android:authorities 在應用程式資訊清單中宣告供應器的 <provider> 元素屬性。
android:accountType
同步處理轉換介面架構所需的帳戶類型。這個值必須相同 是您建立驗證器中繼資料檔案時提供的帳戶類型值,例如 請參閱新增 Authenticator 中繼資料檔案一節。同時也是您為 本節程式碼片段中的常數 ACCOUNT_TYPE 新增架構所需的帳戶
設定屬性
android:userVisible
設定同步轉換介面帳戶類型的顯示設定。根據預設, 帳戶圖示和標籤會顯示在 系統「設定」應用程式的「帳戶」部分,建議您同步處理 不顯示轉接器,除非您有可輕鬆建立關聯的帳戶類型或網域 互動情形即使隱藏帳戶類型,仍可允許使用者 透過其中一個應用程式活動的使用者介面,控制同步轉換介面。
android:supportsUploading
讓您把資料上傳到雲端。如果應用程式僅適用於應用程式,請將此設為 false 下載資料。
android:allowParallelSyncs
允許同時執行同步轉換介面元件的多個執行個體。 如果您的應用程式支援多個使用者帳戶,而您想要允許多個使用者帳戶,請使用此選項 使用者,以便同時轉移資料。如果您從未執行過這個標記,這個標記就不會有任何作用 以便進行多段資料移轉作業
android:isAlwaysSyncable
向同步處理轉接程式架構,指出該架構可在任何位置執行同步轉換介面 您指定的時間。如要透過程式輔助方式控管同步處理時間 轉接程式可以執行、將此標記設為 false,然後呼叫 requestSync() 來執行 同步處理轉換介面如要進一步瞭解如何執行同步處理轉換介面,請參閱 執行同步處理配接器

下例顯示使用單一預留位置帳戶的同步轉換介面 XML 只會下載下載

<?xml version="1.0" encoding="utf-8"?>
<sync-adapter
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:contentAuthority="com.example.android.datasync.provider"
        android:accountType="com.android.example.datasync"
        android:userVisible="false"
        android:supportsUploading="false"
        android:allowParallelSyncs="false"
        android:isAlwaysSyncable="true"/>

在資訊清單中宣告同步轉換介面

在應用程式中新增同步轉換介面元件後,您必須要求權限 您必須宣告繫結的 Service

由於同步轉換介面元件會執行程式碼,在網路和 裝置,請提出上網權限要求。此外,您的應用程式需求 要求讀取和寫入同步轉換介面設定的權限,以便您控制同步處理作業 透過程式輔助方式從應用程式中的其他元件導入轉接程式。此外,您也必須請求 特殊權限,讓應用程式使用您建立的驗證器元件 請參閱「建立 Stub Authenticator」課程。

如要要求這些權限,請將以下內容加入應用程式資訊清單,做為 <manifest>:

android.permission.INTERNET
允許同步轉接器程式碼存取網際網路,以便下載或上傳資料 應用程式之間如果您具有以下權限,則無須再次新增這項權限: 這項要求
android.permission.READ_SYNC_SETTINGS
允許應用程式讀取目前的同步轉換介面設定。舉例來說,您需要 權限才能呼叫 getIsSyncable()
android.permission.WRITE_SYNC_SETTINGS
允許應用程式控制同步處理轉換介面設定。必須授予這項權限 使用 addPeriodicSync() 設定定期同步轉換介面執行。呼叫此權限需要這項權限 requestSync()。如要進一步瞭解 執行同步處理轉接程式,請參閱執行同步轉換介面

下列程式碼片段說明如何新增權限:

<manifest>
...
    <uses-permission
            android:name="android.permission.INTERNET"/>
    <uses-permission
            android:name="android.permission.READ_SYNC_SETTINGS"/>
    <uses-permission
            android:name="android.permission.WRITE_SYNC_SETTINGS"/>
    <uses-permission
            android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
...
</manifest>

最後,宣告架構會使用到的繫結 Service 與同步轉換介面互動,請將下列 XML 加入應用程式資訊清單做為子項元素 /<application>

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

<intent-filter> 元素會設定由意圖動作觸發的篩選器 android.content.SyncAdapter,系統會傳送此項目來執行同步轉換介面。套用篩選器後 即會啟動您建立的繫結服務,在本範例中, SyncService。屬性 android:exported="false" 僅允許應用程式和系統存取 Service。屬性 android:process=":sync" 會告知系統,在名為Service sync。如果應用程式中有多個同步轉換介面,這些轉接程式可以共用這項程序, 進而減輕作業負擔

<meta-data> 元素提供先前建立的同步轉換介面中繼資料 XML 檔案名稱。 android:name 屬性表示這項中繼資料適用於同步轉換介面架構。 android:resource 元素會指定中繼資料檔案名稱。

您現在已有同步轉接程式的所有元件。下一個課程會說明如何 指示同步轉換介面架構執行同步轉換介面,以回應事件或 定期排程