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主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作

1. 传递Message。用于接受子线程发送的数据, 并用此数据配合主线程更新UI。
在Android中,对于UI的操作通常需要放在主线程中进行操作。如果在子线程中有关于UI的操作,那么就需要把数据消息作为一个Message对象发送到消息队列中,然后,由Handler中的handlerMessage方法处理传过来的数据信息,并操作UI。当然,Handler对象是在主线程中初始化的,因为它需要绑定在主线程的消息队列中。
类sendMessage(Message msg)方法实现发送消息的操作。 在初始化Handler对象时重写的handleMessage方法来接收Message并进行相关操作。
2. 传递Runnable对象。用于通过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();
}



 

 

 

posted @ 2020-07-03 20:19  于光远  阅读(340)  评论(0编辑  收藏  举报