Android如何在賬戶設定中新增App的賬戶
Android系統為外部服務提供了賬號登入的機制,用於同步資料等作用。
進入設定->賬戶->新增賬戶,即可看到目前手機上有哪些App提供了同步服務。
接下來將會演示如何在App中定義登入服務並新增一個登入選項到這裡。
賬戶的授權和同步
賬戶功能主要有2個:授權和同步。
授權:即賬戶登入怎麼把關,可以通過驗證賬戶密碼的形式,也允許直接通過AccountManager的addAcount介面直接新增賬戶。
需要繼承AbstractAccountAuthenticator來實現我們自己的授權機制。
同步:以郵箱賬戶為例,在新增一個Exchange郵箱賬戶後,我們可以看到它提供了聯絡人,日曆,郵件等同步功能,這些同步服務每一項都需要App來繼承實現AbstractThreadedSyncAdapter,用於該項的同步。
簡單的演示
如何新增登入入口
第一步:實現AbstractAccountAuthenticator
在res/xml下新增一個xml,用於定義account-authenticator的以下屬性:
sample_authenticator.xml
<account-authenticator
xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.android.test"
android:icon="@drawable/ic_account_icon"
android:smallIcon="@drawable/ic_account_icon"
android:label="@string/account_title"/>
accountType -> 自定義的一個串,一般為包名
icon -> 圖示
smallIcon -> 小圖示,可能用於顯示在狀態列等空間較小的地方
label -> 顯示的賬戶名
接下來實現一個實現一個AbstractAccountAuthenticator,重點是實現addAccount方法的行為,現在先忽略具體實現。
public class TestAuthenticator extends AbstractAccountAuthenticator {
Context mContext;
AccountManager mManager;
public TestAuthenticator(Context context) {
super(context);
mContext = context;
mManager = AccountManager.get(context);
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse accountAuthenticatorResponse, String s) {
throw new UnsupportedOperationException();
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse accountAuthenticatorResponse, String s, String s1, String[] strings, Bundle bundle) throws NetworkErrorException {
//這裡可以返回包含Login介面Intent的Bunble,在點選新增這個賬戶後就能進入我們自定義的登入介面
return null;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, Bundle bundle) throws NetworkErrorException {
return null;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String s, Bundle bundle) throws NetworkErrorException {
return null;
}
@Override
public String getAuthTokenLabel(String s) {
return null;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String s, Bundle bundle) throws NetworkErrorException {
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String[] strings) throws NetworkErrorException {
return null;
}
}
第二步:實現Service和宣告許可權
實現一個Service,AccountManagerService需要通過Service來獲取Authenticator中的的Binder,通過Binder回撥來獲取到我們自定義的登入行為。
在onBind中,我們需要返回TestAuthenticator中的IBinder:
public class TestAuthenticateService extends Service {
TestAuthenticator mAuthenticator;
@Override
public void onCreate() {
super.onCreate();
mAuthenticator = new TestAuthenticator(this.getApplicationContext());
}
@Override
public IBinder onBind(Intent intent) {
//限制了只有在AccountManagerService繫結service時才返回Authenticator的binder
if (AccountManager.ACTION_AUTHENTICATOR_INTENT.equals(intent.getAction())) {
return mAuthenticator.getIBinder();
} else {
return null;
}
}
}
第三步:新增宣告和許可權到Manifest
既然實現了Service,就需要在Manifest中新增宣告,順便也將賬戶相關的許可權新增進來:
<!--格式都是固定的,必須宣告intent-filter和meta-data-->
<service android:name=".TestAuthenticateService"
android:exported="true">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
<!--meta-data resource指向第一步中定義的sample_authenticator.xml-->
<meta-data android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/sample_authenticator" />
</service>
許可權:
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
現在進入賬號設定介面,就能看到效果了:
但是現在還沒有實現登入功能,並沒有什麼用。
第四步:實現登入介面
先實現一個普通的Activity,包含賬號密碼輸入框和登入按鈕。(這是普通的Activity,別忘了在Manifest中宣告)
因為這裡只是示例,所以點選登入按鈕後,直接呼叫AccountManager的介面將賬戶新增進來。
//LoginActivity.java
mBtnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//指定Account的type和name
Account account = new Account("TestAccount", "com.android.test");
//新增賬戶
AccountManager.get(getApplicationContext()).addAccountExplicitly(account, "", null);
}
});
那麼這個Activity啟動條件是什麼呢?
回顧第一步,在我們實現的AbstractAccountAuthenticator中有一個addAccount方法還沒有具體實現,在addAccount中返回包含啟動LoginActivity Intent的Bundle,就可以啟動我們的登入介面了:
@Override
public Bundle addAccount(AccountAuthenticatorResponse accountAuthenticatorResponse, String s, String s1, String[] strings, Bundle bundle) throws NetworkErrorException {
Bundle b = new Bundle();
Intent i = new Intent(mContext, LoginActivity.class);
b.putParcelable(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, accountAuthenticatorResponse);//回撥
b.putParcelable(AccountManager.KEY_INTENT, i);
return b;
}
最終效果:
新增同步功能
有了登入功能,如何新增同步功能呢?
與上面的步驟類似,這次我們需要繼承實現AbstractThreadedSyncAdapter,
第一步:實現AbstractThreadedSyncAdapter
在xml目錄下新建檔案並寫入sync-adapter的描述:
sync-adapter.xml
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.android.test"
android:contentAuthority="com.android.contacts"
/>
accountType ->賬戶的type,需要與前面定義的一致
contentAuthority ->需要同步的資料,必須是有提供Provider供外部訪問的模組,如聯絡人(com.android.contacts),瀏覽器(”com.android.browser”),日曆(”com.android.calendar”)等,也可以是自己實現的provider。
還有以下屬性,此處不一一深究。
android:userVisible="true|false"
android:supportsUploading="true|false"
android:allowParallelSyncs="true|false"
android:isAlwaysSyncable="true|false"
android:syncAdapterSettingsAction="ACTION_OF_SETTINGS_ACTIVITY"
public class SyncAdapter extends AbstractThreadedSyncAdapter {
private static final String TAG = "SyncAdapter";
public SyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
}
@Override
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
//實現同步的具體處理
}
}
第二步:實現Service
實現一個普通的Service,負責建立SyncAdapter,並在onBind時返回Ibinder給AccountManagerService。
public class TestSyncService extends Service {
private static final Object sLock = new Object();
private static SyncAdapter sSyncAdapter = null;
@Override
public void onCreate() {
Log.d(TAG, "onCreate");
synchronized (sLock) {
if (null == sSyncAdapter) {
sSyncAdapter = new SyncAdapter(this, true);
}
}
}
@Override
public IBinder onBind(Intent intent) {
return sSyncAdapter.getSyncAdapterBinder();
}
}
第三部:Manifest宣告和許可權
宣告Service,固定格式:
<service
android:name=".TestSyncService"
android:exported="true">
<intent-filter>
<action
android:name="android.content.SyncAdapter" />
</intent-filter>
<!--resource指向第一步定義的xml檔案-->
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/sync_adapter" />
</service>
新增讀取同步資料的許可權,本例為聯絡人:
<uses-permission
android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission
android:name="android.permission.WRITE_SYNC_SETTINGS" />
效果如下:
至於如何實現同步的具體功能,此處不再鋪開。
如何獲取Account
判斷是否已經添加了本APP的Account
private boolean queryAccountStatus(){
if(findAccount("TestAccount", "com.android.test") != null){
return true;
}
return false;
}
private Account findAccount(String accountName, String accountType) {
for (Account account : AccountManager.get(this).getAccounts()) {
return account;
}
return null;
}
查詢有哪些SyncAdapter
final AccountManager am = AccountManager.get(this);
final SyncAdapterType[] syncs = ContentResolver.getSyncAdapterTypes();
for (SyncAdapterType sync : syncs) {
Log.d(LOG_TAG, "syncs account:" + sync.accountType);
}
AccountManager部分原始碼分析
Account資訊存在哪?
Account和SyncAdapter資訊都是以xml的形式,分別存放到/data/system/users/[user_id]/registered_services 目錄下面:
x0:/data/system/users/0/registered_services # ls
android.accounts.AccountAuthenticator.xml
android.content.SyncAdapter.xml
android.accounts.AccountAuthenticator.xml
檔案用於儲存可登入的賬戶:
x0:/data/system/users/0/registered_services # cat android.accounts.AccountAuthenticator.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<services>
<service uid="10063" type="com.android.exchange" />
<service uid="10140" type="com.sina.weibo.account" />
<service uid="10063" type="com.android.email" />
<service uid="10162" type="com.qihoo.pctrl.keepalive.account" />
<service uid="10152" type="ludashi.daemon" />
<service uid="10159" type="com.icoolme.weather.authaccount" />
<service uid="10063" type="com.android.email.pop3" />
</services>
而android.content.SyncAdapter.xml
儲存的是有宣告Sync Adatper的服務:
le_x10:/data/system/users/0/registered_services # cat android.content.SyncAdapter.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<services>
<service uid="10063" authority="com.android.calendar" accountType="com.android.exchange" />
<service uid="10063" authority="com.android.contacts" accountType="com.android.exchange" />
<service uid="10162" authority="com.qihoo.pctrl.keepalive.account.SyncProvider" accountType="com.qihoo.pctrl.keepalive.account" />
...
</services>
這些資料由實現了抽象類RegisteredServicesCache
的AccountAuthenticatorCache
和SyncAdaptersCache
來管理:
public abstract class RegisteredServicesCache<V>
public class SyncAdaptersCache extends RegisteredServicesCache<SyncAdapterType>
class AccountAuthenticatorCache extends RegisteredServicesCache<AuthenticatorDescription> implements IAccountAuthenticatorCache
來看下RegisteredServicesCache
中實現讀寫和解析xml的介面。
外部通過介面獲取SyncAdapters 或AccountAuthenticator服務時,RegisteredServicesCache
內部都會呼叫findOrCreateUserLocked
介面獲取xml中的Service記錄。
//RegisteredServicesCache.java
public abstract class RegisteredServicesCache<V> {
//指定目錄名
protected static final String REGISTERED_SERVICES_DIR = "registered_services";
private final XmlSerializerAndParser<V> mSerializerAndParser;
...
//獲取所有Service的介面
private UserServices<V> findOrCreateUserLocked(int userId, boolean loadFromFileIfNew) {
//從Cache中獲取
UserServices<V> services = mUserServices.get(userId);
if (services == null) { //如沒有Cache,則從xml檔案中獲取
services = new UserServices<V>();
mUserServices.put(userId, services);
if (loadFromFileIfNew && mSerializerAndParser != null) {
UserInfo user = getUser(userId);
if (user != null) {
//1. 獲取xml檔案
AtomicFile file = createFileForUser(user.id);
if (file.getBaseFile().exists()) {
InputStream is = null;
try {
is = file.openRead();
//2. 讀取並解析xml檔案,將結果寫入UserServices Map中
readPersistentServicesLocked(is);
} catch (Exception e) {
} finally {
IoUtils.closeQuietly(is);
}
}
}
}
}
return services;
}
}
來展開程式碼註釋中的第一點。
createFileForUser
方法裡做了檔案路徑組裝並返回對應的File,可以注意到檔名mInterfaceName是變數,這個變數對應的就是xml的檔名,它由RegisteredServicesCache
的子類AccountAuthenticatorCache
和SyncAdaptersCache
來決定,xml的名字也就是上面adb shell演示中的2個檔案。
//RegisteredServicesCache.java
private AtomicFile createFileForUser(int userId) {
File userDir = getUserSystemDirectory(userId);
//mInterfaceName 是變數
File userFile = new File(userDir, REGISTERED_SERVICES_DIR + "/" + mInterfaceName + ".xml");
return new AtomicFile(userFile);
}
protected File getUserSystemDirectory(int userId) {
return Environment.getUserSystemDirectory(userId);
}
SyncAdaptersCache
對應的xml檔名為android.content.SyncAdapter
//SyncAdaptersCache.java
private static final String SERVICE_INTERFACE = "android.content.SyncAdapter";
private static final String SERVICE_META_DATA = "android.content.SyncAdapter";
private static final String ATTRIBUTES_NAME = "sync-adapter";
public SyncAdaptersCache(Context context) {
super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, sSerializer);
}
而AccountAuthenticatorCache
則為android.accounts.AccountAuthenticator
。
//AccountAuthenticatorCache.java
public static final String ACTION_AUTHENTICATOR_INTENT =
"android.accounts.AccountAuthenticator";
public static final String AUTHENTICATOR_META_DATA_NAME =
"android.accounts.AccountAuthenticator";
public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
public AccountAuthenticatorCache(Context context) {
super(context, AccountManager.ACTION_AUTHENTICATOR_INTENT,
AccountManager.AUTHENTICATOR_META_DATA_NAME,
AccountManager.AUTHENTICATOR_ATTRIBUTES_NAME, sSerializer);
}
xml檔案拿到之後,接下來就是xml的解析過程,因為xml本身標籤並不多,就不貼程式碼了,主要就是將uid和service name拿出來,組成UserServices物件的Map返回給呼叫者。
Account的其他“妙用”——提高程序存活率
利用Android程序回收策略對Account同步程序的“照顧”,可以通過新增Account使用同步機制來提高程序的存活率。(可能在AndroidN之後不再有用)
提高程序存活率,詳情可以參考這篇文章:
《一種提高Android應用程序存活率新方法》
一直納悶這兩兄弟躲在系統賬號裡的目的,現在猜到個大概了。。。
相關推薦
Android如何在賬戶設定中新增App的賬戶
Android系統為外部服務提供了賬號登入的機制,用於同步資料等作用。 進入設定->賬戶->新增賬戶,即可看到目前手機上有哪些App提供了同步服務。 接下來將會演示如何在App中定義登入服務並新增一個登入選項到這裡。 賬戶的授權和同步
android系統原始碼中新增app原始碼(原始碼部署移植)
涉及到系統定製,需要在系統中加入自己的apk工程,但是上網找了很多資料都是不夠全面的,或者看了還是沒搞懂,我自己也是一點點摸索過來的,花了不少的時間,也是踩了不少的坑,因此特開一文,幫助大家渡河。 申明,本文親測有效,如果有疑問,歡迎在下方留言。人人為我,我為
Android在activity中新增背景
android:background="@drawable/ming" 這個是我們在佈局檔案中新增的,就是下面的其中一條 xmlns:tools="http://schemas.android.co
RK3288Android5.1系統在設定中新增隱藏和顯示導航欄功能
1.需求 應客戶需求,在android系統設定中新增一個設定選項,該選項中新增一個開關功能,用於顯示和隱藏系統底部導航欄。 2.分析 首先當然是有系統原始碼了,RK3288,5.1系統原始碼一份。 然後就是修改系統設定app,即Settings.apk的原始碼。 再
如何向android手機通訊錄中新增聯絡人
直接在手機的通訊錄的資料庫中新增列表 相關程式碼如下 package com.example.test; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import
Android應用:SurfaceView中新增控制元件
上次說了 如何使用SurfaceView,文章連結: 但如何在SurfaceView中新增控制元件呢? 1、首先,將SurfaceView的建構函式修改為兩個引數的 public MyView(Context context, AttributeSet attrs)
android studio 選單中的app執行按鈕上有個叉號,原因與解決辦法
在android studio寫程式碼中,直接建立專案,寫程式碼然後執行是不會一般是不會出現這樣的問題的,但是一旦更改主Activity,而不跟著手動更改AndroidManifest.xml中的activity配置,則會出現這樣的情況,這就是提醒我們沒有了主Activit
android 專案開發中遇到app主題無法指向style
今天換了主機,將原來程式碼遷移到新的主機上,通過行動硬碟開,不知道為何出現部分class亂碼的問題,通過github實現程式碼移植,直接打開出現app無法執行,類似app主題要加Base的這種問題,在清單檔案中發現,app主題無法指向style,解決方法,close proj
Android在設定裡面新增新功能的方法
1./usr/smdt/self6000/android/packages/apps/Settings/res/xml/device_info_settings.xml 中增加節點: <!-- Detailed build version-->
android 通過設定cookie解決app 登入後WebView還要重新登陸問題
問題描述:因為需要在app里加入html,所以使用了webView,但是第一次進入webview時,需要在webview頁面重新登陸,為了解決這一問題花了大量的時間,所以分享給遇到問題的朋友們。 入下的方法是通過設定cookie來解決 在 webView.l
在android settings.db資料庫中新增一項新的設定
Settings資料存放在com.android.providers.settings/databases/settings.db 中 資料庫中資料的預設資料在frameworks/base/packages/SettingsProvider/res/values
Android中給App設定NoActionBar/FullScreen
因為現在博主在學習安卓,有的時候難免會有設定整個程式無標題和全屏的要求,在百度上搜索有好幾個 解決辦法,但是有的時候的那些辦法好用,有的時候不好用。 現在找到一個辦法,博主自己測試時屢試不爽的,特別好用,現在分享出來,希望對和博主一樣的安卓初學者有幫助。
Android Calendar新增本地賬戶
在Android原生程式碼中,日曆App如要新增活動,需要先新增賬戶,不方便使用者的使用。反編譯某某系統的CalendarProvider.apk,從中提取了新增本地賬戶的程式碼,在此共享。 主要修改了/packages/providers/CalendarP
Android app中新增facebook原生廣告,應該注意的坑
在app中新增facebook廣告,由於facebook廣告做了快取,為了讓廣告展示次數更高,可以在onDestroy方法中,將廣告物件銷燬,下次再請求廣告重新例項化。 @Override protected void onDestroy() {
在***專案中,手機端使用賬戶A登入進入app,檢視模組B的內容XX,顯示正常,檢視模組C的內容XXX也顯示正常,然後進入模組D事件辦理,獲取事件列表,正常,但是選擇辦理的時候,呼叫介面E,一直提
最終解決方案: 經過排查,發現問題是,呼叫獲取事件列表介面,有個欄位為圖片,返回的為空字串,手機端未做判斷,強行載入圖片,導致PHPSESSID發生變化,服務端主動清空cookie,使用者資訊失效,TOKEN驗證失敗,解決辦法,手機端判斷,圖片欄位如果為空,則不載入。 解決
android studio 菜單中的app運行按鈕上有個叉號,原因與解決辦法(自己去百度)
代碼 問題 style post fontsize XML idm 出現 studio http://blog.csdn.net/sz0268/article/details/51706397 : 在Android studio寫代碼中,直接建立項目,寫代碼然後運行是不會
在Android中使App高速、簡單地支持新浪微博、微信、QQ、facebook等十幾個主流社交平臺的分享功能
分析 ont renren androidm mod 執行 xen 12px 操作 前言 在如今的APP或者遊戲中,分享功能差點兒已經成為標配。分享功能不但能夠滿足用戶的需求。也能夠為產品帶來很多其它的用戶,甚至能夠對用戶的行為、活躍度、年齡段等情況進行數據統計,使得軟
Android TV開發中所有的遙控器按鍵監聽及註意事項,新增home鍵監聽
char 技術分享 ces num block eas article 分享 iou 原文:Android TV開發中所有的遙控器按鍵監聽及註意事項,新增home鍵監聽 簡單記錄
記錄Android開發一個小坑,佈局檔案TextView中新增onClick後,點選無效問題
自己寫東西的時候,在TextView上添加了onClick去增加點選事件,去跳轉另一個Activity,執行後結果點選無效,新增Toast,Toast也不顯示,程式碼如下: <TextView android:layout_width="wrap_content"
tomcat8-管理員賬戶設定指南(解決許可權已設定仍然報403錯誤)
新使用者新增: 修改 ${CATALINA_BASE}/conf/ 目錄下的 tomcat-users.xml 檔案,重啟tomcat後生效,例: <user username="test" password="chang3m3N#w" roles="admin-