文章
博客 网店

 Android学习笔记4:关于handler以及它已过时的构造函数handler()


一.android消息驱动机制



Thread、Looper、Handler这三个是android消息驱动机制的核心对象,让我们先分析并了解一下一个线程拥有消息处理能力的过程:


class LooperThread extends Thread
{
    public Handler mHandler;
    public void run() 
    {
        Looper.prepare();//初始化该线程looper
        mHandler = new Handler()  //创建handler对象的同时,将该hanlder注册到线程looper中
        {
            //消息处理
            public void handleMessage(Message msg) 
            {
                switch(msg.what)
                {
                    case 0:
                        break;
                    //case ....
                    default:
                        break;
                }
            }
        };
        Looper.loop();//进入消息循环
    }
}

线程一但被执行(run函数)首先做的就是通过Looper.prepare()函数初始化线程的looper,接着创建handler,创建的过程中hanlder会自动把自己注册到线程looper中,最后Looper.loop()进入消息循环,将CPU交给内核。

其实activity处理消息也是类似的过程,但activity已经把这些代码隐藏了起来以便让我们只做最关心的事情。那么activity里怎么处理用户自己的消息呢?因为关于looper的事都已经处理好了,所以只要创建handler就OK啦。

二.Handler构造函数过时问题



用户消息在Activity内如何接收处理?实现方法如下代码所示:

public class MainActivity extends ActionBarActivity {
 
  //接收消息处理
  private Handler handler = new Handler() {  
    @Override  
    public void handleMessage(Message msg) {  
      if(msg.what==0)
      {
            }
        }};
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
      // ....
  }
}


但现在,这个代码编译时会得到一个警告:“ 警告: [deprecation] Handler中的Handler()已过时” 。下面小编对这个警告以及其解决办法的理解。

先不考虑这个警告的事情,上面的代码存在定的内存泄露风险,那么怎么泄露内存,原理是什么呢?

Handler应认为是属于内核的对象,内核和activity所在线程是异步的,当Activity被销毁时内核可能还在用这个Handler,于是内核不让释放Handler,于是这个Handler没用了,却错过了唯一一次被销毁 的机会,就这样占据着内存,这就是内存泄露。

三.解决办法



以下为小编认为的标准解决办法:

public class MainActivity extends Activity {
    public int m_var; //需要在消息处理中访问的成员变量,一定要声明成public
 
    MyHandler handler = new MyHandler(this);
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_auto);
    }
 
    //新的handler类要声明成静态类
    static class MyHandler extends Handler{
        WeakReference<MainActivity> mactivity;
         
        //构造函数,传来的是外部类的this
        public MyHandler(MainActivity activity){
            mactivity = new WeakReference<MainActivity>(activity);
        }
 
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);  
 
            MainActivity nactivity=mactivity.get();
            if(nactivity == null)
                return ;//avtivity都没了还处理个XXX
 
            switch (msg.what) {
            case 0:   
                //在这里通过nactivity引用外部类
                nactivity.m_var=0;          
                break;              
            default:
                break;
            }
        }
    }

为了防止内存泄露,官方要求将handler声明为静态类,静态就意味着全局,也就是说当app全部被销毁的时候内核不会忘记销毁你这个handler,可内存泄露问题解决了,却引来了另一个很棘手的问题,静态类是无法访问外部类成员的,

访问外部类的成员变量是消息处理代码非常迫切的需求,那怎么办呢?就把外部类的对象引用通过构造函数的参数传递过来不就可以访问了吗,的确是这样,但参数又被WeakReference包装了一下又是什么意思呢?

还是为了解决最初的那个问题,怕的是消息没处理完呢activity被销毁了。在我看来WeakReference就是个具有感知内部对象是否已经被销毁能力的东东。怎么感知?当然是.get()函数啦。最后还要提醒一下,要在handler消息处理中访问的activity成员要声明成public才行,这是java类的一个常识。

那么,到此警告的问题被解决了吗?并没有,这个警告的意思是,handler的隐式传递Looper的构造函数不建议再使用了,上面的代码显然没有解决这个问题。google认为隐式传递的构造函数不合理,所以不建议再使用了,更深的原因小编也说不出一二来,总之,新的代码中应该做的就是handler创建时需要手动指明往哪个Looper注册自己,也就是说创建handler对象时要给构造函数传递当前线程的Looper,修改后的代码如下:

public class MainActivity extends Activity {
    public int m_var; //需要在消息处理中访问的成员变量,一定要声明成public
 
    MyHandler handler = new MyHandler(Looper.myLooper(),this);//获取Looper并传递
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_auto);
    }
 
    //新的handler类要声明成静态类
    static class MyHandler extends Handler{
        WeakReference<MainActivity> mactivity;
         
        //构造函数,传来的是外部类的this
        public MyHandler(@NonNull Looper looper,MainActivity activity){
            super(looper);//调用父类的显式指明的构造函数
            mactivity = new WeakReference<MainActivity>(activity);
        }
 
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);  
 
            MainActivity nactivity=mactivity.get();
            if(nactivity == null)
                return ;//avtivity都没了还处理个XXX
 
            switch (msg.what) {
            case 0:   
                //在这里通过nactivity引用外部类
                nactivity.m_var=0;          
                break;              
            default:
                break;
            }
        }
    }
 


至此警告已被解除

芯艺工作室    蒙ICP备06005492号
Copyright© 2004-2023 ChipArt Studio All Rights Reserved