public class

MultiInstanceInvalidationService

extends Service

 java.lang.Object

↳Service

↳androidx.room.MultiInstanceInvalidationService

Gradle dependencies

compile group: 'androidx.room', name: 'room-runtime', version: '2.5.0-alpha01'

  • groupId: androidx.room
  • artifactId: room-runtime
  • version: 2.5.0-alpha01

Artifact androidx.room:room-runtime:2.5.0-alpha01 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.room:room-runtime android.arch.persistence.room:runtime

Overview

A for remote invalidation among multiple InvalidationTracker instances. This service runs in the main app process. All the instances of InvalidationTracker (potentially in other processes) has to connect to this service.

The intent to launch it can be specified by RoomDatabase.Builder.setMultiInstanceInvalidationServiceIntent(Intent), although the service is defined in the manifest by default so there should be no need to override it in a normal situation.

Summary

Constructors
publicMultiInstanceInvalidationService()

Methods
public IBinderonBind(Intent intent)

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public MultiInstanceInvalidationService()

Methods

public IBinder onBind(Intent intent)

Source

/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.room;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.HashMap;

/**
 * A {@link Service} for remote invalidation among multiple {@link InvalidationTracker} instances.
 * This service runs in the main app process. All the instances of {@link InvalidationTracker}
 * (potentially in other processes) has to connect to this service.
 *
 * <p>The intent to launch it can be specified by
 * {@link RoomDatabase.Builder#setMultiInstanceInvalidationServiceIntent}, although the service is
 * defined in the manifest by default so there should be no need to override it in a normal
 * situation.
 */
@ExperimentalRoomApi
public class MultiInstanceInvalidationService extends Service {

    // synthetic access
    @SuppressWarnings("WeakerAccess")
    int mMaxClientId = 0;

    // synthetic access
    @SuppressWarnings("WeakerAccess")
    final HashMap<Integer, String> mClientNames = new HashMap<>();

    // synthetic access
    @SuppressWarnings("WeakerAccess")
    final RemoteCallbackList<IMultiInstanceInvalidationCallback> mCallbackList =
            new RemoteCallbackList<IMultiInstanceInvalidationCallback>() {
                @Override
                public void onCallbackDied(IMultiInstanceInvalidationCallback callback,
                        Object cookie) {
                    mClientNames.remove((int) cookie);
                }
            };

    private final IMultiInstanceInvalidationService.Stub mBinder =
            new IMultiInstanceInvalidationService.Stub() {

                // Assigns a client ID to the client.
                @Override
                public int registerCallback(IMultiInstanceInvalidationCallback callback,
                        String name) {
                    if (name == null) {
                        return 0;
                    }
                    synchronized (mCallbackList) {
                        int clientId = ++mMaxClientId;
                        // Use the client ID as the RemoteCallbackList cookie.
                        if (mCallbackList.register(callback, clientId)) {
                            mClientNames.put(clientId, name);
                            return clientId;
                        } else {
                            --mMaxClientId;
                            return 0;
                        }
                    }
                }

                // Explicitly removes the client.
                // The client can die without calling this. In that case, mCallbackList
                // .onCallbackDied() can take care of removal.
                @Override
                public void unregisterCallback(IMultiInstanceInvalidationCallback callback,
                        int clientId) {
                    synchronized (mCallbackList) {
                        mCallbackList.unregister(callback);
                        mClientNames.remove(clientId);
                    }
                }

                // Broadcasts table invalidation to other instances of the same database file.
                // The broadcast is not sent to the caller itself.
                @Override
                public void broadcastInvalidation(int clientId, String[] tables) {
                    synchronized (mCallbackList) {
                        String name = mClientNames.get(clientId);
                        if (name == null) {
                            Log.w(Room.LOG_TAG, "Remote invalidation client ID not registered");
                            return;
                        }
                        int count = mCallbackList.beginBroadcast();
                        try {
                            for (int i = 0; i < count; i++) {
                                int targetClientId = (int) mCallbackList.getBroadcastCookie(i);
                                String targetName = mClientNames.get(targetClientId);
                                if (clientId == targetClientId // This is the caller itself.
                                        || !name.equals(targetName)) { // Not the same file.
                                    continue;
                                }
                                try {
                                    IMultiInstanceInvalidationCallback callback =
                                            mCallbackList.getBroadcastItem(i);
                                    callback.onInvalidation(tables);
                                } catch (RemoteException e) {
                                    Log.w(Room.LOG_TAG, "Error invoking a remote callback", e);
                                }
                            }
                        } finally {
                            mCallbackList.finishBroadcast();
                        }
                    }
                }
            };

    @Nullable
    @Override
    public IBinder onBind(@NonNull Intent intent) {
        return mBinder;
    }
}