Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 1084|回复: 0

[实例教程]构建自己的Android账户与内容同步机制,分析Sampl

[复制链接]

该用户从未签到

发表于 2011-10-22 13:30:57 | 显示全部楼层 |阅读模式
装过Android版的Facebook、lastfm的同学是否对于这些应用的功能感到惊喜,它们可以定期更新朋友的最新信息,将最新近况和心情短语集成入联系人中。这些应用全部是以Android2.0后的账户和同步机制为基础的。Google的例程中给出了名为SampleSyncAdpater的例子,通过分析该例子可以学会Android中的Account验证、同步Adapter的使用。

详细例子代码可以看sdk samples中提供的源码,现在拿2.2中的版本来简要说明。




首先是 class Authenticator extends AbstractAccountAuthenticator ,该类是账户认证类,打开手机的Setting里,有Account&Sync 一项,Authenticator就是实现其中的账号功能的类。
// in Authenticator.java

    public Bundle addAccount(AccountAuthenticatorResponse response,

        String accountType, String authTokenType, String[] requiredFeatures,

        Bundle options) {

        final Intent intent = new Intent(mContext, AuthenticatorActivity.class);

        intent.putExtra(AuthenticatorActivity.PARAM_AUTHTOKEN_TYPE,

            authTokenType);

        intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,

            response);

        final Bundle bundle = new Bundle();

        bundle.putParcelable(AccountManager.KEY_INTENT, intent);

        return bundle;

    }

其中addAccount方法用来定义需要增加账号时的操作,如调用AuthenticatorActivity来进行账号的添加认证。
在AuthenticatorActivity.java中定义了handleLogin(),此方法由login_activity.xml中的androidnClick="handleLogin"定义与ui中的okbutton的关联。
// in layout/login_activity.xml

<Button

            android:id="@+id/ok_button"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:layout_gravity="center_horizontal"

            android:minWidth="100dip"

            android:text="@string/login_activity_ok_button"

            android:onClick="handleLogin" />

handleLogin()将ui中的用户名和密码取得,并创建一个试图认证的线程,通过网络去服务端验证。
NetworkUtilities.java中的 public static boolean authenticate(String username, String password, Handler handler, final Context context)方法展示了通过网络验证的具体流程。得到服务端验证结果后,在sendResult()中通过handler.post调用来实现onAuthenticationResult()在AuthenticatorActivity中的运行。onAuthenticationResult()判断验证通过则结束AuthenticatorActivity,否则报出用户名密码错,让用户在AuthenticatorActivity中再次尝试验证。
// AuthenticatorActivity.java中的handleLogin()方法

    /**

     * Handles onClick event on the Submit button. Sends username/password to

     * the server for authentication.

     *

     * @param view The Submit button for which this method is invoked

     */

    public void handleLogin(View view) {

        if (mRequestNewAccount) {

            mUsername = mUsernameEdit.getText().toString();

        }

        mPassword = mPasswordEdit.getText().toString();

        if (TextUtils.isEmpty(mUsername) || TextUtils.isEmpty(mPassword)) {

            mMessage.setText(getMessage());

        } else {

            showProgress();

            // Start authenticating...

            mAuthThread =

                NetworkUtilities.attemptAuth(mUsername, mPassword, mHandler,

                    AuthenticatorActivity.this);

        }

    }
// NetworkUtilities中的authenticate()方法通过网络访问具体来实现服务端的验证,sendResult()来使调用结果被AuthenticatorActivity的onAuthenticationResult()调用。

    /**

     * Connects to the Voiper server, authenticates the provided username and

     * password.

     *

     * @param username The user's username

     * @param password The user's password

     * @param handler The hander instance from the calling UI thread.

     * @param context The context of the calling Activity.

     * @return boolean The boolean result indicating whether the user was

     *         successfully authenticated.

     */

    public static boolean authenticate(String username, String password,

        Handler handler, final Context context) {

        final HttpResponse resp;



        final ArrayList<NameValuePair> params = new ArrayList<NameValuePair>();

        params.add(new BasicNameValuePair(PARAM_USERNAME, username));

        params.add(new BasicNameValuePair(PARAM_PASSWORD, password));

        HttpEntity entity = null;

        try {

            entity = new UrlEncodedFormEntity(params);

        } catch (final UnsupportedEncodingException e) {

            // this should never happen.

            throw new AssertionError(e);

        }

        final HttpPost post = new HttpPost(AUTH_URI);

        post.addHeader(entity.getContentType());

        post.setEntity(entity);

        maybeCreateHttpClient();



        try {

            resp = mHttpClient.execute(post);

            if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {

                if (Log.isLoggable(TAG, Log.VERBOSE)) {

                    Log.v(TAG, "Successful authentication");

                }

                sendResult(true, handler, context);

                return true;

            } else {

                if (Log.isLoggable(TAG, Log.VERBOSE)) {

                    Log.v(TAG, "Error authenticating" + resp.getStatusLine());

                }

                sendResult(false, handler, context);

                return false;

            }

        } catch (final IOException e) {

            if (Log.isLoggable(TAG, Log.VERBOSE)) {

                Log.v(TAG, "IOException when getting authtoken", e);

            }

            sendResult(false, handler, context);

            return false;

        } finally {

            if (Log.isLoggable(TAG, Log.VERBOSE)) {

                Log.v(TAG, "getAuthtoken completing");

            }

        }

    }



    /**

     * Sends the authentication response from server back to the caller main UI

     * thread through its handler.

     *

     * @param result The boolean holding authentication result

     * @param handler The main UI thread's handler instance.

     * @param context The caller Activity's context.

     */

    private static void sendResult(final Boolean result, final Handler handler,

        final Context context) {

        if (handler == null || context == null) {

            return;

        }

        handler.post(new Runnable() {

            public void run() {

                ((AuthenticatorActivity) context).onAuthenticationResult(result);

            }

        });

    }
// AuthenticatorActivity.java中的onAuthenticationResult,来根据验证结果来选择结束认证或重新尝试。

    /**

     * Called when the authentication process completes (see attemptLogin()).

     */

    public void onAuthenticationResult(boolean result) {

        Log.i(TAG, "onAuthenticationResult(" + result + ")");

        // Hide the progress dialog

        hideProgress();

        if (result) {

            if (!mConfirmCredentials) {

                finishLogin();

            } else {

                finishConfirmCredentials(true);

            }

        } else {

            Log.e(TAG, "onAuthenticationResult: failed to authenticate");

            if (mRequestNewAccount) {

                // &quotlease enter a valid username/password.

                mMessage

                    .setText(getText(R.string.login_activity_loginfail_text_both));

            } else {

                // "Please enter a valid password." (Used when the

                // account is already in the database but the password

                // doesn't work.)

                mMessage

                    .setText(getText(R.string.login_activity_loginfail_text_pwonly));

            }

        }

    }

Account的验证完毕后,就生成了账号,可以开始使用同步功能了。同步的主要逻辑在public class SyncAdapter extends AbstractThreadedSyncAdapter中实现。
// SyncAdapter.java中的OnPerformSync方法,主要的同步逻辑

    @Override

    public void onPerformSync(Account account, Bundle extras, String authority,

        ContentProviderClient provider, SyncResult syncResult) {

        List<User> users;

        List<Status> statuses;

        String authtoken = null;

         try {

             // use the account manager to request the credentials

             authtoken =

                mAccountManager.blockingGetAuthToken(account,

                    Constants.AUTHTOKEN_TYPE, true /* notifyAuthFailure */);

             // fetch updates from the sample service over the cloud

             users =

                NetworkUtilities.fetchFriendUpdates(account, authtoken,

                    mLastUpdated);

            // update the last synced date.

            mLastUpdated = new Date();

            // update platform contacts.

            Log.d(TAG, "Calling contactManager's sync contacts");

            ContactManager.syncContacts(mContext, account.name, users);

            // fetch and update status messages for all the synced users.

            statuses = NetworkUtilities.fetchFriendStatuses(account, authtoken);

            ContactManager.insertStatuses(mContext, account.name, statuses);

        } catch (final AuthenticatorException e) {

            syncResult.stats.numParseExceptions++;

            Log.e(TAG, "AuthenticatorException", e);

        } catch (final OperationCanceledException e) {

            Log.e(TAG, "OperationCanceledExcetpion", e);

        } catch (final IOException e) {

            Log.e(TAG, "IOException", e);

            syncResult.stats.numIoExceptions++;

        } catch (final AuthenticationException e) {

            mAccountManager.invalidateAuthToken(Constants.ACCOUNT_TYPE,

                authtoken);

            syncResult.stats.numAuthExceptions++;

            Log.e(TAG, "AuthenticationException", e);

        } catch (final ParseException e) {

            syncResult.stats.numParseExceptions++;

            Log.e(TAG, "ParseException", e);

        } catch (final JSONException e) {

            syncResult.stats.numParseExceptions++;

            Log.e(TAG, "JSONException", e);

        }

    }
onPerformSync中的执行流程中,使用NetworkUtilities中的fetchFriendUpdates和fetchFriendStatuses来访问服务端的联系人更新,并使用了例程中自己封装的ContactManager来读取、更新联系人信息。

那Account和SyncAdapter及其Service和xml定义之间是如何关联的呢? AndroidManifest.xml中定义了AccountAuthenticator,SyncAdapter及对应的Service和xml定义的关联。
    <application

        android:icon="@drawable/icon"

        android:label="@string/label">

        <!-- The authenticator service -->

        <service

            android:name=".authenticator.AuthenticationService"

            android:exported="true">

            <intent-filter>

                <action

                    android:name="android.accounts.AccountAuthenticator" />

            </intent-filter>

            <meta-data

                android:name="android.accounts.AccountAuthenticator"

                android:resource="@xml/authenticator" />

        </service>

        <service

            android:name=".syncadapter.SyncService"

            android:exported="true">

            <intent-filter>

                <action

                    android:name="android.content.SyncAdapter" />

            </intent-filter>

            <meta-data

                android:name="android.content.SyncAdapter"

                android:resource="@xml/syncadapter" />

            <meta-data

                android:name="android.provider.CONTACTS_STRUCTURE"

                android:resource="@xml/contacts" />

        </service>

        <activity

            android:name=".authenticator.AuthenticatorActivity"

            android:label="@string/ui_activity_title"

            android:theme="@android:style/Theme.Dialog"

            android:excludeFromRecents="true"

            >

            <!--

                No intent-filter here! This activity is only ever launched by

                someone who explicitly knows the class name

            -->

        </activity>

    </application>

更详细的代码细节和执行流程,可以去把SDK中的SampleSyncAdapter代码运行起来体会一下,不过要实现整个流程,必须搭建联系人的服务器端,例程中在目录samplesyncadapter_server中也提供了简单的server端python代码,需要搭建在google app engine上。搭建过程遇到一些问题,由于对python不熟我弄了几天才解决好搭建成功,其中遇到的一个model moudle找不到的问题需要你在model中新建一个__init__.py的空文件,来说明是一个python模块,如果你也遇到此问题,希望对你有帮助。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

GMT+8, 2024-6-16 16:14 , Processed in 0.412964 second(s), 52 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表