这是我第一次创建同步适配器,我遇到了问题,我按照Android开发者网站https://developer.android.com/training/sync-adapters/creating-sync-adapter.html上的教程进行了操作,但我似乎无法获得同步工作。
我知道我做错了什么,但我自己也搞清楚了。
SyncAdapter。
public class SyncAdapter extends AbstractThreadedSyncAdapter {
// Global variables
// Define a variable to contain a content resolver instance
ContentResolver mContentResolver;
/**
* 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
*/
mContentResolver = 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
*/
mContentResolver = context.getContentResolver();
}
public void onPerformSync(
Account account,
Bundle extras,
String authority,
ContentProviderClient provider,
SyncResult syncResult) {
/*
* Put the data transfer code here.
*/
Log.d("Message: ", "Perform Sync Call");
new JSONAsyncTask().execute("http://example.com?category=1");
}
}
SyncService
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();
}
}
我的 MainActivity
public class MainActivity extends AppCompatActivity{
int id = 0;
public static String CONNECTION_STATUS="";
String TAG ="Message: ";
/** Sync adapter code **/
// Constants
// The authority for the sync adapter's content provider
public static final String AUTHORITY = "com.example.tech6.sampleapp.contentprovider";
// An account type, in the form of a domain name
public static final String ACCOUNT_TYPE = "com.android.example.sampleapp";
// The account name
public static final String ACCOUNT = "dummyaccount";
// Instance fields
Account mAccount;
ContentResolver mResolver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_movies);
// Set the menu icon instead of the launcher icon.
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
ConnectivityManager cm = (ConnectivityManager) getApplicationContext().getSystemService(getApplicationContext().CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
/** Check if connected, then Sync **/
if (ni != null) {
Account mAccount = new Account(
ACCOUNT, ACCOUNT_TYPE);
Bundle extras = new Bundle();
mResolver.setIsSyncable(mAccount, AUTHORITY, 1);
mResolver.setSyncAutomatically(mAccount, AUTHORITY, true);
mResolver.requestSync(mAccount, AUTHORITY, extras);
}
}
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<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" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Base.Theme.DesignDemo" >
<activity
android:name=".activity.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name=".contentprovider.CinemalistingContentProvider"
android:authorities="com.example.tech6.providersample.contentprovider" >
</provider>
的AndroidManifest.xml
<service
android:name=".helpers.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/sync_adapter" />
</service>
</application>
</manifest>
XML / sync_adapter.xml
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter
xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="com.example.tech6.sampleapp.contentprovider"
android:accountType="com.android.example.sampleapp"
android:userVisible="true"
android:supportsUploading="true"
android:allowParallelSyncs="true"
android:isAlwaysSyncable="true"/>
感谢任何帮助,谢谢。
答案 0 :(得分:0)
您必须在AccountManager中添加虚拟帐户才能在框架中注册SyncAdapter。
在new Account(...)
之后,您必须拨打以下电话:
// 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(mAccount, 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.
*/
Bundle extras = new Bundle();
mResolver.setIsSyncable(mAccount, AUTHORITY, 1);
mResolver.setSyncAutomatically(mAccount, AUTHORITY, true);
mResolver.requestSync(mAccount, AUTHORITY, extras);
} else {
/*
* The account exists or some other error occurred. Log this, report it,
* or handle it internally.
*/
}
另外,不要忘记使用Sync Adapter MetaData文件在Manifest中声明SyncAdapter。
希望这有帮助。
答案 1 :(得分:0)
首先创建存根验证器。
public class Authenticator extends AbstractAccountAuthenticator {
public Authenticator(Context context) {
super(context);
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
return null;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public String getAuthTokenLabel(String authTokenType) {
return null;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
return null;
}
}
使用service将身份验证器绑定到同步适配器框架。
public class AuthenticatorService extends Service {
// Instance field that stores the authenticator object
private Authenticator mAuthenticator;
@Override
public void onCreate() {
// Create a new authenticator object
mAuthenticator = new Authenticator(this);
}
/*
* When the system binds to this Service to make the RPC call
* return the authenticator's IBinder.
*/
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mAuthenticator.getIBinder();
}
}
3。创建一个文件res / xml / authenticator.xml以添加身份验证器的元数据文件
authenticator.xml的内容如下:
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator
xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="client.example.com"
android:icon="@mipmap/ic_launcher"
android:smallIcon="@mipmap/ic_launcher"
android:label="@string/app_name"/>
现在我们需要创建一个存根内容提供商 注意:如果您的应用程序中已有内容提供程序,则不需要存根内容提供程序,因为我没有任何有效的内容提供程序,并且同步适配器框架需要内容提供程序,因此请创建存根内容提供程序。您的应用没有内容提供商,同步适配器会崩溃。 1.添加存根内容提供商
public class StubProvider extends ContentProvider{
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
}
2。在清单中声明提供者:
<provider
android:authorities="app_package.provider"
android:name=".StubProvider"
android:exported="false"
android:syncable="true">
</provider>
创建同步适配器 1.创建一个同步适配器类:
public class SyncAdapter extends AbstractThreadedSyncAdapter {
private static final String TAG = SyncAdapter.class.getSimpleName();
// Global variables
// Define a variable to contain a content resolver instance
ContentResolver mContentResolver;
/**
* 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
*/
mContentResolver = context.getContentResolver();
Log.d(TAG, "SyncAdapter: ctr");
}
/**
* 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
*/
mContentResolver = context.getContentResolver();
Log.d(TAG, "SyncAdapter: ctr");
}
/*
* 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) {
Log.d(TAG, "onPerformSync: + called");
// here write your logic, what exactly you want to sync
}
}
2。使用服务将同步适配器绑定到同步适配器框架
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();
@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.
*
*/
@Nullable
@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();
}
}
3.now添加同步适配器元数据文件(res / xml / syncadapter.xml)
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter
xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="app_package.provider"
android:accountType="client.example.com"
android:userVisible="false"
android:supportsUploading="false"
android:allowParallelSyncs="false"
android:isAlwaysSyncable="true"/>
4。在清单中声明同步适配器
<service
android:name=".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>
5。添加同步适配器框架所需的帐户
public class MainActivity extends FragmentActivity{
// Constants
// The authority for the sync adapter's content provider
public static final String AUTHORITY = "app_package.provider";
// An account type, in the form of a domain name
public static final String ACCOUNT_TYPE = "client.example.com";
// The account name
public static final String ACCOUNT = "dummy_account";
// Instance fields
public Account mAccount;
@Override
protected void onCreate(Bundle savedInstanceState) {
mAccount = CreateSyncAccount(this);
}
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.
*/
accountManager.removeAccountExplicitly(newAccount);
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.
*/
Log.d(TAG, "*******CreateSyncAccount: successful ");
ContentResolver.setSyncAutomatically(newAccount, AUTHORITY, true);
} else {
/*
* The account exists or some other error occurred. Log this, report it,
* or handle it internally.
*/
Log.d(TAG, "*******CreateSyncAccount: error occured ");
}
return newAccount;
}
}
注意:现在运行您的应用程序。现在转到设置,然后搜索帐户。.单击用户和帐户。 这里将列出您的dummy_account。单击dummy_account->帐户同步->立即同步。
同步适配器的onPerformSync将被调用。
现在您应该调用ContentResolver.requestSync(mAccount,AUTHORITY,new Bundle());根据您的要求。