Search code examples
androidmultithreadingscheduledexecutorservicewindow-managers

Cannot Remove View From WindowManager - Multiple Threads


I am having an issue with removing a view from a WindowManager. Here is the method I am calling to do it:

public void updateDockNow (String jsonData) {  
    getWM().removeView(getView());
    setView(new Stuff(jsonData));
    getWM().addView(getView(), getParams());
}

It works perfectly when called from this onChange method initialized in the onCreate.

observer = new ContentObserver(new Handler()){
    @Override
public void onChange(boolean selfChange) {
    super.onChange(selfChange);
    updateDockNow(DockPicker.getDockStringAsync(DockService.this));
}};

However, it fails when called from this, also initialized in onCreate.

final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);       
scheduler.scheduleAtFixedRate(new Runnable() {
    public void run() { 
        if (decision) { updateDockNow(info); } // Problem
    }
}, 0, 1, TimeUnit.SECONDS);

Can I not call the updateDockNow from another thread? Why does it work in one case but not the other?

NOTE: This is not being run from an Activity, it is from a Service.

Thank you

-- ANSWER --

final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);       
scheduler.scheduleAtFixedRate(new Runnable() {
    public void run() { 
        if (decision) { threadMsg(item); } // Problem
    }

    private void threadMsg(String msg) {
        if (!msg.equals(null) && !msg.equals("")) {
            Message msgObj = handler.obtainMessage();
            Bundle b = new Bundle();
            b.putString("message", msg);
            msgObj.setData(b);
            handler.sendMessage(msgObj);
        }
    }

    private final Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            String reply = msg.getData().getString("message");

            if ((null != reply)) {
                Log.i(TAG, "SETTINGS UPDATED, hurray!");
                getWM().removeView(getView());
                setView(new Dock(DockService.this, reply));
                getWM().addView(getView(), getParams());
            }}
    };
}, 0, 1, TimeUnit.SECONDS);

Solution

  • Most View methods should be executed only from the main (UI) thread.

    Activity.runOnUiThread() is your friend here.

    Another option is to initialize a Handler in the UI thread, then keep it available to post() Runnables from each scheduled event. For example:

    public class MyService extends Service
    {
        private Handler mUiHandler;
    
        @Override
        public void onCreate()
        {
            super.onCreate();
            mUiHandler = new Handler();
    
            ContentObserver observer = new ContentObserver(...);
    
            ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);       
            scheduler.scheduleAtFixedRate(new Runnable()
            {
                public void run()
                {
                    mUiHandler.post(new Runnable()
                    {
                        public void run()
                        {
                            updateDockNow(...);
                        }
                    });
                }
            }, 0, 1, TimeUnit.SECONDS);     
        }
    }