非UI线程可不可以更新UI(二)

上篇文章提到因为ViewRootImpl会在线程更新UI时检查当前线程是不是创建它的线程,子线程可以在ViewRootImpl未创建的时候尽行更新UI。下面我们来看另外一种可以在子线程更新UI的办法。

既然ViewRootImpl的checkThread方法只会检查当前更新UI的线程是不是创建它的线程。那么可不可以在子线程中构建自己的ViewRootImpl,这样在子线程中更新UI就应该不会报下面的错误:
Only the original thread that created a view hierarchy can touch its views.
这里写图片描述

由于ViewRootImpl对象的创建在WindowManager调用addView方法时创建的。所以下面我们用windowManager在子线程中创建自己的视图。

UnUIThread.java

public class UnUIThread extends Thread {
    private String TAG = "looper";
    private Looper mLooper = null;
    TextView tv_thread;//要添加的textView
    WindowManager wManager;
    private Context mContext;
    private Handler threadHandler;


    public UnUIThread(Context context){
        mContext = context;
    }


    public void run() {
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        createUI();

        if(threadHandler==null){
            threadHandler = new Handler(Looper.myLooper()){
                @Override
                public void handleMessage(Message msg) {
                    // TODO Auto-generated method stub
                    super.handleMessage(msg);
                    tv_thread.setText(msg.obj.toString());
                    if(msg.arg1==2){
                        getmLooper().quit();
                    }
                    if(msg.arg1==3){
                        wManager.removeView(tv_thread);
                    }
                }
            };
        }

        Looper.loop();
    }

    public void createUI() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        tv_thread = new TextView(mContext);
        tv_thread.setTextColor(0xFF000000);
        tv_thread.setText("UNUIThread");

        wManager = (WindowManager) mContext.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
        WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
        mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
        mParams.format = PixelFormat.TRANSLUCENT;
        mParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        mParams.width = 400;
        mParams.height = 100;
        mParams.alpha = 1.0f;
        mParams.gravity=Gravity.BOTTOM;
        wManager.addView(tv_thread, mParams);
    }

    public void updateUI(){
        Message msg = new Message();
        msg.arg1 =1;
        msg.obj = String.valueOf(System.currentTimeMillis());
        threadHandler.sendMessage(msg);
    }

    public void stopLoop(){
        if(mLooper==null){
            Toast.makeText(mContext, "mLooper", Toast.LENGTH_LONG).show();
        }
        Message msg = new Message();
        msg.arg1 =2;
        msg.obj = String.valueOf(System.currentTimeMillis());
        threadHandler.sendMessage(msg);
    }
    public void removeUI(){
        if(mLooper==null){
            Toast.makeText(mContext, "mLooper", Toast.LENGTH_LONG).show();
        }
        Message msg = new Message();
        msg.arg1 =3;
        msg.obj = String.valueOf(System.currentTimeMillis());
        threadHandler.sendMessage(msg);
    }

    public Looper getmLooper()
    {
        if(mLooper!=null){
            return mLooper;
        }else
        {
            synchronized (this){
                try {
                    while (isAlive()&& mLooper==null){
                        wait();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return mLooper;
        }
    }
}

Activity.java

public class SecondActivity extends ActionBarActivity {

    private Button btn_create;//创建UI
    private Button btn_change;//改变UI显示
    private Button btn_remove;//移除UI
    private Button btn_stop;//停止子线程消息循环

    public void autoLoad_activity_second() {
        btn_create = (Button) findViewById(R.id.btn_create);
        btn_change = (Button) findViewById(R.id.btn_change);
        btn_remove = (Button) findViewById(R.id.btn_remove);
        btn_stop = (Button) findViewById(R.id.btn_stop);
    }

    private UnUIThread unUIThread = new UnUIThread(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        autoLoad_activity_second();

        btn_create.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if(unUIThread.getState()==Thread.State.NEW){
                    unUIThread.start();
                }else{
                    unUIThread = new UnUIThread(SecondActivity.this);
                    unUIThread.start();
                }
                if(unUIThread!=null&&!unUIThread.isAlive()){
                }
            }
        });

        btn_remove.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if(unUIThread==null){
                    return;
                }
                unUIThread.removeUI();
            }
        });

        btn_stop.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if(unUIThread==null){
                    return;
                }
                unUIThread.stopLoop();
            }
        });

        btn_change.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if(unUIThread==null){
                    return;
                }
                unUIThread.updateUI();
            }
        });

    }
}

效果:
这里写图片描述

Android UI相关的操作不管是在主线程还是子线程都必须依赖Looper,Handler,Message。所以必须在子线程中构建自己的消息循环机制。通过Handler机制对UI进行更新。相关原理请看http://blog.csdn.net/innost/article/details/6055793

另外由于小米系列手机对Android权限进行了限制,所以使用小米手机测试时,必须开启浮窗权限。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值