동기화 어댑터 실행

참고: WorkManager를 사용하는 것이 좋습니다. 를 사용하는 것이 좋습니다. 자세한 내용은 백그라운드 처리 가이드를 참조하세요.

이 클래스의 이전 강의에서는 데이터 전송 코드를 캡슐화하고, 코드 스니펫에 추가 구성요소를 사용하여 동기화 어댑터를 시스템에 연결합니다. 이제 다음 작업을 수행하는 앱을 설치하는 데 필요한 모든 것이 갖춰져 있습니다. 동기화 어댑터가 포함되어 있지만 실제로 동기화 어댑터를 실행하지 않는 코드.

일정에 따라 또는 일부 작업의 간접 결과로 동기화 어댑터를 이벤트를 처리합니다. 예를 들어 특정 시간에 또는 하루 중 특정 시간에 광고가 게재되도록 할 수 있습니다. 또한 동기화를 실행하고 기기에 저장된 데이터에 변경사항이 있을 때 어댑터에 사용합니다. 피처스토어를 사용자 작업의 직접적인 결과로 동기화 어댑터를 연결합니다. 이렇게 하면 전체 동기화 데이터를 동기화 어댑터 프레임워크의 일정 예약 기능을 활용할 수 있습니다. 예를 들어 사용자 인터페이스에 새로고침 버튼을 제공합니다

동기화 어댑터 실행 시 다음과 같은 옵션을 사용할 수 있습니다.

서버 데이터 변경 시
서버의 메시지에 대한 응답으로 동기화 어댑터를 실행하면 데이터가 변경되었습니다. 이 옵션을 사용하면 서버에서 기기로 데이터를 새로고침할 수 있습니다. 서버 폴링을 통해 성능을 저하시키거나 배터리 수명을 낭비하지 않을 수 있습니다.
기기 데이터 변경 시
기기에서 데이터가 변경되면 동기화 어댑터를 실행합니다. 이 옵션을 사용하면 전송할 수 있으며, 서버에서 서버로 데이터를 전송하는 데 특히 유용합니다 서버에 항상 최신 기기 데이터가 있어야 합니다. 이 옵션은 구현할 수 있습니다. 스텁을 사용하는 경우 데이터 변경사항을 감지하는 것이 더 어려울 수 있습니다.
일정한 간격으로
선택한 간격이 만료된 후 동기화 어댑터를 실행하거나 특정 시점에 실행 시간을 절약할 수 있습니다
주문형
사용자 작업에 응답하여 동기화 어댑터를 실행합니다. 하지만 최적의 사용자에게 주로 보다 자동화된 옵션 중 하나에 의존해야 합니다. 사용 배터리 및 네트워크 리소스를 절약할 수 있습니다.

이 학습 과정의 나머지 부분에서는 각 옵션에 관해 자세히 설명합니다.

서버 데이터 변경 시 동기화 어댑터 실행

앱이 서버에서 데이터를 전송하고 서버 데이터가 자주 변경되는 경우, 데이터 변경에 응답하여 다운로드를 실행하는 동기화 어댑터. 동기화 어댑터를 실행하려면 서버가 앱의 BroadcastReceiver에 특별한 메시지를 보냅니다. 이 메시지에 대한 응답으로 ContentResolver.requestSync()를 호출하여 동기화 어댑터 프레임워크에 동기화 어댑터입니다.

Google 클라우드 메시징 (GCM)은 서버 및 장치 구성 요소가 있어야 합니다. GCM을 사용하여 트리거 상태를 위해 서버를 폴링하는 것보다 더 안정적이고 효율적입니다. 폴링 중 항상 활성 상태인 Service가 필요하며 GCM은 메시지가 도착하면 활성화되는 BroadcastReceiver입니다. 폴링 중 사용할 수 있는 업데이트가 없더라도, 일정 간격으로 배터리 전원을 사용하며, GCM은 메시지를 보낼 수 있습니다

참고: GCM을 사용하여 모든 대상에게 브로드캐스트를 통해 동기화 어댑터를 트리거하는 경우 메시지를 받을 수 있다는 것을 기억하십시오. 거의 같은 시간에 일어납니다. 이 상황에서는 동기화 어댑터의 여러 인스턴스가 실행될 수 있습니다. 동시에 서버 및 네트워크 과부하를 일으킵니다. 방송에서 이러한 상황을 방지하려면 일정 시간 동안 동기화 어댑터의 시작을 연기하는 것이 고유한 ID를 사용할 수 있습니다

다음 코드 스니펫은 다음에 대한 응답: requestSync() 수신된 GCM 메시지:

Kotlin

...
// Constants
// Content provider authority
const val AUTHORITY = "com.example.android.datasync.provider"
// Account type
const val ACCOUNT_TYPE = "com.example.android.datasync"
// Account
const val ACCOUNT = "default_account"
// Incoming Intent key for extended data
const val KEY_SYNC_REQUEST = "com.example.android.datasync.KEY_SYNC_REQUEST"
...
class GcmBroadcastReceiver : BroadcastReceiver() {
    ...
    override fun onReceive(context: Context, intent: Intent) {
        // Get a GCM object instance
        val gcm: GoogleCloudMessaging = GoogleCloudMessaging.getInstance(context)
        // Get the type of GCM message
        val messageType: String? = gcm.getMessageType(intent)
        /*
         * Test the message type and examine the message contents.
         * Since GCM is a general-purpose messaging system, you
         * may receive normal messages that don't require a sync
         * adapter run.
         * The following code tests for a a boolean flag indicating
         * that the message is requesting a transfer from the device.
         */
        if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE == messageType
            && intent.getBooleanExtra(KEY_SYNC_REQUEST, false)) {
            /*
             * Signal the framework to run your sync adapter. Assume that
             * app initialization has already created the account.
             */
            ContentResolver.requestSync(mAccount, AUTHORITY, null)
            ...
        }
        ...
    }
    ...
}

자바

public class GcmBroadcastReceiver extends BroadcastReceiver {
    ...
    // Constants
    // Content provider authority
    public static final String AUTHORITY = "com.example.android.datasync.provider";
    // Account type
    public static final String ACCOUNT_TYPE = "com.example.android.datasync";
    // Account
    public static final String ACCOUNT = "default_account";
    // Incoming Intent key for extended data
    public static final String KEY_SYNC_REQUEST =
            "com.example.android.datasync.KEY_SYNC_REQUEST";
    ...
    @Override
    public void onReceive(Context context, Intent intent) {
        // Get a GCM object instance
        GoogleCloudMessaging gcm =
                GoogleCloudMessaging.getInstance(context);
        // Get the type of GCM message
        String messageType = gcm.getMessageType(intent);
        /*
         * Test the message type and examine the message contents.
         * Since GCM is a general-purpose messaging system, you
         * may receive normal messages that don't require a sync
         * adapter run.
         * The following code tests for a a boolean flag indicating
         * that the message is requesting a transfer from the device.
         */
        if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)
            &&
            intent.getBooleanExtra(KEY_SYNC_REQUEST)) {
            /*
             * Signal the framework to run your sync adapter. Assume that
             * app initialization has already created the account.
             */
            ContentResolver.requestSync(mAccount, AUTHORITY, null);
            ...
        }
        ...
    }
    ...
}

콘텐츠 제공업체 데이터 변경 시 동기화 어댑터 실행

앱이 콘텐츠 제공자에서 데이터를 수집하고 사용자가 데이터를 업데이트할 때마다 서버를 업데이트하려는 경우 제공자를 업데이트하면 동기화 어댑터를 자동으로 실행하도록 앱을 설정할 수 있습니다. 해야 할 일 콘텐츠 제공자의 관찰자를 등록합니다. 콘텐츠 제공업체의 데이터가 콘텐츠 제공자 프레임워크는 관찰자를 호출합니다. 관찰자에서 다음을 호출합니다. requestSync(): 프레임워크에 실행하도록 알림 동기화 어댑터에 연결합니다.

참고: 스터브 콘텐츠 제공업체를 사용하는 경우 콘텐츠 제공자 및 onChange() 없습니다. 이 경우 기기 데이터 이 메커니즘은 requestSync()입니다.

콘텐츠 제공자의 관찰자를 만들려면 클래스를 확장합니다. ContentObserver하고 다음 형식의 두 가지 형식을 모두 구현합니다. onChange() 메서드를 사용하여 지도 가장자리에 패딩을 추가할 수 있습니다. 포함 onChange(), 통화 requestSync(): 동기화 어댑터를 시작합니다.

관찰자를 등록하려면 다음 호출에서 인수로 전달하세요. registerContentObserver() 포함 보고자 하는 데이터의 콘텐츠 URI도 전달해야 합니다. 콘텐츠 제공자 프레임워크는 이 시계 URI를 인수로 전달된 콘텐츠 URI와 비교합니다. 제공자를 수정하는 ContentResolver 메서드(예: ContentResolver.insert()입니다. 일치하는 항목이 있으면 ContentObserver.onChange() 구현 알 수 있습니다.

다음 코드 스니펫은 ContentObserver를 정의하는 방법을 보여줍니다. requestSync()을 호출하는 변경사항:

Kotlin

// Constants
// Content provider scheme
const val SCHEME = "content://"
// Content provider authority
const val AUTHORITY = "com.example.android.datasync.provider"
// Path for the content provider table
const val TABLE_PATH = "data_table"
...
class MainActivity : FragmentActivity() {
    ...
    // A content URI for the content provider's data table
    private lateinit var uri: Uri
    // A content resolver for accessing the provider
    private lateinit var mResolver: ContentResolver
    ...
    inner class TableObserver(...) : ContentObserver(...) {
        /*
         * Define a method that's called when data in the
         * observed content provider changes.
         * This method signature is provided for compatibility with
         * older platforms.
         */
        override fun onChange(selfChange: Boolean) {
            /*
             * Invoke the method signature available as of
             * Android platform version 4.1, with a null URI.
             */
            onChange(selfChange, null)
        }

        /*
         * Define a method that's called when data in the
         * observed content provider changes.
         */
        override fun onChange(selfChange: Boolean, changeUri: Uri?) {
            /*
             * Ask the framework to run your sync adapter.
             * To maintain backward compatibility, assume that
             * changeUri is null.
             */
            ContentResolver.requestSync(account, AUTHORITY, null)
        }
        ...
    }
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        // Get the content resolver object for your app
        mResolver = contentResolver
        // Construct a URI that points to the content provider data table
        uri = Uri.Builder()
                .scheme(SCHEME)
                .authority(AUTHORITY)
                .path(TABLE_PATH)
                .build()
        /*
         * Create a content observer object.
         * Its code does not mutate the provider, so set
         * selfChange to "false"
         */
        val observer = TableObserver(false)
        /*
         * Register the observer for the data table. The table's path
         * and any of its subpaths trigger the observer.
         */
        mResolver.registerContentObserver(uri, true, observer)
        ...
    }
    ...
}

자바

public class MainActivity extends FragmentActivity {
    ...
    // Constants
    // Content provider scheme
    public static final String SCHEME = "content://";
    // Content provider authority
    public static final String AUTHORITY = "com.example.android.datasync.provider";
    // Path for the content provider table
    public static final String TABLE_PATH = "data_table";
    // Account
    public static final String ACCOUNT = "default_account";
    // Global variables
    // A content URI for the content provider's data table
    Uri uri;
    // A content resolver for accessing the provider
    ContentResolver mResolver;
    ...
    public class TableObserver extends ContentObserver {
        /*
         * Define a method that's called when data in the
         * observed content provider changes.
         * This method signature is provided for compatibility with
         * older platforms.
         */
        @Override
        public void onChange(boolean selfChange) {
            /*
             * Invoke the method signature available as of
             * Android platform version 4.1, with a null URI.
             */
            onChange(selfChange, null);
        }
        /*
         * Define a method that's called when data in the
         * observed content provider changes.
         */
        @Override
        public void onChange(boolean selfChange, Uri changeUri) {
            /*
             * Ask the framework to run your sync adapter.
             * To maintain backward compatibility, assume that
             * changeUri is null.
             */
            ContentResolver.requestSync(mAccount, AUTHORITY, null);
        }
        ...
    }
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        // Get the content resolver object for your app
        mResolver = getContentResolver();
        // Construct a URI that points to the content provider data table
        uri = new Uri.Builder()
                  .scheme(SCHEME)
                  .authority(AUTHORITY)
                  .path(TABLE_PATH)
                  .build();
        /*
         * Create a content observer object.
         * Its code does not mutate the provider, so set
         * selfChange to "false"
         */
        TableObserver observer = new TableObserver(false);
        /*
         * Register the observer for the data table. The table's path
         * and any of its subpaths trigger the observer.
         */
        mResolver.registerContentObserver(uri, true, observer);
        ...
    }
    ...
}

동기화 어댑터를 주기적으로 실행

실행 간의 대기 시간을 설정하여 동기화 어댑터를 주기적으로 실행할 수 있습니다. 하루 중 특정 시간에 게재하거나 둘 다에 실행할 수 있습니다. 동기화 어댑터 실행 주기적으로 서버의 업데이트 간격과 거의 일치시킬 수 있습니다.

마찬가지로 서버가 비교적 유휴 상태일 때 다음과 같은 방법으로 기기에서 데이터를 업로드할 수 있습니다. 동기화 어댑터가 밤에 실행되도록 예약하는 것입니다. 대부분의 사용자는 전원을 켠 상태로 전원에 연결된 상태로 둡니다. 이 시간은 일반적으로 예약 가능한 시간대입니다. 또한, 장치는 동기화 어댑터와 동시에 통신해야 합니다. 그러나 이 접근 방식을 취하는 경우에는 각 기기는 약간 다른 시간에 데이터 전송을 트리거합니다. 모든 장치가 동기화 어댑터를 동시에 사용하면 서버와 셀 제공업체 데이터에 과부하가 걸리기도 합니다. 네트워크

일반적으로 사용자가 즉각적인 업데이트가 필요하지 않지만 정기적으로 업데이트됩니다. 또한 주기적 실행은 가용성이 균형을 이루고자 할 때 기기를 과도하게 사용하지 않는 소규모 동기화 어댑터 실행의 효율성으로 최신 데이터 제공 리소스를 배포합니다

동기화 어댑터를 일정한 간격으로 실행하려면 다음을 호출합니다. addPeriodicSync() 이렇게 하면 일정 시간이 경과한 후에 실행되도록 합니다. 동기화 어댑터 프레임워크는 다른 동기화 어댑터 실행을 고려해야 하고 배터리 효율성을 극대화하려고 하므로 경과 시간은 몇 초 정도 차이가 날 수 있습니다. 또한 동기화 중인 경우 동기화 어댑터는 네트워크를 사용할 수 없습니다.

addPeriodicSync()는 동기화 어댑터를 실행하면 안 됩니다. 대략 반복 알람을 트리거로 사용하세요. 반복 알람에 대해서는 다음에서 자세히 설명합니다. 자세한 내용은 AlarmManager 참조 문서를 확인하세요. 설정할 setInexactRepeating() 메서드 시간 차이가 약간 있는 경우에도 시작 시간을 무작위로 지정하여 동기화 어댑터가 시차를 두고 다른 장치에서 실행되도록 합니다.

addPeriodicSync() 메서드가 setSyncAutomatically() 사용 중지, 따라서 비교적 짧은 시간 내에 동기화를 여러 번 실행할 수 있습니다. 또한 일부 동기화 어댑터 제어 플래그는 addPeriodicSync() 인코더-디코더에 참조 문서에 설명되어 있습니다. addPeriodicSync()입니다.

다음 코드 스니펫에서는 주기적 동기화 어댑터의 실행 시점을 예약하는 방법을 보여줍니다.

Kotlin

// Content provider authority
const val AUTHORITY = "com.example.android.datasync.provider"
// Account
const val ACCOUNT = "default_account"
// Sync interval constants
const val SECONDS_PER_MINUTE = 60L
const val SYNC_INTERVAL_IN_MINUTES = 60L
const val SYNC_INTERVAL = SYNC_INTERVAL_IN_MINUTES * SECONDS_PER_MINUTE
...
class MainActivity : FragmentActivity() {
    ...
    // A content resolver for accessing the provider
    private lateinit var mResolver: ContentResolver

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        // Get the content resolver for your app
        mResolver = contentResolver
        /*
         * Turn on periodic syncing
         */
        ContentResolver.addPeriodicSync(
                mAccount,
                AUTHORITY,
                Bundle.EMPTY,
                SYNC_INTERVAL)
        ...
    }
    ...
}

자바

public class MainActivity extends FragmentActivity {
    ...
    // Constants
    // Content provider authority
    public static final String AUTHORITY = "com.example.android.datasync.provider";
    // Account
    public static final String ACCOUNT = "default_account";
    // Sync interval constants
    public static final long SECONDS_PER_MINUTE = 60L;
    public static final long SYNC_INTERVAL_IN_MINUTES = 60L;
    public static final long SYNC_INTERVAL =
            SYNC_INTERVAL_IN_MINUTES *
            SECONDS_PER_MINUTE;
    // Global variables
    // A content resolver for accessing the provider
    ContentResolver mResolver;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        // Get the content resolver for your app
        mResolver = getContentResolver();
        /*
         * Turn on periodic syncing
         */
        ContentResolver.addPeriodicSync(
                mAccount,
                AUTHORITY,
                Bundle.EMPTY,
                SYNC_INTERVAL);
        ...
    }
    ...
}

요청 시 동기화 어댑터 실행

사용자 요청에 응답하여 동기화 어댑터를 실행하는 것은 가장 바람직하지 않은 전략입니다. 동기화 어댑터 실행을 위해 코드를 바인딩합니다 이 프레임워크는 배터리 전원을 절약하도록 특별히 설계되었습니다. 일정에 따라 동기화 어댑터를 실행할 때 데이터에 대한 응답으로 동기화를 실행하는 옵션 새로운 데이터를 제공하는 데 전력이 사용되므로 배터리 전원을 효과적으로 사용합니다.

이에 비해 사용자가 요청 시 동기화를 실행하도록 허용하면 동기화가 자체적으로 실행되므로 네트워크와 전력 자원을 비효율적으로 사용하는 것입니다. 또한 주문형 동기화를 제공하면 사용자는 데이터가 변경되었다는 증거가 없더라도 동기화를 요청하고 배터리 전원을 비효율적으로 사용하는 것입니다. 일반적으로 앱은 다음 중 하나를 충족해야 합니다. 다른 신호를 사용하여 동기화를 트리거하거나 사용자 입력 없이 일정한 간격으로 신호를 예약합니다.

그러나 여전히 요청 시 동기화 어댑터를 실행하고 싶다면 수동으로 동기화 어댑터를 실행한 다음 ContentResolver.requestSync()

다음 플래그를 사용하여 요청 시 전송을 실행하세요.

SYNC_EXTRAS_MANUAL
수동 동기화를 강제합니다. 동기화 어댑터 프레임워크는 기존 설정을 무시하고 예를 들어 setSyncAutomatically()에서 설정한 플래그와 같은 것입니다.
SYNC_EXTRAS_EXPEDITED
동기화가 즉시 시작되도록 강제합니다. 이 매개변수를 설정하지 않으면 시스템에서 초 후에 동기화 요청을 실행하기 때문입니다. 단기간에 많은 요청을 예약하는 것입니다.

다음 코드 스니펫은 버튼에 대한 응답으로 표시되는 requestSync() 클릭:

Kotlin

// Constants
// Content provider authority
val AUTHORITY = "com.example.android.datasync.provider"
// Account type
val ACCOUNT_TYPE = "com.example.android.datasync"
// Account
val ACCOUNT = "default_account"
...
class MainActivity : FragmentActivity() {
    ...
    // Instance fields
    private lateinit var mAccount: Account
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        /*
         * Create the placeholder account. The code for CreateSyncAccount
         * is listed in the lesson Creating a Sync Adapter
         */

        mAccount = createSyncAccount()
        ...
    }

    /**
     * Respond to a button click by calling requestSync(). This is an
     * asynchronous operation.
     *
     * This method is attached to the refresh button in the layout
     * XML file
     *
     * @param v The View associated with the method call,
     * in this case a Button
     */
    fun onRefreshButtonClick(v: View) {
        // Pass the settings flags by inserting them in a bundle
        val settingsBundle = Bundle().apply {
            putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true)
            putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true)
        }
        /*
         * Request the sync for the default account, authority, and
         * manual sync settings
         */
        ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle)
    }

Java

public class MainActivity extends FragmentActivity {
    ...
    // Constants
    // Content provider authority
    public static final String AUTHORITY =
            "com.example.android.datasync.provider";
    // Account type
    public static final String ACCOUNT_TYPE = "com.example.android.datasync";
    // Account
    public static final String ACCOUNT = "default_account";
    // Instance fields
    Account mAccount;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        /*
         * Create the placeholder account. The code for CreateSyncAccount
         * is listed in the lesson Creating a Sync Adapter
         */

        mAccount = CreateSyncAccount(this);
        ...
    }
    /**
     * Respond to a button click by calling requestSync(). This is an
     * asynchronous operation.
     *
     * This method is attached to the refresh button in the layout
     * XML file
     *
     * @param v The View associated with the method call,
     * in this case a Button
     */
    public void onRefreshButtonClick(View v) {
        // Pass the settings flags by inserting them in a bundle
        Bundle settingsBundle = new Bundle();
        settingsBundle.putBoolean(
                ContentResolver.SYNC_EXTRAS_MANUAL, true);
        settingsBundle.putBoolean(
                ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
        /*
         * Request the sync for the default account, authority, and
         * manual sync settings
         */
        ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle);
    }