Android Handler的使用
Posted 胡刚2021
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Handler的使用相关的知识,希望对你有一定的参考价值。
参考文章:
Android Handler用法解析
Android:为什么子线程不能更新UI
因为 android 的UI更新是在主线程中做的,如果在主线程做一些耗时操作,比如进行网络请求,弱网环境下就极为容易阻塞住UI更新,画面看起来就像是被卡住了。
从Android4.0开始,Android不允许再主线程中进行网络请求,不允许在子线程中进行UI更新。
既然不允许在主线程进行网络请求,为什么还不允许在子线程更新UI呢?
假如允许多线程更新UI,但是访问UI是没有加锁的,一旦多线程抢占了资源,那么界面将会乱套更新了,体验效果就不言而喻了。而且,加了锁会让性能降低,所以在Android中规定必须在主线程更新UI。
那是不是在子线程更新UI就一定不行呢?
答案是:在onCreate方法中,如果你调用子线程不做太多耗时操作的话,是可以在onCreate方法中调用子线程去更新UI的。
下面的代码,在子线程更新UI就不会报错
public class MainActivity extends AppCompatActivity
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = findViewById(R.id.text);
new Thread(new Runnable()
@Override
public void run()
mTextView.setText("测试是否报出异常");
).start();
原因是ViewRootIml的checkThread方法会检查当前线程是否是主线程,不是的话会报错,而ViewRootImpl对象是在onResume方法回调之后才创建,所以上面的代码并不会报错。
@Override
public void requestLayout()
if (!mHandlingLayoutInLayoutRequest)
checkThread();
mLayoutRequested = true;
scheduleTraversals();
void checkThread()
if (mThread != Thread.currentThread())
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
当我们在子线程做了一些耗时操作后,要更新UI时,我们需要通知给主线程,这个通知使用的工具就是Handler。
我们需要在主线程中定义Handler
如果写成下面这种写法,就会容易产生内存泄漏
public class MainActivity extends AppCompatActivity
private ImagerView iv;
private Handler mHandler = new Handler()
@Override
public void handleMessage(Message msg)
switch (msg.what)
case value:
iv.setImageResoure(...)
break;
;
原因是,当Activity finish后,延时消息会继续存在主线程消息队列中1分钟,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致 Activity泄露。
因此我们需要写成下面这种形式
private static class MyHandler extends Handler
private final WeakReference<MainActivity> mTarget;
public MyHandler(MainActivity activity)
mTarget = new WeakReference<MainActivity>(activity);
@Override
public void handleMessage(@NonNull Message msg)
super.handleMessage(msg);
if (msg.what == 0)
Log.e("myhandler", "change textview");
// 当没有任何强引用到widget对象时使用get时突然返回null
MainActivity ma = mTarget.get();
ma.textView.setText("hahah");
我们需要将 MyHandler 定义成 static ,因为静态内部类不会引用外部类对象。而且,当我们要使用外部类对象的时候需要将其定义成 WeakReference ,这样一旦外部类销毁的时候使用weakWidget.get()就可以得到真实的Widget对象,因为弱引用不能阻挡垃圾回收器对其回收,你会发现当没有任何强引用到MainActivity对象时使用get时返回null。
然后在主线程创建一个类型为MyHandler的私有属性:
private Handler myHandler= new MyHandler(this);
创建一个线程,在子线程中向主线程发送消息
new Thread(new Runnable()
@Override
public void run()
myHandler.sendEmptyMessage(0);
).start();
还有一种情况是:如果主线程想通知子线程一些消息,那么就需要使用下面的代码:
先在主线程声明Handler
private Handler threadHandler;
然后创建一个线程
HandlerThread handlerThread = new HandlerThread("test Handler");
handlerThread.start();
然后,真正创建在主线程声明的 Handler 对象
threadHandler = new Handler(handlerThread.getLooper())
@Override
public void handleMessage(Message msg)
super.handleMessage(msg);
if (msg.what==2)
Toast.makeText(MainActivity.this, "主线程:", Toast.LENGTH_SHORT).show();
;
最后,我们需要在主线程调用
threadHandler.sendEmptyMessage(2);
这样 Handler 中的 handleMessage 方法处理消息。
以上是关于Android Handler的使用的主要内容,如果未能解决你的问题,请参考以下文章
Android--Handler的使用方法:在子线程中更新界面
Android Handler处理机制 ( 三 ) ——Handler,Message,Looper,MessageQueue