0

I am implementing DictionaryDatabase in andriod. At that time i have an EditText View in which i type a word and matching word comes from the database. Now problem is that my database is too large.Like for Word "A" i have from 200 to 300 words. At first time on installation of app the database have to be loaded which takes 1 to 3 minutes and its horrible. Now i want that on after app installation or on click on EditTex means on focus i should call an asyntask with ProgressDialogue box. Do loadWords in database and on completion dismiss the dialogue. I know about async task but i don't know how to use async for my that code

here is the code for Database Loading Class:

   /**
    * 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);
}
   }

UPDATED: EditTEx search;

//::::::::::::::::::::::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);
    }
};

Here is Provider Class:

     */
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);
    }
}
GrIsHu
  • 29,068
  • 10
  • 64
  • 102
User42590
  • 2,473
  • 12
  • 44
  • 85

1 Answers1

1

There is a number of tutorials like http://www.javasrilankansupport.com/2012/11/asynctask-android-example-asynctask-in.html

The problem is that the life cycle of an AsyncTask differs from the Activity one: by the moment that AsyncTask completes, the Activity may be already removed from the screen and replaced by another Activity. An activity is re-created e.g. when the screen orientation changes. But it is the old, not visible anymore, Activity that gets notified by the AsyncTask about operation completion.

See the discussion at: Is AsyncTask really conceptually flawed or am I just missing something?

(Note the incompatibility between versions in the default AsyncTask behaviour: prior to API 11, different AsyncTasks were executed in parallel; since API 11, they are executed serially on a single worker thread.)

The solution is to invoke long operations from Model (in MVC sense) rather than from Controller (=Activity), and this most likely will not be an AsyncTask.

That being said, some code:

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(); }
}

Basically, you have to refactor your app so that arrival of the AsyncTask data is an asynchronous event. And if an operation is unacceptably long, it will not become faster in an AsyncTask.

Community
  • 1
  • 1
18446744073709551615
  • 16,368
  • 4
  • 94
  • 127