多线程编程——Handler

Handler与多线程

概述

Handler译为处理者,不难理解,它的作用就算发送和处理消息。

在Android开发中,我们常常会使用单独的线程来完成某些操作。

如用一个线程来完成从网络上下载图片,然后显示在ImageView上。

在多线程操作时,Android中必须保证以下两点:

  1. 不要阻塞UI线程
  2. 不要在UI线程之外访问Android UI工具包 

有了以上两点限制,我们在线程之间的消息如何进行传递?回顾一下Handler的作用,确实,线程之间的消息传递就是依靠它。

出处见图上水印

初级案例

准备Button和TextView 

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        android:id="@+id/text1"
        android:textSize="33sp"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/text1"
        android:id="@+id/button1"
        android:onClick="download"
        android:text="下载"/>

</android.support.constraint.ConstraintLayout>

要实现Handler线程之间的消息传递,需要重写Handler方法。

package com.example.a4_9handler;

import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.text1);
    }

    //匿名内部类
    private Handler handler=new Handler(){
        //重写Handler方法
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 100:
                    textView.setText("下载完成");
                    break;
            }
        }
    };

    //使用线程模拟下载操作
    public void download(View v){
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    break;
                }
                //下载完成后更新UI状态
                //错误的示范:textView.setText("下载完成");
                //发送了一个标记——100
                handler.sendEmptyMessage(100);

            }
        }).start();
    }
}

效果如下: 

Handler常用API

使用Handler通常完成以下两点工作:

  1. 消息调度和在将来的某个时间点执行一个Runnable
  2. 多个任务加入到一个队列中执行

Handler的相关方法:

  • void handleMessage(Message msg):处理消息的方法,通常是用于被重写!
  • sendEmptyMessage(int what):发送空消息
  • sendEmptyMessageDelayed(int what,long delayMillis):指定延时多少毫秒后发送空信息
  • sendMessage(Message msg):立即发送信息
  • sendMessageDelayed(Message msg):指定延时多少毫秒后发送信息
  • final boolean hasMessage(int what):检查消息队列中是否包含what属性为指定值的消息 如果是参数为(int what,Object object):除了判断what属性,还需要判断Object属性是否为指定对象的消息

在相关方法的帮助下,再看之前的案例

handler.sendEmptyMessage(100);

实际上是发送了一个空消息,并标记为100

其等价于下面一段话

//获取一个消息对象
Message msg=handler.obtainMessage();
//标记为100
msg.what=100;
//任意类型
msg.obj="要存储的信息";
//发送消息
handler.sendMessage(msg);

另外还可以延迟发送(之前是立即发送)

//在指定时间后发送消息(当前时间3秒后)
handler.sendEmptyMessageAtTime(200,System.currentTimeMillis()+3000);
//延迟多少时间后发送消息(延迟2秒)
handler.sendEmptyMessageDelayed(300,2000);

//对于非空有...
handler.sendMessageDelayed();
handler.sendMessageAtTime();

Handler内部实现原理

Handler实现机制

  1. Message对象:表示要传递的一个消息。(要传递的数据对象打包成了一个Message,内部使用链表数据结构实现了一个可重复利用的消息池,避免了资源浪费
  2. MessageQueue对象:Android启动程序时会在UI线程创建,存放消息对象的消息队列,先进先出原则。(sendMessage实际上就是把Message放如MessageQueue的过程
  3. Looper对象:负责管理当前线程的消息队列MessageQueue(循环检查MessageQueue是否有消息,有就取出来
  4. Handler对象:负责把消息push到消息队列中,以及接受Looper从消息队列中取出的消息,通过handlerMessage方法处理消息。

出处见图上水印

Handler内存泄露问题分析 

程序运行过程中会使用内存,正常情况下,退出时会释放内存。但是,如果在退出时占用的部分没有被正常释放,就会造成内存泄漏。

回顾之前的案例中,IDE已经给出了相应的警告——

This Handler class should be static or leaks might occur

这个处理程序类应该是静态的,否则可能会发生泄漏。

显然这么写是不合理的。

在外部类内定义Handler,Handler会持有外部类的引用(内部类的对象会依赖于外部类的对象),在使用的时候,退出Activity,Activity对象会被销毁,此时如何Handler还在工作,且获取外部类的引用(Handler隐式的获取Activity对象),结果就是Activity无法正常退出(Activity依然占有内存

即存在内存泄漏风险

案例分析

新建一个Activity(HandlerMemoryActivity)

package com.example.a4_9handler;

import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class HandlerMemoryActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_memory);
        //使用handler延迟执行一个Runnable
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                System.out.println("Runnable");
            }
            //延迟10分钟
        },1000*60*10);
        //10分钟后关闭当前Activity
        finish();
    }
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };
}

启动程序,程序一闪而过,看似退出,实际上还是在后台继续运行。

解决方案

  • 定义一个内部类时,会默认拥有外部类对象的引用,所以建议使用内部类时,最好定义为一个静态内部类
  • 引用的强弱:强引用(普通new,不会自动回收) > 软引用(内存不足时候回收) > 弱引用(对象不存在就引用不到),所以最好使用弱引用
package com.example.a4_9handler;

import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import java.lang.ref.WeakReference;

public class HandlerMemoryActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_memory);
        //使用handler延迟执行一个Runnable
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                System.out.println("Runnable");
            }
            //延迟10分钟
        },1000*60*10);
        //10分钟后关闭当前Activity
        finish();
    }
/*    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };*/

    private MyHandler handler=new MyHandler(this);
    private static class MyHandler extends Handler{
        //弱引用,引用当前Activity
        WeakReference<HandlerMemoryActivity> weakReference;
        public MyHandler(HandlerMemoryActivity activity){
            weakReference=new WeakReference<HandlerMemoryActivity>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            HandlerMemoryActivity activity=weakReference.get();
            if (activity!=null){
            }
        }
    }
}

Handler实现闪屏页功能

新建一个Activity(,并准备一张图片(用作闪屏页)

 

布局方面,Imageview也好,background也好,反正就是把准备好的图片设置为Activity的背景

android:background="@mipmap/s123"
<ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"        
        android:src="@mipmap/s123"
        />

闪屏页显然不需要标题栏,于是可以在配置清单文件里设置一下全屏的主题。 

android:theme="@android:style/Theme.DeviceDefault.Light.NoActionBar.Fullscreen"

完整代码如下:

package com.example.a4_9handler;

import android.app.Activity;
import android.content.Intent;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class SplashActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                startMainActivity();
            }
            //延迟三秒
        },3000);
    }
    private void startMainActivity(){
        //启动主页
        Intent intent=new Intent(this,MainActivity.class);
        startActivity(intent);
    }

    private Handler handler=new Handler();

}

效果如下:三秒后从图1跳到图2 

AsyncTask

Android提供的一个抽象类,可以更好的协助我们完成多线程编程,这部分在

https://blog.csdn.net/nishigesb123/article/details/89145264

这篇文章中描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云无心鸟知还

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值