一、目的:
简单介绍系统设置搜索功能实现,初步熟悉搜索数据库构建规则以及匹配逻辑。
二、相关问题解答
1、系统设置可对那些设置项进行构建搜索数据库?
答:系统设置对数据项的构建规则在com.android.settings.search.SearchIndexableResources类中进行定义,例如如下,将wifi设置,wifi高级设置设置项假如搜索数据匹配库。

sResMap.put(WifiSettings.class.getName(),                
    new SearchIndexableResource(                        
    Ranking.getRankForClassName(WifiSettings.class.getName()),                        
    NO_DATA_RES_ID,                        
    WifiSettings.class.getName(),                        
    R.drawable.ic_settings_wireless));
sResMap.put(AdvancedWifiSettings.class.getName(),                
    new SearchIndexableResource(                        
    Ranking.getRankForClassName(AdvancedWifiSettings.class.getName()),                        
    R.xml.wifi_advanced_settings,                        
    AdvancedWifiSettings.class.getName(),                        
    R.drawable.ic_settings_wireless)
);

2、进行关键字搜索时,有多个匹配项,在展示时,他们的排序规则是什么?
答:com.android.settings.search.Ranking,其中定义了每个设置项对应的RAND_ID.例如:

sRankMap.put(WifiSettings.class.getName(), RANK_WIFI);        
sRankMap.put(AdvancedWifiSettings.class.getName(), RANK_WIFI);        
sRankMap.put(SavedAccessPointsWifiSettings.class.getName(), RANK_WIFI);

在进行搜索匹配时,会根据RANK_ID进行排序:

private String buildSearchSQL(String query, String[] colums, boolean withOrderBy) {
        StringBuilder sb = new StringBuilder();
        sb.append(buildSearchSQLForColumn(query, colums));
        if (withOrderBy) {
            sb.append(" ORDER BY ");
            sb.append(IndexColumns.DATA_RANK);
        }
        return sb.toString();
    }

3、设置中的设置项如何被解析并加入到搜索匹配数据库中的?
共两类:
类一:
如,下图代码所示,为wifi高级设置项:

sResMap.put(AdvancedWifiSettings.class.getName(),
                 new SearchIndexableResource(
                         Ranking.getRankForClassName(AdvancedWifiSettings.class.getName()),
                         R.xml.wifi_advanced_settings,
                         AdvancedWifiSettings.class.getName(),
                         R.drawable.ic_settings_wireless));

其中定义了R.xml.wifi_advanced_settings,,那么在Index.java中,会对该xml文件进行解析,并加入到搜索数据库中。如下图代码:

if (!nodeName.equals(NODE_NAME_CHECK_BOX_PREFERENCE)) {
                     summary = getDataSummary(context, attrs);
                    String entries = null;
                    if (nodeName.endsWith(NODE_NAME_LIST_PREFERENCE)) {
                         entries = getDataEntries(context, attrs);
                     }
                    // Insert rows for the child nodes of PreferenceScreen
                     updateOneRowWithFilteredData(database, localeStr, title, summary, null, entries,
                             fragmentName, screenTitle, iconResId, rank,
                             keywords, intentAction, intentTargetPackage, intentTargetClass,
                             true, key, -1 /* default user id */);
                 } else {
                     String summaryOn = getDataSummaryOn(context, attrs);
                     String summaryOff = getDataSummaryOff(context, attrs);
                    if (TextUtils.isEmpty(summaryOn) && TextUtils.isEmpty(summaryOff)) {
                         summaryOn = getDataSummary(context, attrs);
                     }
                    updateOneRowWithFilteredData(database, localeStr, title, summaryOn, summaryOff,
                             null, fragmentName, screenTitle, iconResId, rank,
                             keywords, intentAction, intentTargetPackage, intentTargetClass,
                             true, key, -1 /* default user id */);
                 }

非CheckBoxPreference的设置项,会解析preference的title以及summary。CheckBoxPreference类型的设置项会解析summaryOn,summaryOff等。也就是说解析存在细微差别。
详细解析流程可关注Index.java中的indexFromResource()方法。
类二:
如下图代码所示:

sResMap.put(WifiSettings.class.getName(),
                 new SearchIndexableResource(
                         Ranking.getRankForClassName(WifiSettings.class.getName()),
                         NO_DATA_RES_ID,
                         WifiSettings.class.getName(),
                         R.drawable.ic_settings_wireless));

这类与类一的不同在于,它没有明确指定xml文件,这里是:NO_DATA_RES_ID,。那么这种情况,其会在其类中定义名称为:SEARCH_INDEX_DATA_PROVIDER的SearchIndexProvider,在其中会定义加载那些设置项。
4、如何为一个设置项定义多个匹配名字?
答:匹配逻辑本身是弱匹配,我们看下设置的匹配逻辑会匹配以下几项:

private static final String[] MATCH_COLUMNS_PRIMARY = {
             IndexColumns.DATA_TITLE,
             IndexColumns.DATA_TITLE_NORMALIZED,
             IndexColumns.DATA_KEYWORDS
     };

title就是每个设置项的title名称,TITLE_NORMALIZED只是对title做了一些格式化操作。最重要的就是我们的keyword了,我们可以使用keywords来实现为一个设置项指定额外匹配名称,例如:

<PreferenceScreen
                 android:key="brightness"
                 android:title="@string/brightness"
                 settings:keywords="@string/keywords_display_brightness_level">
             <intent android:action="android.intent.action.SHOW_BRIGHTNESS_DIALOG" />
         </PreferenceScreen>

5、系统设置是否支持搜索匹配加载外部设置项?
答:系统设置存在搜索匹配外部应用设置项的功能。系统设置会加载所有应用中AndroidManifest.xml中配置了“android.content.action.SEARCH_INDEXABLES_PROVIDER”的provider,并从该provider中读取设置项到设置的数据库中。
由于android5.0初步实现了该功能,系统中很多应用还没有进行Provider的创建,故而搜索匹配其他应用的体验性不是很好。
三、我们如何将我们新开发的设置项加入到系统设置中。
1、首先如果,新增加的设置项在一级目录,或者多层级目录,并且该设置项为leui新增的,即原生不存在的。需要将该一级设置项加入到:SearchIndexableResources的sResMap成员变量中。
2、如果是在原有设置项内新增设置项,保持title一级summary,fragmeng等配置ok。