0

我在安卓中实现 DictionaryDatabase。当时我有一个 EditText 视图,我在其中输入一个单词,匹配的单词来自数据库。现在的问题是我的数据库太大了。就像单词“A”一样,我有 200 到 300 个单词。第一次安装应用程序时,必须加载数据库,这需要 1 到 3 分钟,这太可怕了。现在我希望在应用程序安装后或单击 EditTex 意味着焦点上我应该调用带有 ProgressDialogue 框的 asyntask。在数据库中执行 loadWords 并在完成时关闭对话。我知道异步任务,但我不知道如何为我的代码使用异步

这是数据库加载类的代码:

   /**
    * Contains logic to return specific words from the dictionary, and
    * load the dictionary table when it needs to be created.
    */
   public class DictionaryDatabase extends SQLiteOpenHelper  {
       private static final String TAG = "DictionaryDatabase";
            final static String DB_DESTINATION = "/data/data/YOUR_PACKAGE_NAME/databases/dictionary.db";
            private DictionaryDatabase mDictionary;
//****FOr Dictionary****//
static int [] raw_textFiles={R.raw.a,R.raw.b,R.raw.c,
    R.raw.d,R.raw.e,R.raw.f,
    R.raw.g,R.raw.h,R.raw.i,
    R.raw.j,R.raw.k,R.raw.l,
    R.raw.m,R.raw.n,R.raw.o,
    R.raw.p,R.raw.q,R.raw.r,
    R.raw.s,R.raw.t,R.raw.u,
    R.raw.v,R.raw.w,R.raw.x,
    R.raw.y,R.raw.z};


//Array for Parsing Use
ArrayList<String> Words = new ArrayList<String>();
private static String word;
private final Context mHelperContext;
private SQLiteDatabase mDatabase;
private ProgressDialog dialog;

//The columns we'll include in the dictionary table
public static final String KEY_WORD = SearchManager.SUGGEST_COLUMN_TEXT_1;
public static final String KEY_DEFINITION = SearchManager.SUGGEST_COLUMN_TEXT_2;

private static final String DATABASE_NAME = "dictionary";
private static final String FTS_VIRTUAL_TABLE = "FTSdictionary";
private static final int DATABASE_VERSION = 2;


private static final HashMap<String,String> mColumnMap = buildColumnMap();

private static final String FTS_TABLE_CREATE =
        "CREATE VIRTUAL TABLE " + FTS_VIRTUAL_TABLE +
        " USING fts3 (" +
        KEY_WORD + ", " +
        KEY_DEFINITION + ");";

/**
 * Constructor
 * @param context The Context within which to work, used to create the DB
 */
public DictionaryDatabase(Context context) {

    super(context, DATABASE_NAME, null, DATABASE_VERSION);
    mHelperContext = context;
}

/**
 * Builds a map for all columns that may be requested, which will be given to the 
 * SQLiteQueryBuilder. This is a good way to define aliases for column names, but must include 
 * all columns, even if the value is the key. This allows the ContentProvider to request
 * columns w/o the need to know real column names and create the alias itself.
 */
private static HashMap<String,String> buildColumnMap() {
    HashMap<String,String> map = new HashMap<String,String>();
    map.put(KEY_WORD, KEY_WORD);

    map.put(KEY_DEFINITION, KEY_DEFINITION);
    map.put(BaseColumns._ID, "rowid AS " +
            BaseColumns._ID);
    map.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, "rowid AS " +
            SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
    map.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, "rowid AS " +
            SearchManager.SUGGEST_COLUMN_SHORTCUT_ID);
    return map;
}

/**
 * Returns a Cursor positioned at the word specified by rowId
 *
 * @param rowId id of word to retrieve
 * @param columns The columns to include, if null then all are included
 * @return Cursor positioned to matching word, or null if not found.
 */
public Cursor getWord(String rowId, String[] columns) {
    String selection = "rowid = ?";
    String[] selectionArgs = new String[] {rowId};

    return query(selection, selectionArgs, columns);

    /* This builds a query that looks like:
     *     SELECT <columns> FROM <table> WHERE rowid = <rowId>
     */
}

/**
 * Returns a Cursor over all words that match the given query
 *
 * @param query The string to search for
 * @param columns The columns to include, if null then all are included
 * @return Cursor over all words that match, or null if none found.
 */
public Cursor getWordMatches(String query, String[] columns) {
    String selection = KEY_WORD + " MATCH ?";
    String[] selectionArgs = new String[] {query+"*"};

    return query(selection, selectionArgs, columns);

    /* This builds a query that looks like:
     *     SELECT <columns> FROM <table> WHERE <KEY_WORD> MATCH 'query*'
     * which is an FTS3 search for the query text (plus a wildcard) inside the word column.
     *
     * - "rowid" is the unique id for all rows but we need this value for the "_id" column in
     *    order for the Adapters to work, so the columns need to make "_id" an alias for "rowid"
     * - "rowid" also needs to be used by the SUGGEST_COLUMN_INTENT_DATA alias in order
     *   for suggestions to carry the proper intent data.
     *   These aliases are defined in the DictionaryProvider when queries are made.
     * - This can be revised to also search the definition text with FTS3 by changing
     *   the selection clause to use FTS_VIRTUAL_TABLE instead of KEY_WORD (to search across
     *   the entire table, but sorting the relevance could be difficult.
     */
}

/**
 * Performs a database query.
 * @param selection The selection clause
 * @param selectionArgs Selection arguments for "?" components in the selection
 * @param columns The columns to return
 * @return A Cursor over all rows matching the query
 */
private Cursor query(String selection, String[] selectionArgs, String[] columns) {
    /* The SQLiteBuilder provides a map for all possible columns requested to
     * actual columns in the database, creating a simple column alias mechanism
     * by which the ContentProvider does not need to know the real column names
     */
    SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
    builder.setTables(FTS_VIRTUAL_TABLE);
    builder.setProjectionMap(mColumnMap);

    Cursor cursor = builder.query(getReadableDatabase(),
            columns, selection, selectionArgs, null, null, null);

    if (cursor == null) {
        return null;
    } else if (!cursor.moveToFirst()) {
        cursor.close();
        return null;
    }
    return cursor;
}

/**
 * This creates/opens the database.
 */
@Override
public void onCreate(SQLiteDatabase db) {
    mDatabase = db;
    Log.i("PATH",""+mDatabase.getPath());
    mDatabase.execSQL(FTS_TABLE_CREATE);
//  loadDictionary();
}
/**
 * Starts a thread to load the database table with words
 */
private void loadDictionary() {
    new Thread(new Runnable() {
        public void run() {
            try {
                loadWords();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }).start();
}

public void loadWords() throws IOException {
    Log.d(TAG, "Loading words...");
    for(int i=0;i<=25;i++)
    {  //***// 
        final Resources resources = mHelperContext.getResources();
        InputStream inputStream = resources.openRawResource(raw_textFiles[i]);
        //InputStream inputStream = resources.openRawResource(R.raw.definitions);
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

        try {
            StringBuilder sb = new StringBuilder();
            while ((word = reader.readLine()) != null)
            {
                sb.append(word);
                //  Log.i("WORD in Parser", ""+word);
            }
            String contents = sb.toString();
            StringTokenizer st = new StringTokenizer(contents, "||");
            while (st.hasMoreElements()) {
                String row = st.nextElement().toString();
                String title = row.substring(0, row.indexOf("$$$"));
                String desc = row.substring(row.indexOf("$$$") + 3);
                // Log.i("Strings in Database",""+title+""+desc);
                long id = addWord(title,desc);

                if (id < 0) {
                    Log.e(TAG, "unable to add word: " + title);
                }
            }
        } finally {
            reader.close();
        }
    } //***//
    Log.d(TAG, "DONE loading words.");
}

/**
 * Add a word to the dictionary.
 * @return rowId or -1 if failed
 */
public long addWord(String word, String definition) {
    ContentValues initialValues = new ContentValues();
    initialValues.put(KEY_WORD, word);
    initialValues.put(KEY_DEFINITION, definition);

    return mDatabase.insert(FTS_VIRTUAL_TABLE, null, initialValues);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
            + newVersion + ", which will destroy all old data");
    db.execSQL("DROP TABLE IF EXISTS " + FTS_VIRTUAL_TABLE);
    onCreate(db);
}
   }

更新: EditTEx 搜索;

//::::::::::::::::::::::TextWatcher For EditTextView :::::::::::::::::::::://

TextWatcher myTextWatcher = new TextWatcher() {

    @SuppressLint("NewApi")
    @Override
    public void onTextChanged(CharSequence s, int start, int before,
            int count) {
        query= (search.getText().toString());
        char character = 0;
        if(query.isEmpty()==false)
        {
            character=query.toLowerCase().charAt(0);
        }

        if (start==0) {
            //Log.i("query",""+query);

                Cursor cursor = managedQuery(DictionaryProvider.CONTENT_URI, null, null,new String[] {query}, null);

            viewFlipper.showNext();

        }

        listAdapter.notifyDataSetChanged();
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count,
            int after) {

    }

    @SuppressLint("NewApi")
    @Override
    public void afterTextChanged(Editable s) {

        query= s.toString();
        char character = 0;

        if(query.isEmpty()==false)
        {
            character=query.toLowerCase().charAt(0);
        }

            Cursor cursor = managedQuery(DictionaryProvider.CONTENT_URI, null, null,new String[] {query}, null);

        listAdapter.getFilter().filter(s);
    }
};

这是提供者类:

     */
private static UriMatcher buildUriMatcher() {
    UriMatcher matcher =  new UriMatcher(UriMatcher.NO_MATCH);
    // to get definitions...
    matcher.addURI(AUTHORITY, "dictionary", SEARCH_WORDS);
    matcher.addURI(AUTHORITY, "dictionary/#", GET_WORD);
    // to get suggestions...
    matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
    matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST);

    /* The following are unused in this implementation, but if we include
     * {@link SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} as a column in our suggestions table, we
     * could expect to receive refresh queries when a shortcutted suggestion is displayed in
     * Quick Search Box, in which case, the following Uris would be provided and we
     * would return a cursor with a single item representing the refreshed suggestion data.
     */
    matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT, REFRESH_SHORTCUT);
    matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*", REFRESH_SHORTCUT);
    return matcher;
}

@Override
public boolean onCreate() {
    mDictionary = new DictionaryDatabase(getContext());
    return true;
}

/**
 * Handles all the dictionary searches and suggestion queries from the Search Manager.
 * When requesting a specific word, the uri alone is required.
 * When searching all of the dictionary for matches, the selectionArgs argument must carry
 * the search query as the first element.
 * All other arguments are ignored.
 */
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                    String sortOrder) {

    // Use the UriMatcher to see what kind of query we have and format the db query accordingly
    switch (sURIMatcher.match(uri)) {
        case SEARCH_SUGGEST:
            if (selectionArgs == null) {
              throw new IllegalArgumentException(
                  "selectionArgs must be provided for the Uri: " + uri);
            }
            return getSuggestions(selectionArgs[0]);
        case SEARCH_WORDS:
            if (selectionArgs == null) {
              throw new IllegalArgumentException(
                  "selectionArgs must be provided for the Uri: " + uri);
            }
            return search(selectionArgs[0]);
        case GET_WORD:
            return getWord(uri);
        case REFRESH_SHORTCUT:
            return refreshShortcut(uri);
        default:
            throw new IllegalArgumentException("Unknown Uri: " + uri);
    }
}

private Cursor getSuggestions(String query) {
  query = query.toLowerCase();
  String[] columns = new String[] {
      BaseColumns._ID,
      DictionaryDatabase.KEY_WORD,
      DictionaryDatabase.KEY_DEFINITION,
   /* SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
                    (only if you want to refresh shortcuts) */
      SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID};

  return mDictionary.getWordMatches(query, columns);
}

private Cursor search(String query) {
  query = query.toLowerCase();
  String[] columns = new String[] {
      BaseColumns._ID,
      DictionaryDatabase.KEY_WORD,
      DictionaryDatabase.KEY_DEFINITION};

  return mDictionary.getWordMatches(query, columns);
}

private Cursor getWord(Uri uri) {
  String rowId = uri.getLastPathSegment();
  String[] columns = new String[] {
      DictionaryDatabase.KEY_WORD,
      DictionaryDatabase.KEY_DEFINITION};

  return mDictionary.getWord(rowId, columns);
}

private Cursor refreshShortcut(Uri uri) {
  /* This won't be called with the current implementation, but if we include
   * {@link SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} as a column in our suggestions table, we
   * could expect to receive refresh queries when a shortcutted suggestion is displayed in
   * Quick Search Box. In which case, this method will query the table for the specific
   * word, using the given item Uri and provide all the columns originally provided with the
   * suggestion query.
   */
  String rowId = uri.getLastPathSegment();
  String[] columns = new String[] {
      BaseColumns._ID,
      DictionaryDatabase.KEY_WORD,
      DictionaryDatabase.KEY_DEFINITION,
      SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
      SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID};

  return mDictionary.getWord(rowId, columns);
}

/**
 * This method is required in order to query the supported types.
 * It's also useful in our own query() method to determine the type of Uri received.
 */
@Override
public String getType(Uri uri) {
    switch (sURIMatcher.match(uri)) {
        case SEARCH_WORDS:
            return WORDS_MIME_TYPE;
        case GET_WORD:
            return DEFINITION_MIME_TYPE;
        case SEARCH_SUGGEST:
            return SearchManager.SUGGEST_MIME_TYPE;
        case REFRESH_SHORTCUT:
            return SearchManager.SHORTCUT_MIME_TYPE;
        default:
            throw new IllegalArgumentException("Unknown URL " + uri);
    }
}
4

1 回答 1

1

有许多教程,例如 http://www.javasrilankansupport.com/2012/11/asynctask-android-example-asynctask-in.html

问题在于 Activity 的生命周期与之前的AsyncTask不同Activity:在 AsyncTask 完成的那一刻,Activity 可能已经从屏幕上移除并被另一个 Activity 替换。重新创建一个活动,例如当屏幕方向改变时。但它是旧的,不再可见的,由 AsyncTask 通知操作完成的 Activity。

请参阅以下讨论: AsyncTask 真的在概念上存在缺陷还是我只是遗漏了什么?

(注意默认 AsyncTask 行为中版本之间的不兼容:在 API 11 之前,不同的 AsyncTask 是并行执行的;从 API 11 开始,它们在单个工作线程上串行执行。)

解决方案是从 Model(在 MVC 意义上)而不是从 Controller(=Activity)调用长操作,这很可能不是 AsyncTask。

话虽这么说,一些代码:

public class AsyncTaskTestActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.d("~~~~","~~~onCreate ~~~ "+this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
    public void onStartButton(View view) {
        Log.d("~~~~","~~~onStartButton {");
        class MyTask extends AsyncTask<Void, Void, Void> {

            @Override
            protected Void doInBackground(Void... params) {
                // TODO Auto-generated method stub
                Log.d("~~~~","~~~doInBackground started");
                try {
                    for (int i=0; i<10; i++) {
                        Log.d("~~~~","~~~sleep#"+i);
                        Thread.sleep(200);
                    }
                    Log.d("~~~~","~~~sleeping over");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                Log.d("~~~~","~~~doInBackground ended");
                return null;
            }
            @Override
            protected void onPostExecute(Void result) {
                super.onPostExecute(result);
                taskDone();
            }
        }
        MyTask task = new MyTask();
        task.execute(null);
        Log.d("~~~~","~~~onStartButton }");
    }
    private void taskDone() {
        Log.d("~~~~","\n\n~~~taskDone ~~~ "+this+"\n\n");
    }
    public void onStopButton(View view) {
        Log.d("~~~~","~~~onStopButton {");
        MagicAppRestart.doRestart(this);
        Log.d("~~~~","~~~onStopButton }");
    }
    public void onPause() {   Log.d("~~~~","~~~onPause ~~~ "+this);   super.onPause(); }
    public void onStop() {    Log.d("~~~~","~~~onStop ~~~ "+this);    super.onPause(); }
    public void onDestroy() { Log.d("~~~~","~~~onDestroy ~~~ "+this); super.onDestroy(); }
}

基本上,您必须重构您的应用程序,以便 AsyncTask 数据的到达是一个异步事件。如果一个操作长得不可接受,它在 AsyncTask 中不会变得更快。

于 2013-02-07T07:34:23.293 回答