android开发工作随笔述术Android 开发相关文章收集

android账号创建,管理,同步

2016-09-20  本文已影响2622人  81bad73e9053

1.AccountManager获取账户信息

AccountManager.html

1.1 AccountManager的获取

AccountManager mAccountManager = (AccountManager) getSystemService(ACCOUNT_SERVICE);

或者

AccountManager accountManager = AccountManager.get(context); 

1.2 账户的获取

首先需要权限

<uses-permission android:name="android.permission.GET_ACCOUNTS"/>  

获取账户的方法

//Lists all accounts of any type registered on the device.获取所有账户
 getAccounts()
//Lists all accounts of a particular type.获取指定类型的账户
getAccountsByType(String type) 

2. 账户服务

要建立自己的账号信息,就要在manifest里声明一个关于账号的service
如davdroid的manifest文件中有如下代码

        <service
            android:name=".syncadapter.AccountAuthenticatorService"
            android:exported="false" >
            <intent-filter>
                <action android:name="android.accounts.AccountAuthenticator" />
            </intent-filter> 
            <meta-data
                android:name="android.accounts.AccountAuthenticator"
                android:resource="@xml/account_authenticator" />
        </service>

其中account_authenticator如下

<account-authenticator 
xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="bitfire.at.davdroid"   
android:icon="@mipmap/ic_launcher"   
android:label="@string/app_name"   
android:smallIcon="@mipmap/ic_launcher"  
/>

2.1 authenticatorservice的实现

这里实现的很简单,分为以下几个部分

public class AccountAuthenticatorService extends Service {

    private AccountAuthenticator accountAuthenticator;

    @Override
    public void onCreate() {
        accountAuthenticator = new AccountAuthenticator(this);
        accountAuthenticator.toString();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.e("AccountAuthenticator", accountAuthenticator.toString());
    }

    @Override
    public IBinder onBind(Intent intent) {
        if (intent.getAction().equals(
                android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT))
            return accountAuthenticator.getIBinder();
        return null;
    }

    private static class AccountAuthenticator extends
            AbstractAccountAuthenticator {
        final Context context;

        public AccountAuthenticator(Context context) {
            super(context);
            this.context = context;
        }

        @Override
        public Bundle addAccount(AccountAuthenticatorResponse response,
                String accountType, String authTokenType,
                String[] requiredFeatures, Bundle options)
                throws NetworkErrorException {
            Intent intent = new Intent(context, LoginActivity.class);
            intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,
                    response);
            Bundle bundle = new Bundle();
            bundle.putParcelable(AccountManager.KEY_INTENT, intent);
            return bundle;
        }

        @Override
        public Bundle confirmCredentials(AccountAuthenticatorResponse response,
                Account account, Bundle options) throws NetworkErrorException {
            return null;
        }

        @Override
        public Bundle editProperties(AccountAuthenticatorResponse response,
                String accountType) {
            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 hasFeatures(AccountAuthenticatorResponse response,
                Account account, String[] features)
                throws NetworkErrorException {
            return null;
        }

        @Override
        public Bundle updateCredentials(AccountAuthenticatorResponse response,
                Account account, String authTokenType, Bundle options)
                throws NetworkErrorException {
            return null;
        }

    }
}

完成上面的步骤之后,在系统的设置里,就会出现这个类型的账户了

3 添加账号

首先需要权限

 <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>

3.1 重写authenticator的addAccount方法

        @Override
        public Bundle addAccount(AccountAuthenticatorResponse response,
                String accountType, String authTokenType,
                String[] requiredFeatures, Bundle options)
                throws NetworkErrorException {
            Intent intent = new Intent(context, LoginActivity.class);
            intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
            Bundle bundle = new Bundle();
            bundle.putParcelable(AccountManager.KEY_INTENT, intent);
            return bundle;
        }

3.2 调用addAccountExplicitly

accountManager.addAccountExplicitly(account, config.password, userData)

这样就把一个账户添加进去了,其中account参数会制定账户类型和名称

4.账户信息的同步

4.1 自动同步

             ContentResolver.setSyncAutomatically(account, authority, true);  //自动同步
             ContentResolver.addPeriodicSync(account, authority, new Bundle(), seconds);//设置同步时间间隔

4.2 手动同步

  ContentResolver.setSyncAutomatically(account, authority, false);  //手动同步

4.3 davdroid中的同步的调用实现

     
     public Long getSyncInterval(@NonNull String authority) {
         if (ContentResolver.getIsSyncable(account, authority) <= 0)
             return null;

         if (ContentResolver.getSyncAutomatically(account, authority)) {
             List<PeriodicSync> syncs = ContentResolver.getPeriodicSyncs(account, authority);
             if (syncs.isEmpty())
                 return SYNC_INTERVAL_MANUALLY;
             else
                 return syncs.get(0).period;
         } else
             return SYNC_INTERVAL_MANUALLY;
     }
   
     public void setSyncInterval(@NonNull String authority, long seconds) {
         if (seconds == SYNC_INTERVAL_MANUALLY) {
             //手动同步
             ContentResolver.setSyncAutomatically(account, authority, false);
         } else {//自动同步 
             ContentResolver.setSyncAutomatically(account, authority, true);  //自动同步
             ContentResolver.addPeriodicSync(account, authority, new Bundle(), seconds);//设置同步时间间隔
         }
     }

4.4 开发流程

4.4.1 创建SyncService

Sync服务一般工作在独立的进程, 并且可由操作系统调度, 当然要实现自己的Binder接口, 好在Google已经提供了非常简单的抽象类AbstractThreadedSyncAdapter来完成这件事情

public class SyncService extends Service{

    private static final Object syncLock = new Object();
    private static SyncAdapter syncAdapter = null;

    @Override
    public void onCreate() {
        super.onCreate();
        synchronized (syncLock) {
            if (syncAdapter == null) {
                syncAdapter = new SyncAdapter(getApplicationContext(), true);
            }
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return syncAdapter.getSyncAdapterBinder();
    }


    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) {
            //do sync here
        }
    }
}

davdroid中syncservice的实现

public abstract class SyncAdapterService extends Service {

    abstract protected AbstractThreadedSyncAdapter syncAdapter();

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return syncAdapter().getSyncAdapterBinder();
    }

    public static abstract class SyncAdapter extends
            AbstractThreadedSyncAdapter {

        public SyncAdapter(Context context) {
            super(context, false);
        }

        @Override
        public void onPerformSync(Account account, Bundle extras,
                String authority, ContentProviderClient provider,
                SyncResult syncResult) {
            App.log.info("Sync for " + authority + " has been initiated"); 
            // required for dav4android (ServiceLoader)
            Thread.currentThread().setContextClassLoader(getContext().getClassLoader());
        }

        @Override
        public void onSecurityException(Account account, Bundle extras,
                String authority, SyncResult syncResult) {
            App.log.log(Level.WARNING,
                    "Security exception when opening content provider for "
                            + authority);
            syncResult.databaseError = true;

            Intent intent = new Intent(getContext(), PermissionsActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

            Notification notify = new NotificationCompat.Builder(getContext())
                    .setSmallIcon(R.drawable.ic_error_light)
                    .setLargeIcon(App.getLauncherBitmap(getContext()))
                    .setContentTitle( getContext().getString(R.string.sync_error_permissions))
                    .setContentText( getContext().getString(R.string.sync_error_permissions_text))
                    .setContentIntent(
                            PendingIntent.getActivity(getContext(), 0, intent,
                                    PendingIntent.FLAG_CANCEL_CURRENT))
                    .setCategory(NotificationCompat.CATEGORY_ERROR).build();
            NotificationManagerCompat nm = NotificationManagerCompat
                    .from(getContext());
            nm.notify(Constants.NOTIFICATION_PERMISSIONS, notify);
        }

        protected boolean checkSyncConditions(@NonNull AccountSettings settings) {
            if (settings.getSyncWifiOnly()) {
                ConnectivityManager cm = (ConnectivityManager) getContext()
                        .getSystemService(CONNECTIVITY_SERVICE);
                NetworkInfo network = cm.getActiveNetworkInfo();
                if (network == null) {
                    App.log.info("No network available, stopping");
                    return false;
                }
                if (network.getType() != ConnectivityManager.TYPE_WIFI
                        || !network.isConnected()) {
                    App.log.info("Not on connected WiFi, stopping");
                    return false;
                }

                String onlySSID = settings.getSyncWifiOnlySSID();
                if (onlySSID != null) {
                    onlySSID = "\"" + onlySSID + "\"";
                    WifiManager wifi = (WifiManager) getContext()
                            .getSystemService(WIFI_SERVICE);
                    WifiInfo info = wifi.getConnectionInfo();
                    if (info == null || !onlySSID.equals(info.getSSID())) {
                        App.log.info("Connected to wrong WiFi network ("
                                + info.getSSID() + ", required: " + onlySSID
                                + "), ignoring");
                        return false;
                    }
                }
            }
            return true;
        }

    }

}

日历的同步操作
同步数据的实际操作在onPerformSync方法中进行

public class CalendarsSyncAdapterService extends SyncAdapterService {

    @Override
    protected AbstractThreadedSyncAdapter syncAdapter() {
        return new SyncAdapter(this);
    }


    private static class SyncAdapter extends SyncAdapterService.SyncAdapter {

        public SyncAdapter(Context context) {

            super(context);
        }

        @Override
        public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
            Log.e("SyncAdapter", "init ...");
            super.onPerformSync(account, extras, authority, provider, syncResult);

            try {
                AccountSettings settings = new AccountSettings(getContext(), account);
                if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(settings))
                    return;

                updateLocalCalendars(provider, account, settings);

                for (LocalCalendar calendar : (LocalCalendar[])LocalCalendar.find(account, provider, LocalCalendar.Factory.INSTANCE, CalendarContract.Calendars.SYNC_EVENTS + "!=0", null)) {
//                    App.log.info("Synchronizing calendar #"  + calendar.getId() + ", URL: " + calendar.getName());
                    CalendarSyncManager syncManager = new CalendarSyncManager(getContext(), account, settings, extras, authority, syncResult, calendar);
                    syncManager.performSync();
                }
            } catch (CalendarStorageException e) {
                App.log.log(Level.SEVERE, "Couldn't enumerate local calendars", e);
                syncResult.databaseError = true;
            } catch (InvalidAccountException e) {
                App.log.log(Level.SEVERE, "Couldn't get account settings", e);
            }

            App.log.info("Calendar sync complete");
        }

        private void updateLocalCalendars(ContentProviderClient provider, Account account, AccountSettings settings) throws CalendarStorageException {
            SQLiteOpenHelper dbHelper = new ServiceDB.OpenHelper(getContext());
            try {
                // enumerate remote and local calendars
                SQLiteDatabase db = dbHelper.getReadableDatabase();
                Long service = getService(db, account);
                Map<String, CollectionInfo> remote = remoteCalendars(db, service);

                LocalCalendar[] local = (LocalCalendar[])LocalCalendar.find(account, provider, LocalCalendar.Factory.INSTANCE, null, null);

                boolean updateColors = settings.getManageCalendarColors();

                // delete obsolete local calendar
                for (LocalCalendar calendar : local) {
                    String url = calendar.getName();
                    if (!remote.containsKey(url)) {
                        App.log.fine("Deleting obsolete local calendar " + url);
                        calendar.delete();
                    } else {
                        // remote CollectionInfo found for this local collection, update data
                        CollectionInfo info = remote.get(url);
                        App.log.fine("Updating local calendar " + url + " with " + info);
                        calendar.update(info, updateColors);
                        // we already have a local calendar for this remote collection, don't take into consideration anymore
                        remote.remove(url);
                    }
                }

                // create new local calendars
                for (String url : remote.keySet()) {
                    CollectionInfo info = remote.get(url);
                    App.log.info("Adding local calendar list " + info);
                    LocalCalendar.create(account, provider, info);
                }
            } finally {
                dbHelper.close();
            }
        }

        @Nullable
        Long getService(@NonNull SQLiteDatabase db, @NonNull Account account) {
            @Cleanup Cursor c = db.query(Services._TABLE, new String[] { Services.ID },
                    Services.ACCOUNT_NAME + "=? AND " + Services.SERVICE + "=?", new String[] { account.name, Services.SERVICE_CALDAV }, null, null, null);
            if (c.moveToNext())
                return c.getLong(0);
            else
                return null;
        }

        @NonNull
        private Map<String, CollectionInfo> remoteCalendars(@NonNull SQLiteDatabase db, Long service) {
            Map<String, CollectionInfo> collections = new LinkedHashMap<>();
            if (service != null) {
                @Cleanup Cursor cursor = db.query(Collections._TABLE, null,
                        Collections.SERVICE_ID + "=? AND " + Collections.SUPPORTS_VEVENT + "!=0 AND " + Collections.SYNC,
                        new String[] { String.valueOf(service) }, null, null, null);
                while (cursor.moveToNext()) {
                    ContentValues values = new ContentValues();
                    DatabaseUtils.cursorRowToContentValues(cursor, values);
                    CollectionInfo info = CollectionInfo.fromDB(values);
                    collections.put(info.url, info);
                }
            }
            return collections;
        }
    }

}

4.4.2 声明Sync服务, 并制定SyncAdapter的配置文件

此服务需能交给操作系统使唤, 那么声明也需要加入一些表示

用SyncAdapterCache类来将这些信息放到/data/system/registered_services/android.content.SyncAdapter.xml

        <service
            android:name=".syncadapter.CalendarsSyncAdapterService"
            android:exported="true"
            android:process=":sync"
            tools:ignore="ExportedService" >
            <intent-filter>
                <action android:name="android.content.SyncAdapter" />
            </intent-filter>

            <meta-data
                android:name="android.content.SyncAdapter"
                android:resource="@xml/sync_calendars" />
        </service>
    <sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
        android:accountType="bitfire.at.davdroid"
        android:contentAuthority="com.android.calendar"
        android:allowParallelSyncs="true"
        android:supportsUploading="true"
        android:isAlwaysSyncable="true"
        android:userVisible="true" />

userVisible决定了是否在系统Settings的Account里面可以看到更新记录, 并且控制开启与关闭
supportsUploading决定在Provider notifyChanged 的时候, syncToNetwork是否起作用, 如果前者为true, 那么后者为true的时候可以触发SyncAdapter onPerformSync的回调

总结

1.创建账户类型xml/authenticator.xml
2.创建一个service用来给account framework调用,service中拿到authticator的binder
3.在manifest中配置service(注意service的intentfilter和metadata等配置)
4.通过accountmanager的addaccount就可以添加账户
5.创建provider定义好authrity
6.创建syncadapter和使用syncadapter的service(service中有syncadapter的binder对象)
7.xml中配置这个syncadapter
8.在manifest中配置这个service("android.content.SyncAdapter")
9.激活同步功能
1-3创建账户类型 4增加账户 5-9同步账户



参考
http://blog.csdn.net/kifile/article/category/1402671/1
http://talentprince.github.io/blog/2015/01/26/android-zhang-hao-yu-tong-bu-xi-tong-part-one/
http://www.apkbus.com/thread-14570-1-1.html
http://skyseraph.com/2016/06/19/Android/%E4%B8%80%E7%A7%8D%E6%8F%90%E9%AB%98Android%E5%BA%94%E7%94%A8%E8%BF%9B%E7%A8%8B%E5%AD%98%E6%B4%BB%E7%8E%87%E6%96%B0%E6%96%B9%E6%B3%95/
https://shyling.com/posts/android-app-persistent.html
http://blog.csdn.net/crazyman2010/article/details/52101446

上一篇下一篇

猜你喜欢

热点阅读