• 进程保活-账号同步实现


    账户同步的作用 : 如果应用的数据发生了改变 , 可以通过账户进行同步 , 进而与服务器进行数据同步操作 , 执行同步时 , 系统会拉活对应的应用进程 ;

    实现的话,主要是应用 APP 中可以注册 " 账户服务 Service " , 应用安装后 , 如果系统发现应用中有该类型服务 , 就会为该应用开放添加账户的功能 ;

    系统通过 Binder 机制 , 操作用户的 " 账户服务 Service " ;

    第三方应用可以通过该账户服务 , 将数据同步 到服务器中 ;

    系统在进行应用账户同步时 , 会自动将对应的应用拉活 ;

    Google 官方提供了账户同步案例 , https://github.com/googlearchive/android-BasicSyncAdapter , 已经停止维护了 , 但是项目还是有参考价值的 ; ( 源码放在了本博客的资源文件中 )

    在上述项目中指向了推荐的后台任务示例 https://github.com/android/background-tasks-samples ; ( 源码放在了本博客的资源文件中 )

    说白了,进程拉活只是账户同步的附带作用 ;

    具体实现:

    1.创建一个账号同步工具类

    1. public class AccountUtil {
    2. private static final String TAG = "AccountHelper";
    3. private static final String ACCOUNT_TYPE = "com.example.appprocesskeepalive.accounttype";
    4. private static final String ACCOUNT_AUTHORITY = "com.example.appprocesskeepalive.provider";
    5. private static final String ACCOUNT_NAME = "app账号同步demo";
    6. /**
    7. * 添加账号
    8. * @param context
    9. */
    10. public static void addAccount(Context context) {
    11. AccountManager accountManager = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
    12. // 获得此类型的账户
    13. // 需要增加权限 GET_ACCOUNTS
    14. Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
    15. if (accounts.length > 0) {
    16. Log.e(TAG, "账户已存在");
    17. return;
    18. }
    19. Account account = new Account(ACCOUNT_NAME, ACCOUNT_TYPE);
    20. // 给这个账户类型添加一个账户
    21. // 需要增加权限 AUTHENTICATE_ACCOUNTS
    22. accountManager.addAccountExplicitly(account, "xx", new Bundle());
    23. }
    24. /**
    25. * 设置账户自动同步
    26. */
    27. public static void sync() {
    28. Account account = new Account(ACCOUNT_NAME, ACCOUNT_TYPE);
    29. // 下面三个都需要同一个权限 WRITE_SYNC_SETTINGS
    30. // 设置同步
    31. ContentResolver.setIsSyncable(account, ACCOUNT_AUTHORITY, 1);
    32. // 自动同步
    33. ContentResolver.setSyncAutomatically(account, ACCOUNT_AUTHORITY, true);
    34. // 设置同步周期
    35. ContentResolver.addPeriodicSync(account, ACCOUNT_AUTHORITY, new Bundle(), 1);
    36. }
    37. }

    2.账号服务注册

    注册一个服务 , 该服务的 onBind 方法返回 AbstractAccountAuthenticator 对象的 Binder , 只要该应用安装 , 就可以在 " 设置 -> 账号 " 中查看该应用的账号

    1. public class AuthService extends Service {
    2. private AccountAuthenticator accountAuthenticator;
    3. @Nullable
    4. @Override
    5. public IBinder onBind(Intent intent) {
    6. return accountAuthenticator.getIBinder();
    7. }
    8. @Override
    9. public void onCreate() {
    10. super.onCreate();
    11. accountAuthenticator = new AccountAuthenticator(this);
    12. }
    13. public static class AccountAuthenticator extends AbstractAccountAuthenticator {
    14. private Context mContext;
    15. public AccountAuthenticator(Context context) {
    16. super(context);
    17. mContext=context;
    18. }
    19. @Override
    20. public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
    21. return null;
    22. }
    23. @Override
    24. public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType,
    25. String[] requiredFeatures, Bundle options) throws NetworkErrorException {
    26. AccountUtil.addAccount(mContext);
    27. AccountUtil.sync();
    28. return null;
    29. }
    30. @Override
    31. public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account,
    32. Bundle options) throws NetworkErrorException {
    33. return null;
    34. }
    35. @Override
    36. public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
    37. String authTokenType, Bundle options) throws NetworkErrorException {
    38. return null;
    39. }
    40. @Override
    41. public String getAuthTokenLabel(String authTokenType) {
    42. return null;
    43. }
    44. @Override
    45. public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account,
    46. String authTokenType, Bundle options) throws NetworkErrorException {
    47. return null;
    48. }
    49. @Override
    50. public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account,
    51. String[] features) throws NetworkErrorException {
    52. return null;
    53. }
    54. }
    55. }

     3.注册一个ContentProvider

    1. public class SyncProvider extends ContentProvider {
    2. @Override
    3. public boolean onCreate() {
    4. return false;
    5. }
    6. @Nullable
    7. @Override
    8. public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
    9. return null;
    10. }
    11. @Nullable
    12. @Override
    13. public String getType(@NonNull Uri uri) {
    14. return null;
    15. }
    16. @Nullable
    17. @Override
    18. public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
    19. return null;
    20. }
    21. @Override
    22. public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
    23. return 0;
    24. }
    25. @Override
    26. public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
    27. return 0;
    28. }
    29. }

    4. 注册一个服务,这个服务提供一个Action给系统以便系统能找到它;然后就是继承和实现AbstractThreadedSyncAdapter,此类中包含实现了ISyncAdapter.Stub内部类,这个内部类封装了远程接口调用

    1. public class SyncService extends Service {
    2. private SyncAdapter mSyncAdapter;
    3. private static final String TAG = "SyncService";
    4. @Nullable
    5. @Override
    6. public IBinder onBind(Intent intent) {
    7. return mSyncAdapter.getSyncAdapterBinder();
    8. }
    9. @Override
    10. public void onCreate() {
    11. super.onCreate();
    12. mSyncAdapter = new SyncAdapter(getApplicationContext(), true);
    13. }
    14. public class SyncAdapter extends AbstractThreadedSyncAdapter {
    15. public SyncAdapter(Context context, boolean autoInitialize) {
    16. super(context, autoInitialize);
    17. }
    18. @Override
    19. public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
    20. // 账户同步操作
    21. // 与数据库 , 服务器同步操作 , 这里只是为了应用进程拉活 , 不实现具体的逻辑
    22. Log.i("AccountSyncService", "账户同步拉活激活");
    23. Log.i("AccountSyncService","是否还在运行:"+isServiceRunning(App.instance,"com.example.appprocesskeepalive.WakeUpService"));
    24. Intent intent=new Intent(App.instance, WakeUpService.class);
    25. if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
    26. ContextCompat.startForegroundService(App.instance, intent);
    27. }else {
    28. startService(intent);
    29. }
    30. }
    31. }
    32. public static boolean isServiceRunning(Context context, String serviceName) {
    33. ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    34. List runningServices = activityManager.getRunningServices(Integer.MAX_VALUE);
    35. if (runningServices != null) {
    36. for (ActivityManager.RunningServiceInfo serviceInfo : runningServices) {
    37. if (serviceInfo.service.getClassName().equals(serviceName)) {
    38. return true;
    39. }
    40. }
    41. }
    42. return false;
    43. }
    44. }

    核心也是这块onPerformSync,收到账号同步的回调,收到后启动一个前台Service,

    1. public class WakeUpService extends Service {
    2. @Override
    3. public IBinder onBind(Intent intent) {
    4. return null;
    5. }
    6. @Override
    7. public int onStartCommand(Intent intent, int flags, int startId) {
    8. // 在这里启动你的应用
    9. startForeground(110, getNotification());
    10. Intent intent2 = getPackageManager().getLaunchIntentForPackage("com.example.appprocesskeepalive");
    11. if (intent2 != null) {
    12. //setFlags看自己情况使用,也可以不调用
    13. intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    14. getApplication().startActivity(intent2);
    15. }
    16. return START_STICKY;
    17. }
    18. @Override
    19. public void onCreate() {
    20. super.onCreate();
    21. createNotificationChannel();
    22. }
    23. private void createNotificationChannel() {
    24. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    25. CharSequence name = "My Foreground Service Channel";
    26. String description = "This channel is used for my foreground service";
    27. int importance = NotificationManager.IMPORTANCE_DEFAULT;
    28. NotificationChannel channel = new NotificationChannel("测试保活", name, importance);
    29. channel.setDescription(description);
    30. NotificationManager notificationManager = getSystemService(NotificationManager.class);
    31. notificationManager.createNotificationChannel(channel);
    32. }
    33. }
    34. private Notification getNotification() {
    35. Intent notificationIntent = new Intent(this, MainActivity.class);
    36. PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
    37. return new NotificationCompat.Builder(this, "测试保活")
    38. .setContentTitle("My Foreground Service")
    39. .setContentText("Running...")
    40. .setSmallIcon(R.drawable.ic_launcher_background)
    41. .setContentIntent(pendingIntent)
    42. .build();
    43. }
    44. }

    在service中启动一个activity,但是要注意的是,如果要启动activity的话,需要申请一个悬浮窗权限

        "android.permission.SYSTEM_ALERT_WINDOW" />

     然后就是配置文件的注册了

    1. <service
    2. android:name=".auth.AuthService"
    3. android:enabled="true"
    4. android:exported="true">
    5. <intent-filter>
    6. <action android:name="android.accounts.AccountAuthenticator" />
    7. intent-filter>
    8. <meta-data
    9. android:name="android.accounts.AccountAuthenticator"
    10. android:resource="@xml/account_auth" />
    11. service>
    12. <service
    13. android:name=".auth.SyncService"
    14. android:enabled="true"
    15. android:exported="true">
    16. <intent-filter>
    17. <action android:name="android.content.SyncAdapter" />
    18. intent-filter>
    19. <meta-data
    20. android:name="android.content.SyncAdapter"
    21. android:resource="@xml/up" />
    22. service>
    23. <provider
    24. android:name=".auth.SyncProvider"
    25. android:authorities="@string/account_authority"
    26. android:exported="false"
    27. android:syncable="true" />
    28. <service
    29. android:name=".WakeUpService"
    30. android:enabled="true"
    31. android:exported="true"
    32. android:persistent="true"
    33. android:stopWithTask="false" />

    其中 android:persistent是表示app常驻内存的意思,虽然其实没啥大作用,app被杀死了后,它服务该死还是得死--

    account_auth.xml

    1. "1.0" encoding="utf-8"?>
    2. <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:label="@string/app_name"
    4. android:icon="@mipmap/ic_launcher"
    5. android:accountType="@string/account_type"
    6. android:smallIcon="@mipmap/ic_launcher"/>

    这块是用来配置账号同步在设置界面的UI显示的,要注意的时候此处的accountType要跟AbstractAccountAuthenticator执行创建账号、同步账号时的accountType要一样

    up.xml

    1. "1.0" encoding="utf-8"?>
    2. <sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:accountType="@string/account_type"
    4. android:contentAuthority="@string/account_authority"
    5. android:userVisible="true"
    6. android:supportsUploading="false"
    7. android:allowParallelSyncs="false"
    8. android:isAlwaysSyncable="true"/>

    写的可能有点乱,具体实现在这个demo:

    AppProcessKeepAlive: 进程保活来的

  • 相关阅读:
    MindSpore Graph Learning
    【Pandas总结】第七节 Pandas 合并数据集_pd.concat()
    vim的使用笔记
    Ubuntu22.04安装libudev-dev时的Bug
    LeetCode75——Day30
    为了摸鱼,我开发了一个工具网站
    【无标题】
    Zookeeper系列文章-Curator
    Google Earth Engine 教程——Landsat 8 影像集合去云分析QA波段和去云NDVI的影像
    1.32 Cubemx_canfestival对象字典生成工具环境搭建
  • 原文地址:https://blog.csdn.net/z936689039/article/details/134510742