Handler杂谈【二】Handler的用法及解析

原文地址:https://www.jianshu.com/p/7cdf2c2ed354
https://blog.csdn.net/qq_37321098/article/details/81535449#2.常用api

一、handler作用:

  • 传递消息Message
//2种创建消息方法
//1.通过handler实例获取
Handler handler = new Handler();
Message message=handler.obtainMessage();
 
//2.通过Message获取
Message message=Message.obtain();
 
 
//源码中第一种获取方式其实也是内部调用了第二种:
public final Message obtainMessage(){
    
    
    return Message.obtain(this);
}

不建议直接new Message,Message内部保存了一个缓存的消息池,我们可以用obtain从缓存池获得一个消息,Message使用完后系统会调用recycle回收,如果自己new很多Message,每次使用完后系统放入缓存池,会占用很多内存的。

//传递的数据
Bundle bundle = new Bundle();
bundle.putString("msg", "传递我这个消息");
//发送数据
Message message = Message.obtain();
message.setData(bundle);   //message.obj=bundle  传值也行
message.what = 0x11;
handler.sendMessage(message);
 
 
 
//数据的接收
final Handler handler = new Handler() {
    
    
        @Override
        public void handleMessage(Message msg) {
    
    
            super.handleMessage(msg);
            if (msg.what == 0x11) {
    
    
                Bundle bundle = msg.getData();
                String date = bundle.getString("msg");
            }
        }
};

  • 子线程通知主线程更新ui
        //创建handler
        final Handler handler = new Handler() {
    
    
            @Override
            public void handleMessage(Message msg) {
    
    
                super.handleMessage(msg);
                if (msg.what == 0x11) {
    
    
                    //更新ui
                          ......
                }
            }
        };
 
        new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                //FIXME 这里直接更新ui是不行的
                //还有其他更新ui方式,runOnUiThread()等          
                message.what = 0x11;     
                handler.sendMessage(message);  
            }
        }).start();

二、常用api

    //消息
    Message message = Message.obtain();
    //发送消息
        new Handler().sendMessage(message);
    //延时1s发送消息
        new Handler().sendMessageDelayed(message, 1000);
    //发送带标记的消息(内部创建了message,并设置msg.what = 0x1)
        new Handler().sendEmptyMessage(0x1);
    //延时1s发送带标记的消息
        new Handler().sendEmptyMessageDelayed(0x1, 1000);
    //延时1秒发送消息(第二个参数为:相对系统开机时间的绝对时间,而SystemClock.uptimeMillis()是当前开机时间)
        new Handler().sendMessageAtTime(message, SystemClock.uptimeMillis() + 1000);
 
    //避免内存泄露的方法:
    //移除标记为0x1的消息
        new Handler().removeMessages(0x1);
    //移除回调的消息
        new Handler().removeCallbacks(Runnable);
    //移除回调和所有message
        new Handler().removeCallbacksAndMessages(null);

三、handler使用避免内存泄露

  • handler怎么使用会产生内存泄露?
public class MainActivity extends AppCompatActivity {
    
    

   final Handler handler = new Handler() {
    
    
       @Override
       public void handleMessage(Message msg) {
    
    
           super.handleMessage(msg);
               ......
       }
   };

   @Override
   protected void onCreate(Bundle savedInstanceState) {
    
    
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       //activity被执行时,被延迟的这个消息存于主线程消息队列中1分钟,
       //此消息包含handler引用,而handler由匿名内部类创建,持有activity引用,
       //activity便不能正常销毁,从而泄露
       handler.postDelayed(new Runnable() {
    
    
           @Override
           public void run() {
    
    
               ......
           }
       }, 1000 * 60);
   }

页面初始化,开启了一个延时的handler事件,最后页面关闭的时候,没有及时释放Handler,会导致该handler一直持有当前Activity的引用,导致内存泄漏

  • 如何避免handler的内存泄露?
public class MainActivity extends AppCompatActivity {
    
    
 
    //创建静态内部类
    private static class MyHandler extends Handler{
    
    
        //持有弱引用MainActivity,GC回收时会被回收掉.
        private final WeakReference<MainActivity> mAct;
        public MyHandler(MainActivity mainActivity){
    
    
            mAct =new WeakReference<MainActivity>(mainActivity);
        }
        @Override
        public void handleMessage(Message msg) {
    
    
            MainActivity mainAct=mAct.get();
            super.handleMessage(msg);
            if(mainAct!=null){
    
    
                //执行业务逻辑
            }
        }
    }
    private static final Runnable myRunnable = new Runnable() {
    
    
        @Override
        public void run() {
    
    
            //执行我们的业务逻辑
        }
    };
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MyHandler myHandler=new MyHandler(this);
        //延迟5分钟后发送
        myHandler.postDelayed(myRunnable, 1000 * 60 * 5);
    }
}

雷区

  • 使用Handler.post(Runnable)的注意事项

Handler.post(Runnable)其实就是生成一个what为0的Message,调用

myHandler.removeMessages(0);

会使runnable任务从消息队列中清除。

详细解释可以参考这:Android Handler使用Message的一个注意事项

  • 子线程直接创建Handler发生异常

子线程直接创建Handler,系统会抛出异常

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

原因是非主线程没有loop对象,所以要调用Looper.prepare()方法,而且如果主线程给子线程发送消息,还要调用一个Looper.loop()的方法(此方法保证消息队列中的消息被不停的拿出,并被处理)

class MyThread extends Thread{
    
    
        @Override
        public void run() {
    
    
            super.run();
            Looper.prepare();
            Handler handler = new Handler() {
    
    
                @Override
                public void handleMessage(Message msg) {
    
    
                    super.handleMessage(msg);
                    //处理消息
                }
            };
            Looper.loop();
        }
}
  • 空指针异常

activity如被finish,但是handler刚好还在处理消息,如果需要用的资源已被释放,则会出现空指针异常。

所以在onDestory中去remove掉我们要处理的事件,还是有必要的。不想处理就直接try catch或者判空。

  • removeCallbacks失效

有时候你会发现removeCallbacks会失效,不能从消息队列中移除。
出现这情况是activity切入后台,再回到前台,此时的runnable由于被重定义,就会和原先的runnable并非同一个对象。所以这么做,加上static即可

static Handler handler = new Handler();
static Runnable myRunnable = new Runnable() {
    
    
        @Override
        public void run() {
    
    
            //执行我们的业务逻辑
        }
    };

这样,因为静态变量在内存中只有一个拷贝,保证runnable始终是同一个对象。

四、handlerThread

  • handlerThread是什么?

异步存在形式有thread,handlerThead,asyncTask,线程池,intentService,handlerThread继承thread,不过内部比普通线程多了一个Looper。

//内部Looper.prepare()
@Override
    public void run() {
    
    
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
    
    
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
}

  • HandlerThread使用及销毁
public class MainActivity extends AppCompatActivity {
    
    
 
    private HandlerThread thread;
    static Handler mHandler;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //创建一个HandlerThread并启动它
        thread = new HandlerThread("MyHandlerThread");
        thread.start();
 
        //使用HandlerThread的looper对象创建Handler
        mHandler = new Handler(thread.getLooper(), new Handler.Callback() {
    
    
            @Override
            public boolean handleMessage(Message msg) {
    
    
                //这个方法是运行在 handler-thread 线程中的,可以执行耗时操作,因此不能更新ui,要注意
                if (msg.what == 0x1) {
    
    
                    try {
    
    
                        Thread.sleep(3000);
                        Log.e("测试: ", "执行了3s的耗时操作");
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    //这个方法是运行在 handler-thread 线程中的,可以执行耗时操作,因此不能更新ui,要注意
//                    ((Button) MainActivity.this.findViewById(R.id.button)).setText("hello");
                }
                return false;
            }
        });
 
        //停止handlerthread接收事件
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
                thread.quit();
            }
        });
 
        //运行
        mHandler.sendEmptyMessage(0x1);
    }
 
}

上面demo中,只要调用了

mHandler.sendEmptyMessage(0x1);

就会开始执行任务

几个地方要注意:

  1. handleMessage()可以做耗时操作,但是不能更新ui
  2. 如果不手动的调用HandlerThread.quit()或者HandlerThread…quitSafely()方法,HandlerThread会将持续的接收新的任务事件。
  3. 只有handleMessage()方法执行完,这轮的任务才算完成,HandlerThread才会去执行下一个任务。而且在此次执行时,即使手动的去调用quit()方法,HandlerThread的此次任务也不会停止。但是,会停止下轮任务的接收。
举例:
 
//耗时任务换成这个,点击按钮执行quit()方法,发现此次任务依旧执行
for (int i = 0; i < 99999999; i++) {
    
    
    Log.e("测试: ", "输出" +i);
}

Guess you like

Origin blog.csdn.net/abc6368765/article/details/103651837