本文主要是介绍Android拨号搜索机制源码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Android拨号搜索机制源码分析(原)
目录(?)[+]
拨号搜索机制分为两个部分,引导搜索和搜索。其中引导搜索是指,从用户输入到开始搜索之间的流程,而搜索部分是指,从数据库搜索字符串的过程。
一、引导搜索部分
默认的拨号界面的布局从上到下主要分为3个部分:显示列表、数字编辑框、拨号键盘。他们的作用是:用户直接在拨号键盘上输入数字,然后数字编辑框显示所输入的数字,同时在显示列表中体现此时的搜索结果。如图所示:
从流程上来讲,需要拨号键盘将用户点击转换为按键事件并传递给编辑框,然后由编辑框传递给搜索框,再由搜索框传递给列表Fragment,然后在列表所加载的Adapter中体现当前的搜索结果。
接下来我们详细分析这个过程。
1.1、从拨号键盘到编辑框
用户在拨号键盘上的点击的数字按钮,都会在编辑框中体现出来,我们先来追踪这一过程。
每个拨号键盘按钮都是DialpadKeyButton类型的View,他们继承自FrameLayout,当遇到点击事件时,就会触发setPressed()方法:
- @setPressed
- public void setPressed(boolean pressed) {
- super.setPressed(pressed);
- if (mOnPressedListener != null) {
- mOnPressedListener.onPressed(this, pressed);
- }
- }
- @DialpadFragment.java
- public void onPressed(View view, boolean pressed) {
- if (pressed) {
- switch (view.getId()) {
- case R.id.one: {
- //将当前点击事件转换为键盘事件
- keyPressed(KeyEvent.KEYCODE_1);
- break;
- }
- case R.id.two: {
- keyPressed(KeyEvent.KEYCODE_2);
- break;
- }
- default: {
- Log.wtf(TAG, "Unexpected onTouch(ACTION_DOWN) event from: " + view);
- break;
- }
- }
- } else {
- }
- }
- private void keyPressed(int keyCode) {
- mHaptic.vibrate();
- KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
- //传递给编辑框控件
- mDigits.onKeyDown(keyCode, event);
- // If the cursor is at the end of the text we hide it.
- final int length = mDigits.length();
- if (length == mDigits.getSelectionStart() && length == mDigits.getSelectionEnd()) {
- mDigits.setCursorVisible(false);
- }
- }
上面的mDigits就是显示当前输入内容的编辑框控件。
1.2、从编辑框到搜索框
当编辑框检测到KeyDown事件后,就会将当前键盘的输入放入编辑框中,并触发TextWatcher的相关方法:
- @DialpadFragment.java
- public void afterTextChanged(Editable input) {
- if (!mDigitsFilledByIntent && SpecialCharSequenceMgr.handleChars(getActivity(), input.toString(), mDigits)) {
- mDigits.getText().clear();
- }
- if (isDigitsEmpty()) {
- mDigitsFilledByIntent = false;
- mDigits.setCursorVisible(false);
- }
- if (mDialpadQueryListener != null) {
- //传递给mDialpadQueryListener
- mDialpadQueryListener.onDialpadQueryChanged(mDigits.getText().toString());
- }
- updateDialAndDeleteButtonEnabledState();
- }
- @DialtactsActivity.java
- public void onDialpadQueryChanged(String query) {
- final String normalizedQuery = SmartDialNameMatcher.normalizeNumber(query, SmartDialNameMatcher.LATIN_SMART_DIAL_MAP);
- if (!TextUtils.equals(mSearchView.getText(), normalizedQuery)) {
- if (mDialpadFragment == null || !mDialpadFragment.isVisible()) {
- return;
- }
- //传递给搜索框
- mSearchView.setText(normalizedQuery);
- }
- }
我们看到,在onDialpadQueryChanged()中将当前编辑框的内容通过setText()方法传递给了mSearchView,也就是最上方的搜索框。
1.3、从搜索框到搜索结果列表Fragment
在搜索时,由于搜索框注册了文本监听器,所以将会触发TextWatcher,此时需要暂存当前要搜索的文本,并进入搜索模式,然后再将搜索内容交给SmartDialSearchFragment。
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- final String newText = s.toString();
- if (newText.equals(mSearchQuery)) {
- return;
- }
- //存储当前的搜索文本
- mSearchQuery = newText;
- final boolean dialpadSearch = isDialpadShowing();
- // Show search result with non-empty text. Show a bare list otherwise.
- if (TextUtils.isEmpty(newText) && getInSearchUi()) {
- //退出搜索模式
- exitSearchUi();
- mSearchViewCloseButton.setVisibility(View.GONE);
- mVoiceSearchButton.setVisibility(View.VISIBLE);
- return;
- } else if (!TextUtils.isEmpty(newText)) {
- final boolean sameSearchMode = (dialpadSearch && mInDialpadSearch) || (!dialpadSearch && mInRegularSearch);
- if (!sameSearchMode) {
- //进入搜素模式
- enterSearchUi(dialpadSearch, newText);
- }
- if (dialpadSearch && mSmartDialSearchFragment != null) {
- //将搜索文本转交给mSmartDialSearchFragment
- mSmartDialSearchFragment.setQueryString(newText, false);
- } else if (mRegularSearchFragment != null) {
- mRegularSearchFragment.setQueryString(newText, false);
- }
- mSearchViewCloseButton.setVisibility(View.VISIBLE);
- mVoiceSearchButton.setVisibility(View.GONE);
- return;
- }
- }
1、将当前要搜索的文本存储在mSearchQuery中,在当前界面被恢复时使用;
2、进入/退出搜素界面,也就是配置当前需要加载的Fragment;
3、将要搜素的文本传递给搜索列表的Fragment,也就是mSmartDialSearchFragment;
1.4、从搜索列表的Fragment到Adapter
SmartDialSearchFragment
----SearchFragment
----PhoneNumberPickerFragment
----ContactEntryListFragment<ContactEntryListAdapter>
----Fragment
如下图所示:
SmartDialSearchFragment拿到搜索的文本后,需要传递给自己的Adapter才能完成搜索任务,我们现在来分析这个交接的过程。
从上面1.3节中我们看到,SmartDialSearchFragment通过setQueryString()拿到了要搜索的字串,我们来查看这个方法,他是在SmartDialSearchFragment的父类ContactEntryListFragment中被实现的:
- @ContactEntryListFragment.java
- public void setQueryString(String queryString, boolean delaySelection) {
- if (TextUtils.isEmpty(queryString)) queryString = null;
- if (!TextUtils.equals(mQueryString, queryString)) {
- mQueryString = queryString;
- setSearchMode(!TextUtils.isEmpty(mQueryString));
- if (mAdapter != null) {
- //传递给Adapter
- mAdapter.setQueryString(queryString);
- //触发Adapter重新搜索
- reloadData();
- }
- }
- }
我们在SmartDialSearchFragment中找到了该Adapter的创建之处,他就是SmartDialNumberListAdapter:
- @SmartDialSearchFragment.java
- protected ContactEntryListAdapter createListAdapter() {
- SmartDialNumberListAdapter adapter = new SmartDialNumberListAdapter(getActivity());
- adapter.setUseCallableUri(super.usesCallableUri());
- adapter.setQuickContactEnabled(true);
- adapter.setShortcutEnabled(SmartDialNumberListAdapter.SHORTCUT_DIRECT_CALL, false);
- return adapter;
- }
SmartDialNumberListAdapter
----DialerPhoneNumberListAdapter
----PhoneNumberListAdapter
----ContactEntryListAdapter
----IndexerListAdapter
----PinnedHeaderListAdapter
----CompositeCursorAdapter
如下图所示:
接下来我们分析如何通过Fragment的reloadData()触发Adapter的搜索。
1.5、Adapter触发搜索机制
刚才介绍到,SmartDialSearchFragment在setQueryString()时,通过reloadData()触发了Adapter的搜索,我们来看一下这个流程:
- @ContactEntryListFragment.java
- protected void reloadData() {
- removePendingDirectorySearchRequests();
- mAdapter.onDataReload();
- mLoadPriorityDirectoriesOnly = true;
- mForceLoad = true;
- //触发新的Adapter
- startLoading();
- }
- protected void startLoading() {
- if (mAdapter == null) {
- return;
- }
- //配置Adapter要搜索的文本
- configureAdapter();
- int partitionCount = mAdapter.getPartitionCount();
- for (int i = 0; i < partitionCount; i++) {
- Partition partition = mAdapter.getPartition(i);
- if (partition instanceof DirectoryPartition) {
- DirectoryPartition directoryPartition = (DirectoryPartition)partition;
- if (directoryPartition.getStatus() == DirectoryPartition.STATUS_NOT_LOADED) {
- if (directoryPartition.isPriorityDirectory() || !mLoadPriorityDirectoriesOnly) {
- startLoadingDirectoryPartition(i);
- }
- }
- } else {
- //通过LoaderManager进行异步查询
- getLoaderManager().initLoader(i, null, this);
- }
- }
- mLoadPriorityDirectoriesOnly = false;
- }
我们来看Loader的流程。
经过initLoader()的操作之后,就会触发SmartDialSearchFragment中的onCreateLoader()方法:
- @SmartDialSearchFragment.java
- public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- if (id == getDirectoryLoaderId()) {
- return super.onCreateLoader(id, args);
- } else {
- //创建当前的CursorLoader,也就是SmartDialCursorLoader
- final SmartDialNumberListAdapter adapter = (SmartDialNumberListAdapter) getAdapter();
- SmartDialCursorLoader loader = new SmartDialCursorLoader(super.getContext());
- adapter.configureLoader(loader);
- return loader;
- }
- }
- @ContactEntryListFragment.java
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- if (!mEnabled) {
- return;
- }
- int loaderId = loader.getId();
- if (loaderId == DIRECTORY_LOADER_ID) {
- mDirectoryListStatus = STATUS_LOADED;
- mAdapter.changeDirectories(data);
- startLoading();
- } else {
- //更新Adapter的Cursor
- onPartitionLoaded(loaderId, data);
- if (isSearchMode()) {
- int directorySearchMode = getDirectorySearchMode();
- if (directorySearchMode != DirectoryListLoader.SEARCH_MODE_NONE) {
- if (mDirectoryListStatus == STATUS_NOT_LOADED) {
- mDirectoryListStatus = STATUS_LOADING;
- getLoaderManager().initLoader(DIRECTORY_LOADER_ID, null, this);
- } else {
- startLoading();
- }
- }
- } else {
- mDirectoryListStatus = STATUS_NOT_LOADED;
- getLoaderManager().destroyLoader(DIRECTORY_LOADER_ID);
- }
- }
- }
- protected void onPartitionLoaded(int partitionIndex, Cursor data) {
- if (partitionIndex >= mAdapter.getPartitionCount()) {
- return;
- }
- //更新当前的Adapter
- mAdapter.changeCursor(partitionIndex, data);
- setProfileHeader();
- showCount(partitionIndex, data);
- if (!isLoading()) {
- completeRestoreInstanceState();
- }
- }
在onLoadFinished()中,通过onPartitionLoaded()对当前的Adapter所使用的Cursor进行更新,从而刷新列表。
二、字符搜索过程
- @SmartDialCursorLoader.java
- public Cursor loadInBackground() {
- //从dialerDatabaseHelper中查找匹配结果
- final DialerDatabaseHelper dialerDatabaseHelper = DatabaseHelperManager.getDatabaseHelper( mContext);
- final ArrayList<ContactNumber> allMatches = dialerDatabaseHelper.getLooseMatches(mQuery, mNameMatcher);
- //构建Cursor给Adapter使用
- final MatrixCursor cursor = new MatrixCursor(PhoneQuery.PROJECTION_PRIMARY);
- Object[] row = new Object[PhoneQuery.PROJECTION_PRIMARY.length];
- for (ContactNumber contact : allMatches) {
- row[PhoneQuery.PHONE_ID] = contact.dataId;
- row[PhoneQuery.PHONE_NUMBER] = contact.phoneNumber;
- row[PhoneQuery.CONTACT_ID] = contact.id;
- row[PhoneQuery.LOOKUP_KEY] = contact.lookupKey;
- row[PhoneQuery.PHOTO_ID] = contact.photoId;
- row[PhoneQuery.DISPLAY_NAME] = contact.displayName;
- cursor.addRow(row);
- }
- return cursor;
- }
原来,SmartDialCursorLoader是利用DialerDatabaseHelper进行的查找,他是SQLiteOpenHelper的子类,每次拨号盘进程的创建都会根据当前的通讯录内容创建表单,用于联系人搜索。下面我们从该数据库的创建、查询、更新三个方面来分析其内部原理。
2.1、数据库的创建及搜索机制
- @DatabaseHelperManager.java
- public static DialerDatabaseHelper getDatabaseHelper(Context context) {
- return DialerDatabaseHelper.getInstance(context);
- }
- @DialerDatabaseHelper.java
- public static synchronized DialerDatabaseHelper getInstance(Context context) {
- if (sSingleton == null) {
- sSingleton = new DialerDatabaseHelper(context.getApplicationContext(), DATABASE_NAME);
- }
- return sSingleton;
- }
- public void onCreate(SQLiteDatabase db) {
- setupTables(db);
- }
- private void setupTables(SQLiteDatabase db) {
- //删除旧表单
- dropTables(db);
- //创建新表“smartdial_table”
- db.execSQL("CREATE TABLE " + Tables.SMARTDIAL_TABLE + " (" +
- SmartDialDbColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
- SmartDialDbColumns.DATA_ID + " INTEGER, " +
- SmartDialDbColumns.NUMBER + " TEXT," +
- SmartDialDbColumns.CONTACT_ID + " INTEGER," +
- SmartDialDbColumns.LOOKUP_KEY + " TEXT," +
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY + " TEXT, " +
- SmartDialDbColumns.PHOTO_ID + " INTEGER, " +
- SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + " LONG, " +
- SmartDialDbColumns.LAST_TIME_USED + " LONG, " +
- SmartDialDbColumns.TIMES_USED + " INTEGER, " +
- SmartDialDbColumns.STARRED + " INTEGER, " +
- SmartDialDbColumns.IS_SUPER_PRIMARY + " INTEGER, " +
- SmartDialDbColumns.IN_VISIBLE_GROUP + " INTEGER, " +
- SmartDialDbColumns.IS_PRIMARY + " INTEGER" +
- ");");
- //创建新表“prefix_table”
- db.execSQL("CREATE TABLE " + Tables.PREFIX_TABLE + " (" +
- PrefixColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
- PrefixColumns.PREFIX + " TEXT COLLATE NOCASE, " +
- PrefixColumns.CONTACT_ID + " INTEGER" +
- ");");
- //创建新表“properties”
- db.execSQL("CREATE TABLE " + Tables.PROPERTIES + " (" +
- PropertiesColumns.PROPERTY_KEY + " TEXT PRIMARY KEY, " +
- PropertiesColumns.PROPERTY_VALUE + " TEXT " +
- ");");
- //设置属性
- setProperty(db, DATABASE_VERSION_PROPERTY, String.valueOf(DATABASE_VERSION));
- //更新时间
- resetSmartDialLastUpdatedTime();
- }
prefix_table表单
该表是对所有联系人的电话号码以及英文姓名进行解析,形成搜索的索引表单。
如果是姓名,则将姓名的英文单词解析为相应的数字,比如:
英文名 dushaofeng 将会被解析为:3(d)8(u)7(s)4(h)2(a)6(o)3(f)3(e)6(n)4(g)
如果是号码,除了要保存号码本身外,对于包含国家码的号码,还要保存除去国家码以外的有效号码。
经过上面的解析,每个联系人至少包含两条记录,即姓名对应的数字以及号码所拆分出来的数字。当搜索时,就会利用用户所输入的内容在该表中进行匹配,匹配成功的记录,将根据该条记录的contact_id在smartdial_table表中查找该联系人的详细信息。
smartdial_table表单
每个联系人都对应该表中一条记录,每条记录都包含了该联系人的phone_number、contact_id、display_name、photo_id、starred、last_smartdial_update_time等信息,在搜索时,会利用用户输入区prefix_table中进行匹配,对于匹配成功的记录,根据prefix_table表中对应的contact_id再来smartdial_table中查找该联系人的详细信息,也就是头像、姓名、收藏状态等,并把这些信息构建为Cursor类数据,返回给查询者。
这就是该数据库搜索的原理。
2.2、数据库的更新
每次拨号界面经过onResume(),都会触发数据库的更新:
- @DialtactsActivity.java
- protected void onResume() {
- super.onResume();
- //进入数据库更新入口
- mDialerDatabaseHelper.startSmartDialUpdateThread();
- }
- @DialerDatabaseHelper.java
- public void startSmartDialUpdateThread() {
- new SmartDialUpdateAsyncTask().execute();
- }
- private class SmartDialUpdateAsyncTask extends AsyncTask {
- @Override
- protected Object doInBackground(Object[] objects) {
- updateSmartDialDatabase();
- return null;
- }
- @Override
- protected void onCancelled() {
- super.onCancelled();
- }
- @Override
- protected void onPostExecute(Object o) {
- super.onPostExecute(o);
- }
- }
- public void updateSmartDialDatabase() {
- final SQLiteDatabase db = getWritableDatabase();
- synchronized(mLock) {
- final StopWatch stopWatch = DEBUG ? StopWatch.start("Updating databases") : null;
- //获取上一次更新的时间
- final SharedPreferences databaseLastUpdateSharedPref = mContext.getSharedPreferences( DATABASE_LAST_CREATED_SHARED_PREF, Context.MODE_PRIVATE);
- final String lastUpdateMillis = String.valueOf(databaseLastUpdateSharedPref.getLong(LAST_UPDATED_MILLIS, 0));
- //得到当前的通讯录数据
- final Cursor updatedContactCursor = mContext.getContentResolver().query(PhoneQuery.URI,
- PhoneQuery.PROJECTION, PhoneQuery.SELECT_UPDATED_CLAUSE,
- new String[]{lastUpdateMillis}, null);
- //获取当前的时间
- final Long currentMillis = System.currentTimeMillis();
- if (updatedContactCursor == null) {
- return;
- }
- sInUpdate.getAndSet(true);
- //删掉已经删除的和无效的联系人记录
- removeDeletedContacts(db, lastUpdateMillis);
- removePotentiallyCorruptedContacts(db, lastUpdateMillis);
- try {
- if (!lastUpdateMillis.equals("0")) {
- removeUpdatedContacts(db, updatedContactCursor);
- }
- //向smartdial_table表中插入当前所有有效的联系人数据,以及向prefix_table表中添加联系人号码添加为搜索索引
- insertUpdatedContactsAndNumberPrefix(db, updatedContactCursor, currentMillis);
- } finally {
- updatedContactCursor.close();
- }
- //从smartdial_table表中读取当前联系人的姓名和号码
- final Cursor nameCursor = db.rawQuery(
- "SELECT DISTINCT " +
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", " + SmartDialDbColumns.CONTACT_ID +
- " FROM " + Tables.SMARTDIAL_TABLE +
- " WHERE " + SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME +
- " = " + Long.toString(currentMillis),
- new String[] {});
- if (nameCursor != null) {
- try {
- //根据联系人姓名生成相应的数字索引
- insertNamePrefixes(db, nameCursor);
- } finally {
- nameCursor.close();
- }
- }
- //创建数据库相应的列
- /** Creates index on contact_id for fast JOIN operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS smartdial_contact_id_index ON " + Tables.SMARTDIAL_TABLE + " (" + SmartDialDbColumns.CONTACT_ID + ");");
- /** Creates index on last_smartdial_update_time for fast SELECT operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS smartdial_last_update_index ON " +
- Tables.SMARTDIAL_TABLE + " (" +
- SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + ");");
- /** Creates index on sorting fields for fast sort operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS smartdial_sort_index ON " +
- Tables.SMARTDIAL_TABLE + " (" +
- SmartDialDbColumns.STARRED + ", " +
- SmartDialDbColumns.IS_SUPER_PRIMARY + ", " +
- SmartDialDbColumns.LAST_TIME_USED + ", " +
- SmartDialDbColumns.TIMES_USED + ", " +
- SmartDialDbColumns.IN_VISIBLE_GROUP + ", " +
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", " +
- SmartDialDbColumns.CONTACT_ID + ", " +
- SmartDialDbColumns.IS_PRIMARY +
- ");");
- /** Creates index on prefix for fast SELECT operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS nameprefix_index ON " +
- Tables.PREFIX_TABLE + " (" + PrefixColumns.PREFIX + ");");
- /** Creates index on contact_id for fast JOIN operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS nameprefix_contact_id_index ON " +
- Tables.PREFIX_TABLE + " (" + PrefixColumns.CONTACT_ID + ");");
- /** Updates the database index statistics.*/
- db.execSQL("ANALYZE " + Tables.SMARTDIAL_TABLE);
- db.execSQL("ANALYZE " + Tables.PREFIX_TABLE);
- db.execSQL("ANALYZE smartdial_contact_id_index");
- db.execSQL("ANALYZE smartdial_last_update_index");
- db.execSQL("ANALYZE nameprefix_index");
- db.execSQL("ANALYZE nameprefix_contact_id_index");
- sInUpdate.getAndSet(false);
- final SharedPreferences.Editor editor = databaseLastUpdateSharedPref.edit();
- editor.putLong(LAST_UPDATED_MILLIS, currentMillis);
- editor.commit();
- }
- }
1、从联系人数据库中读取从上次更新到现在时间段内更新的联系人记录;
2、在removeDeletedContacts()中,删除已经被删除的联系人记录;
3、在removePotentiallyCorruptedContacts()中,删除已经损坏的联系人记录;
4、在removeUpdatedContacts()中,删除需要更新的联系人记录;
5、在insertUpdatedContactsAndNumberPrefix()中,将更新的联系人数据插入到smartdial_table中,并把联系人号码插入到prefix_table中;
6、在insertNamePrefixes()中,将本次需要更新的联系人的姓名转换为数字存入prefix_table中;
7、为数据库建立索引;
需要注意两点:
1、从联系人数据库查询时,并不是查询所有联系人,而是查询从上次查询到现在之间所更新的联系人数据;
2、解析联系人姓名为号码时,只对英文姓名有效,这就决定了,无法通过拼音搜索联系人;
2.3、数据库的查询
在1.5节中我们分析到,在SmartDialCursorLoader中通过DialerDatabaseHelper的getLooseMatches()方法进行搜索任务,现在来看具体的操作:
- @DialerDatabaseHelper.java
- public ArrayList<ContactNumber> getLooseMatches(String query, SmartDialNameMatcher nameMatcher) {
- final boolean inUpdate = sInUpdate.get();
- if (inUpdate) {
- return Lists.newArrayList();
- }
- final SQLiteDatabase db = getReadableDatabase();
- //准备搜索匹配语句
- final String looseQuery = query + "%";
- final ArrayList<ContactNumber> result = Lists.newArrayList();
- final StopWatch stopWatch = DEBUG ? StopWatch.start(":Name Prefix query") : null;
- final String currentTimeStamp = Long.toString(System.currentTimeMillis());
- //搜索语句,从prefix_table中搜索匹配项,并从smartdial_table中读取匹配项的详细信息
- final Cursor cursor = db.rawQuery("SELECT " +
- SmartDialDbColumns.DATA_ID + ", " +
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", " +
- SmartDialDbColumns.PHOTO_ID + ", " +
- SmartDialDbColumns.NUMBER + ", " +
- SmartDialDbColumns.CONTACT_ID + ", " +
- SmartDialDbColumns.LOOKUP_KEY +
- " FROM " + Tables.SMARTDIAL_TABLE + " WHERE " +
- SmartDialDbColumns.CONTACT_ID + " IN " +
- " (SELECT " + PrefixColumns.CONTACT_ID +
- " FROM " + Tables.PREFIX_TABLE +
- " WHERE " + Tables.PREFIX_TABLE + "." + PrefixColumns.PREFIX +
- " LIKE '" + looseQuery + "')" +
- " ORDER BY " + SmartDialSortingOrder.SORT_ORDER,
- new String[] {currentTimeStamp});
- final int columnDataId = 0;
- final int columnDisplayNamePrimary = 1;
- final int columnPhotoId = 2;
- final int columnNumber = 3;
- final int columnId = 4;
- final int columnLookupKey = 5;
- final Set<ContactMatch> duplicates = new HashSet<ContactMatch>();
- int counter = 0;
- try {
- //对匹配项去重,并构建搜索结果
- while ((cursor.moveToNext()) && (counter < MAX_ENTRIES)) {
- final long dataID = cursor.getLong(columnDataId);
- final String displayName = cursor.getString(columnDisplayNamePrimary);
- final String phoneNumber = cursor.getString(columnNumber);
- final long id = cursor.getLong(columnId);
- final long photoId = cursor.getLong(columnPhotoId);
- final String lookupKey = cursor.getString(columnLookupKey);
- final ContactMatch contactMatch = new ContactMatch(lookupKey, id);
- //该匹配项已经被收录,无需重复添加到结果中
- if (duplicates.contains(contactMatch)) {
- continue;
- }
- final boolean nameMatches = nameMatcher.matches(displayName);
- final boolean numberMatches = (nameMatcher.matchesNumber(phoneNumber, query) != null);
- if (nameMatches || numberMatches) {
- //匹配成功,且没有重复项
- duplicates.add(contactMatch);
- result.add(new ContactNumber(id, dataID, displayName, phoneNumber, lookupKey, photoId));
- counter++;
- }
- }
- } finally {
- cursor.close();
- }
- return result;
- }
搜索的过程就是从prefix_table中匹配当前的搜索字串,对于匹配到的项,再去smartdial_table中查找该联系人的详细记录,但是由于同一条联系人有可能既匹配姓名又匹配号码,因此搜索结果中可能包含相同的联系人记录。所以对于拿到的Cursor进行遍历,整理其每一条数据,遇到重复项则忽略,最终整理得到有效的结果组合ArrayList<ContactNumber>,返回给查询者。
这就是拨号界面的搜索机制。
这篇关于Android拨号搜索机制源码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!