handler
https://www.jianshu.com/p/f70ee1765a61
如何判断当前线程是不是主线程
Looper.myLooper() == Looper.getMainLooper()
Looper.getMainLooper().getThread() == Thread.currentThread()
Looper.getMainLooper().isCurrentThread()
进度条的显示,可以直接在子线程里面处理。特例。
更新ui显示只能在主线程。
https://baike.baidu.com/item/handler/10404534?fr=aladdin
Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作
Handler对象在进行初始化的时候,会默认的自动绑定消息队列。利用类post方法,可以将Runnable对象发送到消息队列中,按照队列的机制按顺序执行不同的Runnable对象中的run方法。
-
voidhandleMessage(Message msg):处理消息的方法,通常是用于被重写!
-
sendEmptyMessage(int what):发送空消息
-
sendEmptyMessageDelayed(int what,long delayMillis):指定延时多少毫秒后发送空信息
-
sendMessage(Message msg):立即发送信息
-
sendMessageDelayed(Message msg):指定延时多少毫秒后发送信息
-
final booleanhasMessage(int what):检查消息队列中是否包含what属性为指定值的消息 如果是参数为(int what,Object object):除了判断what属性,还需要判断Object属性是否为指定对象的消息
创建handler(),并实现处理消息的方法。
public void handleMessage(@NonNull Message msg);
按钮1和按钮2点击事件,模拟耗时操作,开启线程,然后发msg给hander; 处理函数根据消息参数,执行不同动作。
hander2 是在子线程处理接收消息,bt3,主线程发往子线程。
子线程创建handler需要加looper。
Looper.prepare();
Looper.loop();
子线程没有做控件操作,因为只有主线程才能进行UI操作。
package com.example.handlerdemo; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; import android.view.View; import android.widget.TextView; import android.widget.Toast; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.Random; public class MainActivity extends AppCompatActivity { private TextView txt; private String str = ""; //1.实例化 //2.在子线程中发送(空)消息 private Handler handler = new Handler(){ //3.由Handler对象接收消息,并处理 //只要Handler发消息了,必然出发该方法,并且会传入一个Message对象 @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); if(msg.what == 1) { //btn1 txt.setText(str); }else if(msg.what == 2) { //btn2 String str2 = "what:"+msg.what+",arg1:"+msg.arg1 +",arg2:"+msg.arg2+",随机数"+((Random)msg.obj).nextInt(); Toast.makeText(MainActivity.this, str2, Toast.LENGTH_SHORT).show(); } } }; Handler handler2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); txt = findViewById(R.id.txt); new Thread(){ @Override public void run() { super.run(); Looper.prepare(); //准备,开始一个消息循环。系统会自动为主线程开启消息循环 handler2 = new Handler(){ @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); Log.e("TAG","由主线程传递过来的Message,它的what是:"+msg.what); } }; Looper.loop(); //循环。相当于产生了一个while(true){....} } }.start(); } //每个线程仅有一个Looper,对应一个MessageQueue public void myclick(View view) { switch (view.getId()){ case R.id.btn1: new Thread(){ @Override public void run() { str = get(); Log.e("TAG",str+"========"); //发消息 //handler.sendMessage() //发空消息.参数:what?是Message对象的一个属性 handler.sendEmptyMessage(1); // runOnUiThread(new Runnable() { // @Override // public void run() { // //txt.setText(msg); // } // }); } }.start(); break; case R.id.btn2: new Thread(){ @Override public void run() { str = get()+"~~~~~~~"; //what :用于区分handler发送消息的不同线程来源 // arg1,arg2:如果子线程需要想主线程传递整型数据,则可用这些参数 // obj:Object 。 Message msg = new Message(); msg.what = 2; msg.arg1 = 666; msg.arg2 = 2333; msg.obj = new Random(); //handler.sendEmptyMessage(2); handler.sendMessage(msg); } }.start(); break; case R.id.btn3: handler2.sendEmptyMessage(1000); break; case R.id.btn4: startActivity(new Intent(this,TimerActivity.class)); break; case R.id.btn5: startActivity(new Intent(this,MemoryLeakActivity.class)); break; } } private String get() { try { URL url = new URL("http://www.imooc.com/api/teacher?type=3&cid=1"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(6000); if(conn.getResponseCode() == HttpURLConnection.HTTP_OK) { InputStream in = conn.getInputStream(); byte[] b = new byte[1024]; int len = 0; ByteArrayOutputStream baos = new ByteArrayOutputStream(); while((len = in.read(b))>-1){ baos.write(b,0,len); } String msg = new String(baos.toByteArray()); return msg; } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return ""; } }
使用post实现秒表。
package com.example.handlerdemo; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.SystemClock; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; public class TimerActivity extends AppCompatActivity { private TextView title,timer,txt; private ImageView btn; private boolean flag = false; //1.用于区别当前对按钮的点击是属于开启计时器还是停止计时器2.控制while循环 //post postDelay postAtTime private Handler handler = new Handler(); private int i; private String time; private Runnable runnable = new Runnable() { @Override public void run() { int min = i / 60; int sec = i % 60; // 00:00 time = (min < 10 ? "0" + min : "" + min) + ":" + (sec < 10 ? "0" + sec : "" + sec); timer.setText(time); i++; handler.postDelayed(runnable,1000); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_timer); title = findViewById(R.id.title); timer = findViewById(R.id.timer); txt = findViewById(R.id.txt); btn = findViewById(R.id.btn); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(flag == false) { flag = true; title.setText("工作中..."); btn.setImageResource(R.mipmap.stop); txt.setText(""); i = 1; new Thread() { @Override public void run() { //递归 handler.postDelayed(runnable, 1000); } }.start(); }else{ flag = false; title.setText("计时器"); btn.setImageResource(R.mipmap.start); txt.setText("用时:"+time); handler.removeCallbacks(runnable); } } }); /* btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //在post方法中,我们可以处理一切和视图相关的操作 new Thread(){ @Override public void run() { super.run(); // try { // sleep(2000); // } catch (InterruptedException e) { // e.printStackTrace(); // } //运行机制:判断当前线程是不是主线程 //如果是:则直接执行Runnable对象的run方法 //如果不是:则由handler调用post方法 // runOnUiThread(new Runnable() { // @Override // public void run() { // btn.setImageResource(R.mipmap.stop); // } // }); // handler.post(new Runnable() { // @Override // public void run() { // btn.setImageResource(R.mipmap.stop); // } // }); // handler.postDelayed(new Runnable() { // @Override // public void run() { // btn.setImageResource(R.mipmap.stop); // } // },3000); //3000:指定是等待多长时间 // handler.postAtTime(new Runnable() { // @Override // public void run() { // btn.setImageResource(R.mipmap.stop); // } // }, SystemClock.uptimeMillis() +3000); //参数2:时间点。当前系统时间+3秒 } }.start(); } });*/ } }
内存泄漏
延迟的Message对象-->Handler对象-->当前Activity对象
延迟发送的message,
或者发送大量的message,等同于消息没有处理完成,activity意外销毁。
由于占用了 activity引用。窗体销毁了,但是没有释放资源。
使用弱引用解决,或者直接在退出时候销毁
handler.removeCallbacksAndMessages(null);
package com.example.handlerdemo; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.View; import android.widget.TextView; import java.lang.ref.WeakReference; public class MemoryLeakActivity extends AppCompatActivity { private TextView txt; // private Handler handler = new Handler(){ // @Override // public void handleMessage(@NonNull Message msg) { // super.handleMessage(msg); // txt.setText("这是延迟发送消息后产生的新文本"); // } // }; private MyHandler handler = new MyHandler(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_memory_leak); txt = findViewById(R.id.txt); findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //延迟的Message对象-->Handler对象-->当前Activity对象 handler.sendEmptyMessageDelayed(0,5*1000); } }); } //解决Handler引发的内存泄露问题 //1.主动清除所有的Message //2.弱引用 // @Override // protected void onDestroy() { // super.onDestroy(); // handler.removeCallbacksAndMessages(null); // } private static class MyHandler extends Handler{ private WeakReference<MemoryLeakActivity> wr; // private MemoryLeakActivity mla; public MyHandler(MemoryLeakActivity mla){ // this.mla = mla; wr = new WeakReference<>(mla); } @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); Log.e("TAG","延迟处理消息"); MemoryLeakActivity mla = wr.get(); mla.txt.setText("xxxx"); } } }
关于handler内存问题
https://blog.csdn.net/androidsj/article/details/79865091
public static class MyHandler extends Handler { private WeakReference<Activity> reference public MyHandler(Activity activity) { reference = new WeakReference<Activity>(activity); } @Override public void handleMessage(Message msg) { if (reference.get() != null) { switch (msg.what) { case 0: // do something... break; default: // do something... break; } } } } 静态内部类实现,据说这是最标准的写法。 private MyHandler handler = new MyHandler(this); static class MyHandler extends Handler { WeakReference weakReference; public MyHandler(SecondActivity activity) { weakReference = new WeakReference(activity); } @Override public void handleMessage(Message msg) { } } 另一种情况: 不规范的写法: private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { }; }; 正确的写法: private Handler mHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { return false; } });
总结用法:
Android更新UI的方式?
1、runOnUIThread
2、handler post
3、handler sendMessage
4、view post
(1). 使用静态内部类+弱引用的方式:
静态内部类不会持有外部类的的引用,当需要引用外部类相关操作时,可以通过弱引用还获取到外部类相关操作,弱引用是不会造成对象该回收回收不掉的问题,不清楚的可以查阅JAVA的几种引用方式的详细说明。
private Handler sHandler = new TestHandler(this);
static class TestHandler extends Handler {
private WeakReference<Activity> mActivity;
TestHandler(Activity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Activity activity = mActivity.get();
if (activity != null) {
//TODO:
}
}
}
(2). 在外部类对象被销毁时,将MessageQueue中的消息清空。例如,在Activity的onDestroy时将消息清空。
@Override
protected void onDestroy() {
handler.removeCallbacksAndMessages(null);
super.onDestroy();
}