前段时间介绍了大致获取搜索联想词的步骤, 这次我们介绍一下获取网络联想词的详细步骤, 我们知道 获取网络
联想词是通过SearchEngine实现的, 在浏览器中, 实现有两套方案:
OpenSearchEngine会去定制的服务器获取搜索的联想词
DefaultSearchEngine 使用Android内部的SearchManager去query 联想列表
但是无论如何返回的数据是Cursor 并返回给SuggestCursor
下面详细分析一下 首先是OpenSearchSearchEngine 的初始化: 初始化了 HttpClent
public OpenSearchSearchEngine(Context context, SearchEngineInfo searchEngineInfo) {
mSearchEngineInfo = searchEngineInfo;
mHttpClient = AndroidHttpClient.newInstance(USER_AGENT);//设置了UA
HttpParams params = mHttpClient.getParams();
params.setLongParameter(HTTP_TIMEOUT, HTTP_TIMEOUT_MS);//设置超时
}
在SlowFilterTask这个Task中开始query
class SlowFilterTask extends AsyncTask<CharSequence, Void, List<SuggestItem>> {
@Override
protected List<SuggestItem> doInBackground(CharSequence... params) {
SuggestCursor cursor = new SuggestCursor();
cursor.runQuery(params[0]);//开始query
List<SuggestItem> results = new ArrayList<SuggestItem>();
int count = cursor.getCount();//遍历Cursor中的各个数据
for (int i = 0; i < count; i++) {
results.add(cursor.getItem());
cursor.moveToNext();
}
cursor.close();
return results;
}
@Override
protected void onPostExecute(List<SuggestItem> items) {
mSuggestResults = items;
mMixedResults = buildSuggestionResults();//获取数据成功了把数据混合然后更新UI
notifyDataSetChanged();
}
}
在SuggestCursor的runQuery中调用 SearchEngine 的 getSuggestions函数
/**
* Queries for a given search term and returns a cursor containing
* suggestions ordered by best match.
*/
public Cursor getSuggestions(Context context, String query) {
if (TextUtils.isEmpty(query)) {
return null;
}
if (!isNetworkConnected(context)) {//检查网络是否ok
Log.i(TAG, "Not connected to network.");
return null;
}
String suggestUri = mSearchEngineInfo.getSuggestUriForQuery(query); //从什么搜索引擎搜索联想词
if (TextUtils.isEmpty(suggestUri)) {
// No suggest URI available for this engine
return null;
}
try {
String content = readUrl(suggestUri); //从网络获取联想词
if (content == null) return null;
/* The data format is a JSON array with items being regular strings or JSON arrays
* themselves. We are interested in the second and third elements, both of which
* should be JSON arrays. The second element/array contains the suggestions and the
* third element contains the descriptions. Some search engines don't support
* suggestion descriptions so the third element is optional.
*/
JSONArray results = new JSONArray(content); //返回的是Json
JSONArray suggestions = results.getJSONArray(1);
JSONArray descriptions = null;
if (results.length() > 2) {
descriptions = results.getJSONArray(2);
// Some search engines given an empty array "[]" for descriptions instead of
// not including it in the response.
if (descriptions.length() == 0) {
descriptions = null;
}
}
return new SuggestionsCursor(suggestions, descriptions); //返回这个Json
} catch (JSONException e) {
Log.w(TAG, "Error", e);
}
return null;
}
从网络获取数据代码
/**
* Executes a GET request and returns the response content.
*
* @param url Request URI.
* @return The response content. This is the empty string if the response
* contained no content.
*/
public String readUrl(String url) {
try {
HttpGet method = new HttpGet(url);
HttpResponse response = mHttpClient.execute(method);
if (response.getStatusLine().getStatusCode() == 200) {
return EntityUtils.toString(response.getEntity());
} else {
Log.i(TAG, "Suggestion request failed");
return null;
}
} catch (IOException e) {
Log.w(TAG, "Error", e);
return null;
}
}
SuggestionsCursor 继承自AbstractCursor 也是一个Cursor了
private static class SuggestionsCursor extends AbstractCursor {
private final JSONArray mSuggestions;
private final JSONArray mDescriptions;
public SuggestionsCursor(JSONArray suggestions, JSONArray descriptions) {
mSuggestions = suggestions;
mDescriptions = descriptions;
}
@Override
public int getCount() { //用来配合 moveToNext
return mSuggestions.length();
}
@Override
public String[] getColumnNames() {
return (mDescriptions != null ? COLUMNS : COLUMNS_WITHOUT_DESCRIPTION);
}
@Override
public String getString(int column) {
if (mPos != -1) {
if ((column == COLUMN_INDEX_QUERY) || (column == COLUMN_INDEX_TEXT_1)) {
try {
return mSuggestions.getString(mPos);
} catch (JSONException e) {
Log.w(TAG, "Error", e);
}
} else if (column == COLUMN_INDEX_TEXT_2) {
try {
return mDescriptions.getString(mPos);
} catch (JSONException e) {
Log.w(TAG, "Error", e);
}
} else if (column == COLUMN_INDEX_ICON) {
return String.valueOf(R.drawable.magnifying_glass);
}
}
return null;
}
@Override
public double getDouble(int column) {
throw new UnsupportedOperationException();
}
@Override
public float getFloat(int column) {
throw new UnsupportedOperationException();
}
@Override
public int getInt(int column) {
throw new UnsupportedOperationException();
}
@Override
public long getLong(int column) {
if (column == COLUMN_INDEX_ID) {
return mPos; // use row# as the _Id
}
throw new UnsupportedOperationException();
}
@Override
public short getShort(int column) {
throw new UnsupportedOperationException();
}
@Override
public boolean isNull(int column) {
throw new UnsupportedOperationException();
}
}
这里废话一句AbstractCursor重要的函数 moveToFirst是如何使用getCount的 在AbstractCursor.java中
参考代码 http://code.taobao.org/p/cnandroiddocs/src/trunk/core/java/android/database/AbstractCursor.java
public final boolean moveToPosition(int position) { // Make sure position isn't past the end of the cursor
final int count = getCount(); if (position >= count) {
mPos = count; return false;
} // Make sure position isn't before the beginning of the cursor
if (position < 0) {
mPos = -1; return false;
} // Check for no-op moves, and skip the rest of the work for them
if (position == mPos) { return true;
} boolean result = onMove(mPos, position); if (result == false) {
mPos = -1;
} else {
mPos = position; if (mRowIdColumnIndex != -1) {
mCurrentRowID = Long.valueOf(getLong(mRowIdColumnIndex));
}
} return result;
} @Override
public void fillWindow(int position, CursorWindow window) {
DatabaseUtils.cursorFillWindow(this, position, window);
} public final boolean move(int offset) { return moveToPosition(mPos + offset);
} public final boolean moveToFirst() { return moveToPosition(0);
}
既然返回了SuggestCursor SuggestionAdapter就可以拿到Cursor进行 数据的读取了
class SuggestCursor extends CursorSource {
@Override
public SuggestItem getItem() {//读取SuggestionsCursor中的数据
if (mCursor != null) {
String title = mCursor.getString(
mCursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1));
String text2 = mCursor.getString(
mCursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2));
String url = mCursor.getString(
mCursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2_URL));
String uri = mCursor.getString(
mCursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_DATA));
int type = (TextUtils.isEmpty(url)) ? TYPE_SUGGEST : TYPE_SUGGEST_URL;
SuggestItem item = new SuggestItem(title, url, type);
item.extra = mCursor.getString(
mCursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA));
return item;
}
return null;
}
@Override
public void runQuery(CharSequence constraint) {
if (mCursor != null) {
mCursor.close();
}
//这里拿到的是OpenSearchSearchEngine对象,后面在分析
SearchEngine searchEngine = mSettings.getSearchEngine();
if (!TextUtils.isEmpty(constraint)) {
if (searchEngine != null && searchEngine.supportsSuggestions()) {
//最后拿到的是json 然后返回一个cursor对象 操作json用来做混合 联想词 处理
//json居然可以这样用! 自定义Cursor , Cursor不仅仅可以用来存取数据库数据
mCursor = searchEngine.getSuggestions(mContext, constraint.toString());
if (mCursor != null) {
mCursor.moveToFirst();
}
}
} else {
if (searchEngine.wantsEmptyQuery()) {
mCursor = searchEngine.getSuggestions(mContext, "");
}
mCursor = null;
}
}
}