前段时间介绍了大致获取搜索联想词的步骤, 这次我们介绍一下获取网络联想词的详细步骤, 我们知道 获取网络
联想词是通过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; } } }