我正在为我的应用程序构建一个同步适配器,我希望在每隔1小时查询一次数据库中的更改后向用户显示通知,即使应用程序将被终止。
我已经成功创建了同步适配器,但是onPermformSync()仅是第一次调用,并向我显示通知,然后在特定时间间隔(1小时)后它不会被调用,即使该应用程序仍在屏幕上显示(前景)
注意:我使用的是SyncAdapter而不是WorkManager,因为WorkManager(最新版本)在某些中国手机(例如Oppo Real Me 2 Pro,Real Me 1等)中无法正常工作。
编辑:此问题主要与上述设备(如Oppo Real Me 2 Pro)有关,并且在轮询频率(定期同步)为60秒的其他设备中也可以正常工作
我已经创建了StubContentProvider和StubAuthenticator。
下面是我的代码
ConfigurationSyncAdapter
public class ConfigurationSyncAdapter extends AbstractThreadedSyncAdapter {
public ConfigurationSyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
}
@Override
public void onPerformSync(Account account, Bundle bundle, String s, ContentProviderClient contentProviderClient, SyncResult syncResult) {
Log.e("sync","onPerformSync is called");
showNotification(getContext(),"The query","let'see");
}
private void showNotification(Context context, String title, String message){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
CharSequence name = "RandomName";
String description = "RandomDescription";
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel =
new NotificationChannel("ID1", name, importance);
channel.setDescription(description);
// Add the channel
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
notificationManager.createNotificationChannel(channel);
}
}
// Create the notification
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "ID1")
//used notification icon for testing purpose
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(title)
.setStyle(new NotificationCompat.BigTextStyle().bigText(message))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setVibrate(new long[0]);
// Show the notification
NotificationManagerCompat.from(context).notify(1, builder.build());
Log.e("WorkManager","Notification shown");
}
}
ConfigurationSyncAdapterService
public class ConfigurationSyncAdapterService extends Service {
private ConfigurationSyncAdapter syncAdapter = null;
private static final Object syncAdapterLock = new Object();
@Override
public void onCreate() {
super.onCreate();
synchronized (syncAdapterLock){
if(syncAdapter == null)
syncAdapter = new ConfigurationSyncAdapter(getApplicationContext(),true);
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return syncAdapter.getSyncAdapterBinder();
}
}
SyncAdapterManager
class SyncAdapterManager {
private static final String TAG = SyncAdapterManager.class.getSimpleName();
private final String authority;
private final String type;
private Account account;
private Context context;
SyncAdapterManager(final Context context) {
this.context = context;
type = context.getString(R.string.account_type);
authority = context.getString(R.string.authority);
account = new Account(context.getString(R.string.app_name), type);
}
@SuppressWarnings ("MissingPermission")
void beginPeriodicSync(final long updateConfigInterval) {
Log.d(TAG, "beginPeriodicSync() called with: updateConfigInterval = [" +
updateConfigInterval + "]");
final AccountManager accountManager = (AccountManager) context
.getSystemService(ACCOUNT_SERVICE);
if (!accountManager.addAccountExplicitly(account, null, null)) {
account = accountManager.getAccountsByType(type)[0];
}
setAccountSyncable();
ContentResolver.addPeriodicSync(account, context.getString(R.string.authority),
Bundle.EMPTY, updateConfigInterval);
ContentResolver.setSyncAutomatically(account, authority, true);
}
void syncImmediately() {
Bundle settingsBundle = new Bundle();
settingsBundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
settingsBundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
ContentResolver.requestSync(account, authority, settingsBundle);
}
private void setAccountSyncable() {
if (ContentResolver.getIsSyncable(account, authority) == 0) {
ContentResolver.setIsSyncable(account, authority, 1);
}
}
}
MainActivity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SyncAdapterManager syncAdapterManager = new SyncAdapterManager(this);
syncAdapterManager.beginPeriodicSync(60*60);
Log.e("sync","onCreate of Main Activity");
}
}
syncadapter.xml
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="@string/account_type"
android:allowParallelSyncs="false"
android:contentAuthority="@string/authority"
android:isAlwaysSyncable="true"
android:supportsUploading="false"
android:userVisible="false">
</sync-adapter>
authenticator.xml
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="@string/account_type"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:smallIcon="@mipmap/ic_launcher" />
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.androidians.syncsample">
<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"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".AuthenticatorService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator"/>
</service>
<provider
android:authorities="@string/authority"
android:name=".StubContentProvider"
android:exported="false"
android:syncable="true"/>
<service android:name=".ConfigurationSyncAdapterService"
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>
</application>
</manifest>