进程保活-账号同步实现

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

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

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

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

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

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

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

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

具体实现:

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

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);
    }

}

2.账号服务注册

注册一个服务 , 该服务的 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<ActivityManager.RunningServiceInfo> 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的话,需要申请一个悬浮窗权限

    <uses-permission android:name="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

<?xml version="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

<?xml version="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"/>
<!--    android:isAlwaysSyncable 属性 , 表示该账户同步操作 , 是否总是同步 , 这里设置 true , 账户拉活 , 越频繁越好 ;-->
<!--    android:userVisible 属性 , 表示是否在 " 设置 -> 账号 " 界面 , 展示一个账户同步开关 , 这里选择 false , 不给用户展示 , 万一用户给关了 , 就无法进行账户拉活应用进程操作 ;-->

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

AppProcessKeepAlive: 进程保活来的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值