非UI线程可不可以更新UI

Posted coderzdz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了非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权限进行了限制,所以使用小米手机测试时,必须开启浮窗权限。

以上是关于非UI线程可不可以更新UI的主要内容,如果未能解决你的问题,请参考以下文章

非UI线程可不可以更新UI

非UI线程可不可以更新UI

使用 DirectX11 更新非 UI 线程中的顶点缓冲区

Android Handler 机制:Hander 机制深入探究问题梳理

Android Handler 机制:Hander 机制深入探究问题梳理

为啥loop之后就可以子线程更新ui