_055_Android_Handler

转自https://blog.csdn.net/geanwen/article/details/54233895,感谢作者的无私分享。 

容易造成内存泄漏的一种Handler使用方法:将Handler声明为Activity的内部类。在Java语言中,非静态内部类会持有外部类的一个隐试引用,这样就可能造成外部类无法被垃圾回收。而导致内存泄漏。

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

 那么正确的使用就是:

1.将Handler声明为静态内部类。并持有外部类的若引用。

2.在子线程中使用Handler,这是需要我们自己创建一个Looper对象。

下面代码介绍一下第一种用法:

/**
 *  正确使用Handler方式
 */
public class HandlerActivity extends AppCompatActivity {
 
    private final MyHandler mHandler = new MyHandler(this);
 
    /**
     * 静态的匿名内部类不会持有外部类的引用
     */
    private static final Runnable sRunnable = new Runnable() {
        @Override
        public void run() {
            // ...你的操作
 
        }
    };
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
 
        mHandler.post(sRunnable);
    }
 
 
    /**
     * 声明一个静态的Handler内部类,并持有外部类的弱引用
     */
    private static class MyHandler extends Handler{
 
        private final WeakReference<HandlerActivity> mActivty;
 
        private MyHandler(HandlerActivity mActivty) {
            this.mActivty = new WeakReference<HandlerActivity>(mActivty);
        }
 
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            HandlerActivity activity = mActivty.get();
            if (activity != null){
                // ....
 
            }
        }
    }
 
}

上面在Activity使用就需要声明一个内部类,下面再看一个,将自定义Handler抽出去,也同样达到效果的小栗子:

1.首先创建一个类,通过泛型将实例传入

public class UIHandler<T> extends Handler {
 
    protected WeakReference<T> ref;
 
    public UIHandler(T cls){
        ref = new WeakReference<T>(cls);
    }
 
    public T getRef(){
        return ref != null ? ref.get() : null;
    }
}

2.看下activity中使用,直接用myHandler对象发送message即可。

private static class UIMyHandler extends UIHandler<HandlerActivity>{
 
        public UIMyHandler(HandlerActivity cls) {
            super(cls);
        }
 
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            HandlerActivity activity = ref.get();
            if (activity != null){
                if (activity.isFinishing())
                    return;
 
                switch (msg.what){
 
                    case 1:{
 
                        break;
                    }
 
                    // ...
 
                }
 
            }
        }
    }
 private UIMyHandler myHandler = new UIMyHandler(this);

最后在简单介绍一下,Java弱引用以及软引用还有虚引用。

个人认为,如果只是想避免OutOfMemory异常的发生,则可以使用软引用。若果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。
          和弱引用功能类似的是WeakHashMap,他对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的回收,回收以后,其条目从映射中有效的一处。WeakHashMap使用ReferenceQueue实现的这种机制。
          java.lang.ref包中提供了几个类:SoftReference类—>软引用;
                                                              WeakReference类—>弱引用;
                                                              PhantomReference—>虚引用;(相当于没有持有引用,形同虚设,随时可能被回收)
          弱引用实践场景:自己定义Handler类采用弱引用方式,防止内存泄漏。
          软引用场景:应用中肯定会有很多的默认的图片资源,会多次用到。如果每次都去获取,读取文件需要硬件操作,速度慢,导致性能降低。所以考虑将图片缓存起来,需要时直接从内存中获取,但是图片占用内存空间很大,容易OutOfMemory。这时考虑软引用。 

 

 Handler实现原理

使用Handler方式进行异步消息处理主要由Message,Handler,MessageQueue,Looper四部分组成:

(1)Message,线程之间传递的消息,用于不同线程之间的数据交互。Message中的what字段用来标记区分多个消息,arg1、arg2 字段用来传递int类型的数据,obj可以传递任意类型的字段。

(2)Handler,用于发送和处理消息。其中的sendMessage()用来发送消息,handleMessage()用于消息处理,进行相应的UI操作。

(3)MessageQueue,消息队列(先进先出),用于存放Handler发送的消息,一个线程只有一个消息队列。

(4)Looper,可以理解为消息队列的管理者,当发现MessageQueue中存在消息,Looper就会将消息传递到handleMessage()方法中,同样,一个线程只有一个Looper。

Handler实现原理如下图:

结合上文的的代码示例以及上图的实现流程,要使用Handler实现异步消息处理,首先我们需要在主线程中创建Handler对象并重写handleMessage()方法,然后当子线程中需要进行UI操作时,就创建一个Message对象,并通过Handlerr将这条消息发送出去。之后这条消息会被添加到MessageQueue的队列中等待被处理,而Looper则会一直尝试从MessageQueue中取出待处理消息,最后分发回Handler的handleMessage()方法中。由于Halldler是在主线程中创建的,所以此时handleMessage()方法中的代码也会在主线程中运行,从而实现子线程通过Handler机制实现UI线程操作的目的。

4. Handler内存泄漏分析

4.1 Handler内存泄漏问题的引出:

上面的Handler实现代码中,其实在Android Studio中会提示以下问题:

大致意思就是应该让Handler类为静态的,不然就会产生内存泄漏。 原因也说的很清楚,Handler被声明为一个非静态内部类或者匿名类可能会阻止外部类的垃圾回收(大家可以了解下Android的gc回收机制)。过多的内存泄漏使程序占用的内存超出系统限制,导致OOM(内存溢出),程序出错。

4.2 防止Handler引起内存泄漏:

方法一:通过程序逻辑进行保护:

(1)在关闭Activity时停掉对应的后台线程。线程停止就相当于切断了Handle和外部链接的线,Activity自然会在合适的时候被回收。

(2)如果Handler是被delay的Message持有了引用,那就使用Handler的removeCallbacks()方法将消息对象从消息队列移除即可。

方法二:将Handler声明为静态类,静态类不持有外部类的对象,所以Activity可以被随意回收。此处使用了弱引用WeakReference,也就是说当在内存不足时,系统会销毁弱/回收引用引用的对象,从而达到优化内存的目的。

 

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值