首页 > 解决方案 > 如何与安卓联系人同步

问题描述

我正在尝试通过电话号码将我的 Firestore 用户与用户联系人同步。由于解析电话号码和匹配数据库的过程需要时间(只有 3 个用户大约需要 20 秒),并且由于应用程序也应该离线工作,即当应用程序不活动时同步,所以我似乎需要使用 SyncAdapter,但它非常复杂。

我主要(不仅)遵循了下一个教程:

https://developer.android.com/training/sync-adapters

我最初的困境是:

  1. 我还需要制作 Stub-Provider 还是可以使用 android 联系人?如果是这样,如何在 Manifest 中定义它?

  2. 联系人更改时如何触发syncAdapter?

在这段代码中,我离开了 Stub Provider 并尝试通过创建观察者来触发同步适配器,但我可以看到 toast 消息“联系人已更改!” (例如删除联系人)仅在应用程序处于活动状态时。如果我关闭应用程序,我看不到吐司。我不确定这是因为 Toast 没有上下文还是因为观察者没有激活。

    public class MainActivity extends AppCompatActivity {

    private static final String TAG = "[MainActivity]: ";

    private Context mContext = this;

    Account mAccount;

    public class TableObserver extends ContentObserver {

        public TableObserver(Handler handler) {
            super( handler );
        }

        @Override
        public void onChange(boolean selfChange) {

            runOnUiThread( new Runnable() {
                @Override
                public void run() {
                    Toast.makeText( mContext, "Contacts Changed!-1", Toast.LENGTH_SHORT ).show();

                }
            } );
            onChange(selfChange, null);
        }

        @Override
        public void onChange(boolean selfChange, Uri changeUri) {

            runOnUiThread( new Runnable() {
                @Override
                public void run() {
                    Toast.makeText( mContext, "Contacts Changed!-2", Toast.LENGTH_SHORT ).show();
                }
            } );

            ContentResolver.requestSync(mAccount, Constants.AUTHORITY, null);
        }

        @Override
        public boolean deliverSelfNotifications() {
            return true;
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.activity_main );

        // Create the dummy account
        mAccount = CreateSyncAccount(this);

        getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true,
                new TableObserver(null));

    }

    public static Account CreateSyncAccount(Context context) {

        Account newAccount = new Account(
                Constants.ACCOUNT, Constants.ACCOUNT_TYPE);

        AccountManager accountManager =
                (AccountManager) context.getSystemService(
                        ACCOUNT_SERVICE);

        if (accountManager.addAccountExplicitly(newAccount, null, null)) {

            Toast.makeText( context, "i think account created successful", Toast.LENGTH_SHORT ).show();

            return newAccount;
        } else {

            Toast.makeText( context, "some error in account creation", Toast.LENGTH_SHORT ).show();

            //TODO: handle errors
            return newAccount;
        }
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.ranslist.android.ranslist">

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


    <application
....

        <provider
            android:name=".sync.StubProvider"
            android:authorities="com.ranslist.android.ranslist.provider"
            android:exported="false"
            android:syncable="true"/>

        <service android:name=".sync.AuthenticationService" >
            <intent-filter>
                <action android:name="android.accounts.AccountAuthenticator" />
            </intent-filter>

            <meta-data
                android:name="android.accounts.AccountAuthenticator"
                android:resource="@xml/authenticator" />
        </service>

        <service
            android:name=".sync.SyncService"
            android:exported="true"
            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>

...

        <service
            android:name="com.google.android.gms.measurement.AppMeasurementJobService"
            android:enabled="true"
            android:exported="false"
            android:permission="android.permission.BIND_JOB_SERVICE" />


....
    </application>

</manifest>

身份验证器.xml

<account-authenticator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:accountType="example.com"
    android:icon="@drawable/ic_launcher_foreground"
    android:smallIcon="@drawable/ic_launcher_foreground"
    android:label="@string/app_name"/>

同步适配器.xml

<sync-adapter
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:contentAuthority="com.android.contacts"
    android:accountType="example.com"
    android:userVisible="false"
    android:supportsUploading="false"
    android:allowParallelSyncs="false"
    android:isAlwaysSyncable="false"/>

常数

public class Constants {

    public static final String AUTHORITY = "com.example.android.datasync.provider";

    public static final String ACCOUNT_TYPE = "example.com";

    public static final String ACCOUNT = "dummyaccount";
}

标签: android

解决方案


推荐阅读