android中的线程执行UI更新方式,以及Handler的初步了解

在安卓中,网络加载数据或准备数据后显示在界面是一种很频繁的需求,大体来说就是在线程内下载了数据之后需要在ui线程内去更新ui界面,我们需要避免以下两点

  • 绝对不能在UI Thread当中进行耗时的操作,不能阻塞我们的UI Thread
在ui Thread中进行耗时操作,如果出现阻塞时间超过5秒钟,就会出现ANR (Application Not Responding)的现象,此时,应用程序会弹出一个框,让用户选择是否退出该程序。对于Android开发来说,出现ANR的现象是绝对不能被允许的


  • 不能在UI Thread之外的线程当中操纵我们的UI元素

一、Handler的ui更新方式
由于我们的Android UI控件是线程不安全的,所以我们不能在UI Thread之外的线程当中对我们的UI控件进行操作,而在我们的具体运行中也会报错,所以我们需要避免


那么,在我们实际代码中,采用什么方式来达到很好的线程内耗时操作+ui线程中更新界面呢,方式其实是有很多的

按照我之前在工作中的方式来说的话,是new Thread()来在线程内进行耗时操作,然后在操作完成之后,通过

new Handler(context.getMainLooper()).post(new Runnable() {
 
    @Override
    public void run() {
        // 在这里执行你要想的操作 比如直接在这里更新ui或者调用回调在 在回调中更新ui
 
    }
});
这种方式来防止线程内操作界面刷新报错,但是,可以说我在代码中这样写的很痛苦(所以说看着我以前写的代码真的想砍了自己),所以我需要一种新的很好的方式来进行规避,我深入了解了Handler,这里有一个最简单的例子

public class MainActivity extends Activity{
	
	private Handler handler;
	private TextView mTextView;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		handler = new Handler(){
			public void handleMessage(Message msg) {
				Log.i("test", "收到消息:" + msg.what);
				A a = (A)msg.obj;
				Log.i("test", "收到对象了吗:" + a.getId() + ",名称:" + a.getName());
				mTextView.setText(a.getName());
			}
		};
		mTextView = (TextView)findViewById(R.id.activity_main_text_title);
		initClick();
	}
	
	private void initClick(){
		Log.i("test", "这是:" + mTextView);
		mTextView.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {

				new Thread(new Runnable() {
					@Override
					public void run() {
						try {
							Thread.sleep(2000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					//采用这种方式新建Message更好,因为这种方法有内存池管理新建的Message对象
						Message msg = Message.obtain(); 
						msg.what = 1;
						msg.obj = new A("3","lilei");
						handler.sendMessage(msg);
					}
				}).start();
			}
		});
	}
}

然后下面是输出

08-16 16:31:31.196: I/test(31963): 收到消息:1
08-16 16:31:31.196: I/test(31963): 收到对象了吗:3,名称:lilei




二、Handler的线程间通信方法

这里Handler调用时的方法有post(Runnable), postAtTime(Runnable, long),postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message),sendMessageAtTime(Message, long)和sendMessageDelayed(Message,long)等方法,方法我就不一一介绍了,看方法名称就能猜测出来一点


但是这里就会发现一点,这里修改Title的方法其实是在主线程的,即使不看源码也可以看出来,因为回调时执行的代码就在主线程中,那么,这里就可以推测一点,是否代表着Handler在哪里新建,回调都会在对应线程执行?


如果根据Handler的构造器来看的话,是可以发现这一点的

public Handler() {  
        this(null, false);  
}  
public Handler(Callback callback, boolean async) {  
        if (FIND_POTENTIAL_LEAKS) {  
            final Class<? extends Handler> klass = getClass();  
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  
                    (klass.getModifiers() & Modifier.STATIC) == 0) {  
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  
                    klass.getCanonicalName());  
            }  
        }  
  
        mLooper = Looper.myLooper();  
        if (mLooper == null) {  
            throw new RuntimeException(  
                "Can't create handler inside thread that has not called Looper.prepare()");  
        }  
        mQueue = mLooper.mQueue;  
        mCallback = callback;  
        mAsynchronous = async;  
    }  

Looper.myLooper()获取了当前线程保存的Looper实例,然后又获取了这个Looper实例中保存的MessageQueue(消息队列),这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了

然后往下看找到最后调用handleMessage的地方的话就可以确认这一点

Handler在哪个线程新建,最后的handleMessage就在哪个线程执行

在这里我们也会发现一点,

 if (mLooper == null) {  

这一句,一般来说是不会无的放矢的,那说明获取到的当前的mLooper是有可能为空的,那么在什么情况下会为空呢?最后发现这种情况就是我们自己新建的工作线程中


除开android的ui线程,创建的工作线程默认是没有消息循环和消息队列的,如果想让该线程具有消息队列和消息循环,需要在线程中首先 调用Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环

class LooperThread extends Thread {    
    public Handler mHandler;    
    
    public void run() {    
        Looper.prepare();    
    
        mHandler = new Handler() {    
            public void handleMessage(Message msg) {    
                // process incoming messages here    
            }    
        };    
        Looper.loop();    
    }    
}    

那么在收集到这些,我们是否就可以利用这一点进行线程之间的通信?关于这一点,我用代码试验了一下,结论是可以的

public class MainActivity extends Activity{
	
	private Handler handler;
	private TextView mTextView;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		new Thread(new Runnable() {
			@Override
			public void run() {
				final A outTag = new A("15","lilei");
				 Looper.prepare(); 
				 handler = new Handler(){
					 public void handleMessage(Message msg) {
						 Log.i("test","callback1:" + msg.what);
						 A a = (A)msg.obj;
						 outTag.setId(a.getId());
						 outTag.setName(a.getName());
						 Log.i("test","callback2:" + a.getId() + ",aname:" + a.getName());
					 }
				 };
				 Looper.loop();
				 
			}
		}).start();
		mTextView = (TextView)findViewById(R.id.activity_main_text_title);
		initClick();
	}
	
	
	private void initClick(){
		mTextView.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				new Thread(new Runnable() {
					@Override
					public void run() {
						Message msg = Message.obtain();
						msg.what = 1;
						msg.obj = new A("50","hanmeimei");
						handler.sendMessage(msg);
					}
				}).start();
			}
		});
	}
}
在我点击了回调之后,在第一个新建的线程内输出了点击时新建的线程传递过去的值,如果将Looper.prepare(); 和Looper.loop();去除的话就会报错

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()


由此可以发现一点规律,其实在子线程里面进行数据操作之后在ui更新界面也是一样的


Handler会绑定在指定线程上,然后可以在任意其他线程将处理过的数据传递过来,或者通知,已经处理结束了




三、线程的队列执行


在这里留个尾巴,是Handler的可以队列执行的方式

 HandlerThread handlerThread = new HandlerThread("myHandlerThread");     
 handlerThread.start();     
 handler = new Handler(handlerThread.getLooper());     
<pre name="code" class="java"> handler<span style="font-family: Arial; font-size: 14px; line-height: 26px;">.sendEmptyMessage(1);</span>

 这样就可以在线程内队列执行任务,一个很方便的方法 


找时间的时候再对Handler深入了解








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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值