账户同步的作用 : 如果应用的数据发生了改变 , 可以通过账户进行同步 , 进而与服务器进行数据同步操作 , 执行同步时 , 系统会拉活对应的应用进程 ;
实现的话,主要是应用 APP 中可以注册 " 账户服务 Service " , 应用安装后 , 如果系统发现应用中有该类型服务 , 就会为该应用开放添加账户的功能 ;
系统通过 Binder 机制 , 操作用户的 " 账户服务 Service " ;
第三方应用可以通过该账户服务 , 将数据同步 到服务器中 ;
系统在进行应用账户同步时 , 会自动将对应的应用拉活 ;
Google 官方提供了账户同步案例 , https://github.com/googlearchive/android-BasicSyncAdapter , 已经停止维护了 , 但是项目还是有参考价值的 ; ( 源码放在了本博客的资源文件中 )
在上述项目中指向了推荐的后台任务示例 https://github.com/android/background-tasks-samples ; ( 源码放在了本博客的资源文件中 )
说白了,进程拉活只是账户同步的附带作用 ;
- public class AccountUtil {
-
- private static final String TAG = "AccountHelper";
-
- private static final String ACCOUNT_TYPE = "com.example.appprocesskeepalive.accounttype";
- private static final String ACCOUNT_AUTHORITY = "com.example.appprocesskeepalive.provider";
- private static final String ACCOUNT_NAME = "app账号同步demo";
-
- /**
- * 添加账号
- * @param context
- */
- public static void addAccount(Context context) {
- AccountManager accountManager = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
-
- // 获得此类型的账户
- // 需要增加权限 GET_ACCOUNTS
- Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
-
- if (accounts.length > 0) {
- Log.e(TAG, "账户已存在");
- return;
- }
- Account account = new Account(ACCOUNT_NAME, ACCOUNT_TYPE);
- // 给这个账户类型添加一个账户
- // 需要增加权限 AUTHENTICATE_ACCOUNTS
- accountManager.addAccountExplicitly(account, "xx", new Bundle());
- }
-
- /**
- * 设置账户自动同步
- */
- public static void sync() {
- Account account = new Account(ACCOUNT_NAME, ACCOUNT_TYPE);
-
- // 下面三个都需要同一个权限 WRITE_SYNC_SETTINGS
-
- // 设置同步
- ContentResolver.setIsSyncable(account, ACCOUNT_AUTHORITY, 1);
-
- // 自动同步
- ContentResolver.setSyncAutomatically(account, ACCOUNT_AUTHORITY, true);
-
- // 设置同步周期
- ContentResolver.addPeriodicSync(account, ACCOUNT_AUTHORITY, new Bundle(), 1);
- }
-
- }
注册一个服务 , 该服务的 onBind 方法返回 AbstractAccountAuthenticator 对象的 Binder , 只要该应用安装 , 就可以在 " 设置 -> 账号 " 中查看该应用的账号
- public class AuthService extends Service {
-
- private AccountAuthenticator accountAuthenticator;
-
- @Nullable
- @Override
- public IBinder onBind(Intent intent) {
- return accountAuthenticator.getIBinder();
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- accountAuthenticator = new AccountAuthenticator(this);
- }
-
- public static class AccountAuthenticator extends AbstractAccountAuthenticator {
- private Context mContext;
-
- public AccountAuthenticator(Context context) {
- super(context);
- mContext=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 {
- AccountUtil.addAccount(mContext);
- AccountUtil.sync();
- 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;
- }
- }
- }
3.注册一个ContentProvider
- public class SyncProvider 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;
- }
- }
4. 注册一个服务,这个服务提供一个Action给系统以便系统能找到它;然后就是继承和实现AbstractThreadedSyncAdapter,此类中包含实现了ISyncAdapter.Stub内部类,这个内部类封装了远程接口调用
- public class SyncService extends Service {
-
- private SyncAdapter mSyncAdapter;
-
- private static final String TAG = "SyncService";
-
- @Nullable
- @Override
- public IBinder onBind(Intent intent) {
- return mSyncAdapter.getSyncAdapterBinder();
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- mSyncAdapter = new SyncAdapter(getApplicationContext(), true);
- }
-
- public class SyncAdapter extends AbstractThreadedSyncAdapter {
-
- public SyncAdapter(Context context, boolean autoInitialize) {
- super(context, autoInitialize);
- }
-
- @Override
- public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
- // 账户同步操作
- // 与数据库 , 服务器同步操作 , 这里只是为了应用进程拉活 , 不实现具体的逻辑
- Log.i("AccountSyncService", "账户同步拉活激活");
- Log.i("AccountSyncService","是否还在运行:"+isServiceRunning(App.instance,"com.example.appprocesskeepalive.WakeUpService"));
-
- Intent intent=new Intent(App.instance, WakeUpService.class);
- if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
- ContextCompat.startForegroundService(App.instance, intent);
- }else {
- startService(intent);
- }
- }
- }
-
- public static boolean isServiceRunning(Context context, String serviceName) {
- ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
- List
runningServices = activityManager.getRunningServices(Integer.MAX_VALUE); -
- if (runningServices != null) {
- for (ActivityManager.RunningServiceInfo serviceInfo : runningServices) {
- if (serviceInfo.service.getClassName().equals(serviceName)) {
- return true;
- }
- }
- }
- return false;
- }
-
- }
核心也是这块onPerformSync,收到账号同步的回调,收到后启动一个前台Service,
- public class WakeUpService extends Service {
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- // 在这里启动你的应用
- startForeground(110, getNotification());
- Intent intent2 = getPackageManager().getLaunchIntentForPackage("com.example.appprocesskeepalive");
- if (intent2 != null) {
- //setFlags看自己情况使用,也可以不调用
- intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getApplication().startActivity(intent2);
- }
- return START_STICKY;
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- createNotificationChannel();
- }
-
- private void createNotificationChannel() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- CharSequence name = "My Foreground Service Channel";
- String description = "This channel is used for my foreground service";
- int importance = NotificationManager.IMPORTANCE_DEFAULT;
- NotificationChannel channel = new NotificationChannel("测试保活", name, importance);
- channel.setDescription(description);
- NotificationManager notificationManager = getSystemService(NotificationManager.class);
- notificationManager.createNotificationChannel(channel);
- }
- }
-
- private Notification getNotification() {
- Intent notificationIntent = new Intent(this, MainActivity.class);
- PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
-
- return new NotificationCompat.Builder(this, "测试保活")
- .setContentTitle("My Foreground Service")
- .setContentText("Running...")
- .setSmallIcon(R.drawable.ic_launcher_background)
- .setContentIntent(pendingIntent)
- .build();
- }
-
- }
在service中启动一个activity,但是要注意的是,如果要启动activity的话,需要申请一个悬浮窗权限
"android.permission.SYSTEM_ALERT_WINDOW" />
然后就是配置文件的注册了
- <service
- android:name=".auth.AuthService"
- android:enabled="true"
- android:exported="true">
- <intent-filter>
- <action android:name="android.accounts.AccountAuthenticator" />
- intent-filter>
-
-
- <meta-data
- android:name="android.accounts.AccountAuthenticator"
- android:resource="@xml/account_auth" />
- service>
- <service
- android:name=".auth.SyncService"
- android:enabled="true"
- android:exported="true">
- <intent-filter>
- <action android:name="android.content.SyncAdapter" />
- intent-filter>
-
- <meta-data
- android:name="android.content.SyncAdapter"
- android:resource="@xml/up" />
- service>
-
- <provider
- android:name=".auth.SyncProvider"
- android:authorities="@string/account_authority"
- android:exported="false"
- android:syncable="true" />
- <service
- android:name=".WakeUpService"
- android:enabled="true"
- android:exported="true"
- android:persistent="true"
- android:stopWithTask="false" />
其中 android:persistent是表示app常驻内存的意思,虽然其实没啥大作用,app被杀死了后,它服务该死还是得死--
account_auth.xml
- "1.0" encoding="utf-8"?>
- <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
- android:label="@string/app_name"
- android:icon="@mipmap/ic_launcher"
- android:accountType="@string/account_type"
- android:smallIcon="@mipmap/ic_launcher"/>
这块是用来配置账号同步在设置界面的UI显示的,要注意的时候此处的accountType要跟AbstractAccountAuthenticator执行创建账号、同步账号时的accountType要一样
up.xml
- "1.0" encoding="utf-8"?>
- <sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
- android:accountType="@string/account_type"
- android:contentAuthority="@string/account_authority"
- android:userVisible="true"
- android:supportsUploading="false"
- android:allowParallelSyncs="false"
- android:isAlwaysSyncable="true"/>
写的可能有点乱,具体实现在这个demo: