Ging3r
Ging3r

Reputation: 2965

Only one Looper may be created per thread

I have an app which request data information on internet (client-server app), but this communication is very slow, thus i have decided to create an AsyncTask to manage the delay. inside of doInBackground i call Looper.prepare() then a my "view generator (which retrives data)".

in detail (the problem):

I have an activity that dinamically create the rows of a list view. but every time i try to inflate rows, android throws a Looper exception "Only one Looper may be created per thread"

i followed the steps:

I suppose I cannot inflate two times but i don't know how i can resolve that

AsyncTask

private class DrawerView extends AsyncTask<ActivityGroup, String, View>{
    Exception exc=null;     

    @Override protected void onPreExecute() {
    super.onPreExecute();   
}

@Override protected View doInBackground(ActivityGroup... params) {
    try {
        Looper.prepare();
    return processAct();
    }catch (ConnectionException e) {    
        exc =e;
    return null;                
    }
    catch (Exception e) {
        exc = e;
    return null;
    }
}

@Override protected void onPostExecute(View result) {
    super.onPostExecute(result);
    if(exc!= null){
        Utils.usrMessage(getApplicationContext(), "Oh Noo!:\n"+exc.getMessage());
        Utils.logErr(getApplicationContext(), exc);
    finish();
    }
    if(result!= null){
        setContentView(result);
    }
}
}

processAct() is an abstract method implemented in this way

@Override protected View processAct() throws Exception {

    Bundle bundle = getIntent().getExtras();
    User user = (User)bundle.getSerializable("user");

    Team team =  Team.getTeamInformation(this,user.getTeamId());
    ArrayList<Player> players =Player.getPlayerList(this,user.getTeamId());

    PlayersListAdapter view = new PlayersListAdapter(this,players,team);
    return view;
}

PlayerListAdapter is the class which builds/sets first view (list container)..here the first inflation

public PlayersListAdapter(Context context, ArrayList<Player> players,Team team) throws Exception{
    super(context);
    View view = inflate(getContext(), R.layout.team_players, this);

    TextView tv_teamName = (TextView)view.findViewById(R.id.tbplrs_tmnm);
    TextView tv_playersNum = (TextView)view.findViewById(R.id.tbplrs_nplrs);

    tv_teamName.setText(team.getName());

    String msg = players.size()+" ";
    msg += (players.size()!=1)?context.getString(R.string.playerPlural):context.getString(R.string.playerSingle);
    tv_playersNum.setText(msg);

    ListView lView = (ListView)view.findViewById(R.id.tbplrs_plrslst);
    PlayersRowListAdapter plAdapter = new PlayersRowListAdapter(context, players);
    lView.setAdapter(plAdapter);
} 

at last PlayerRowListAdapter which extends BaseAdapter,...here the second inflation

@Override public View getView(int position, View view, ViewGroup parent) {
    if (view == null){
        LayoutInflater lInflator = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = lInflator.inflate(R.layout.team_player_singlplayer,null);
    }
    ....
    ....
}

N.B. if i drop the second adapter PlayerRowListAdapter...all works fine...(obviously without list)

Regards

p.s. sorry for my english

Upvotes: 1

Views: 8290

Answers (4)

prodev
prodev

Reputation: 573

Instead of just calling Looper.prepare();, first check if Looper does not already exist for your Thread, if not, call that function. Like this:

if (Looper.myLooper()==null)
    Looper.prepare();

Upvotes: 4

Ging3r
Ging3r

Reputation: 2965

Here's how I worked around this.

As the Developer Reference for AsyncTask says, doInBackground creates a new thread to manage it (this has forced me to call Looper.prepare()), while onPostExecute() uses the main thread.

So I sliced processAct() in two methods: prepareData() which retrieves data and createView() which calls adapter.

I have put the first method into doInBackground(), and the second one (createView()) I have put into onPostExecute().

Upvotes: 0

chrulri
chrulri

Reputation: 1832

AsyncTask already has its own Looper. If you want to update your UI from your doInBackground() method use publishProgress(..) which then invokes onProgressUpdate(..) in the main thread. In your onProgressUpdate you can inflate your Views and add them to your UI.

Edit: example code: http://developer.android.com/reference/android/os/AsyncTask.html

Upvotes: 0

DeeV
DeeV

Reputation: 36045

The only reason you need to call Looper.prepare() and Looper.loop() is when you want to have a message Handler in a thread that is not the UI thread. Basically, it keeps the thread alive forever so that the Handler that was created inside the thread can still send and receive messages. The same goes for callback methods like LocationListener or something similar. You are responsible for killing the thread when it is done by calling Looper.getMyLooper().quit() inside the thread that it is in.

If you are inflating views in the UI thread, then you do not need to call Looper.prepare() or Looper.loop() as this is already done in the background. You should never inflate Views outside the UI thread.

Upvotes: 0

Related Questions