0

I am calculating something that takes some time. For that I've created an inner class that subclasses AsyncTask:

private class AsyncAllPlayAll extends AsyncTask<String, Void, CharSequence> 
{
    StournamentDbAdapter mDbHelper;

    public AsyncAllPlayAll(Context context, StournamentDbAdapter dbHelper)
    {
        this.mContext = context;
        this.mDbHelper = dbHelper;
    }

    @Override
    protected CharSequence doInBackground(String... tournamentRowIds) 
    {
        Long tournamentRowId = Long.valueOf(tournamentRowIds[0]);

        Tournament tournament = new Tournament(mDbHelper, tournamentRowId);
        tournament.setupTournament();               

        Boolean tournamentHasWinner = mDbHelper.winningTournamentParticipantsExists(tournamentRowId);

        // tournament already run: delete score
        if(tournamentHasWinner)
        {
            mDbHelper.resetTournamentsStocksScore(tournamentRowId);

            mDbHelper.resetTournamentWinnerStockId(tournamentRowId);
        }

        //run tournament!
        tournament.allPlayAll();                

        TournamentParticipant tournamentParticipant = mDbHelper.insertWinningParticipant(tournamentRowId);                      

        populateCompetitorsListView();      

        final CharSequence winner = "Winner is: " + tournamentParticipant.getStock().getName() + "(" + tournamentParticipant.getScore() + ")";

        return winner;
    }

    @Override                               //the winner
    protected void onPostExecute(final CharSequence result) {

        super.onPostExecute(result);    

        final int duration = Toast.LENGTH_LONG;

        TournamentEdit.this.runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(mContext, "Hello", duration).show();
            }
        });

    }

}

But I get an error, which I don't quit understand: "Can't create handler inside thread that has not called Looper.prepare()"

I read several threads on stackoverflow but implementing what was written in them didn't change my situation.

I was sure the error came from the Toast I was running in onPostExecute but this is not the issue. The error initiates from trying to initialize tournament.

Tournament tournament = new Tournament(mDbHelper, tournamentRowId);

Here is my stack:

Thread [<11> AsyncTask #1] (Suspended)  
    FragmentManagerImpl$1.<init>(FragmentManagerImpl) line: 423 
    FragmentManagerImpl.<init>() line: 423  
    Tournament(Activity).<init>() line: 702 
    Tournament.<init>(StournamentDbAdapter, Long) line: 48  
    TournamentEdit$AsyncAllPlayAll.doInBackground(String...) line: 346  
    TournamentEdit$AsyncAllPlayAll.doInBackground(Object...) line: 1    
    AsyncTask$2.call() line: 264    
    FutureTask$Sync.innerRun() line: 305    
    AsyncTask$3(FutureTask).run() line: 137 
    AsyncTask$SerialExecutor$1.run() line: 208  
    ThreadPoolExecutor.runWorker(ThreadPoolExecutor$Worker) line: 1076  
    ThreadPoolExecutor$Worker.run() line: 569   
    Thread.run() line: 856  

I tohught it could be some issue with the db thread? As I pass it to AsyncAllPlayAll from the main thread.

So I tried to open the db thread in the inner class (different instance) and changed its constructor to:

    public AsyncAllPlayAll(Context context, StournamentDbAdapter dbHelper)
    {
        this.mContext = context;
        this.mDbHelper = dbHelper;
        mDbHelper  = new StournamentDbAdapter(context);
        mDbHelper.open();
    }

This didn't help either :(

Now I tried adding a Looper (as suggested in AsyncTask and Looper.prepare() error)

    @Override
    protected CharSequence doInBackground(String... tournamentRowIds) 
    {
        Looper.prepare(); 
        Long tournamentRowId = Long.valueOf(tournamentRowIds[0]);

        Tournament tournament = new Tournament(mDbHelper, tournamentRowId);
        tournament.setupTournament();               

        Boolean tournamentHasWinner = mDbHelper.winningTournamentParticipantsExists(tournamentRowId);

        // tournament already run: delete score
        if(tournamentHasWinner)
        {
            mDbHelper.resetTournamentsStocksScore(tournamentRowId);

            mDbHelper.resetTournamentWinnerStockId(tournamentRowId);
        }

        //run tournament!
        tournament.allPlayAll();                

        TournamentParticipant tournamentParticipant = mDbHelper.insertWinningParticipant(tournamentRowId);                      

        populateCompetitorsListView();      

        final CharSequence winner = "Winner is: " + tournamentParticipant.getStock().getName() + "(" + tournamentParticipant.getScore() + ")";

        Looper.loop();

        return winner;
    }

The code went further but I get the following error:

Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

Now populateCompetitorsListView() is causing the crash and it is also clear why: look at the last line of populateCompetitorsListView(), which is

setListAdapter(tournamentStocks);

is trying to alter the main thread. I moved it to the main class, which seems to have fixed it: the calculation runs.

@SuppressWarnings("deprecation")
private void populateCompetitorsListView()
{
    // Get all of the notes from the database and create the item list
    Cursor tournamentStocksCursor = mDbHelper.retrieveTrounamentStocks(mTournamentRowId);
    startManagingCursor(tournamentStocksCursor);

    // Create an array to specify the fields we want to display in the list (only name)
    String[] from = new String[] {StournamentConstants.TblStocks.COLUMN_NAME, StournamentConstants.TblTournamentsStocks.TBL_COLUMN_TOURNAMENTS_STOCKS_STOCK_SCORE};

    // and an array of the fields we want to bind those fields to (in this case just name)
    int[] to = new int[]{R.id.competitor_name, R.id.competitor_score};

    // Now create an array adapter and set it to display using our row
    SimpleCursorAdapter tournamentStocks = new SimpleCursorAdapter(this, R.layout.competitor_row, tournamentStocksCursor, from, to);

    //tournamentStocks.convertToString(tournamentStocksCursor);
    setListAdapter(tournamentStocks);       
}

But the problem now is that I seem to be stuck inside doInBackground() due to the implementation of Looper.loop(); which is guess runs an endless loop?

Any further ideas?

Thanks!

Community
  • 1
  • 1
roysch
  • 773
  • 1
  • 7
  • 22

1 Answers1

0

After much research I found out what was my problem: I was using too much code inside doInBackground() & after I took out most of the code and put it back in my Activity I had strange behavior of my ListView: sonetimes it showed an empty result. Some times it showed only one row (out of 23) with a result and some times a different row with a result. When I run the app - I mostly got no or only one line of result. When I used the debugger - I got all the results just fine! How come??? - well - I was doing the mistake of updating my ListView on my main thread BEFORE the 2nd thread managed to update the db with the results. So the main thread grabbed empty results from the db. I also got rid of the Loop.

My Async Class now looks like that:

private class AsyncAllPlayAll extends AsyncTask<String, Void, Long> 
{
    Tournament mTournament = null;

    public AsyncAllPlayAll(Tournament tournament)
    {
        this.mTournament = tournament;
    }

    @Override
    protected Long doInBackground(String... string) 
    {
        Long ml = null;
        //run tournament!
        mTournament.allPlayAll();

        return ml = (long) 0;
    }

    @Override
    protected void onPostExecute(Long result) 
    {
        TournamentEdit.this.runOnUiThread(new Runnable() {

            public void run() {
                // TODO Auto-generated method stub
                populateCompetitorsListView();
            }
        });
    }
} 

As I now use runOnUiThread() to call a populate method on my main thread. I still have to update my Toast message but this I will do tomorrow :-)

Thank you for reading this ultra-long saga...

roysch
  • 773
  • 1
  • 7
  • 22