Handler

本文深入探讨了Android中的Handler机制,讲解了如何利用Handler实现线程间的通信,包括创建Handler、发送消息、处理消息的方法。同时,提供了多个实战案例,如点击事件触发子线程下载并更新UI进度条、倒计时功能的实现,详细展示了Handler在实际开发中的应用。通过对MessageQueue和Looper的理解,帮助开发者更好地理解和运用Handler进行异步操作和UI更新。
摘要由CSDN通过智能技术生成
/*
Handler:
每一个 Handler 实例与一个线程关联,每一个线程又会维护一个自己的 MessageQueue
  ,当我们创建一个 Handler 的时候我们需要制定一个 Looper 对象(Looper对象对应一个线程)
  ,这样就将一个 Handler 对象和一个线程绑定到了一起,随后就可以编写我们的耗时操作
  ,然后通过 Handler 将消息塞入线程的 MessageQueue中,当对应线程从 MessageQueue 取出该条消息的时候
  ,就会回调 Handler 的 handleMessage方法并拿到消息,这样就完成了跨线程通信。

void handleMessage(Message msg):在该方法中处理其他线程传递过来的消息(用的非常多)
sendEmptyMessage(int what):发送一条空消息,what 可以理解为消息 ID , int类型值
sendEmptyMessageDelayed(int what,long delayMillis):延时发送空消息,what 为自定义 ID
sendMessage(Message msg):发送消息,msg 是打包的消息内容,what只是其中一个属性值
sendMessageDelayed(Message msg):延时发送,msg是消息内容
hasMessage(int what):检查 MessageQueue 中是否包含一条 ID 为 what 的消息。what 是用户自定义的整形数,可作为消息 ID

在主线程创建Handler,触发点击事件,有可能做大量耗时操作就创建子线程new Thread(new Runnable() { ,
获取设置的消息Message message = Message.obtain(); (这里不建议直接new Message,Message内部保存了一个缓存的消息池
,我们可以用obtain从缓存池获得一个消息,Message使用完后系统会调用recycle回收,如果自己new很多Message
,每次使用完后系统放入缓存池,会占用很多内存的。)
    ,打包发送给Handler          (在子线程里还可以添加定时和handler.post的相关操作)
    ,在handleMessage(Message msg)中处理其他线程传递过来的消息,通过what找目标,实现改变主线程UI的跨线程操作。

 */

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //UI线程,做轻量级的事情
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //主线程UI的对象
        final TextView textView = (TextView) findViewById(R.id.tv);

        //创建Handler
        final Handler handler = new Handler() {
            @Override//在该方法中处理其他线程传递过来的消息
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //主线程接到子线程发出来的消息,处理消息
                Log.d(TAG, "handleMessage:" + msg.what);
                //通过what找目标
                if (msg.what == 1002) {
                    //改变主线程UI
                    textView.setText("imooc");
                    Log.d(TAG, "handleMessage:" + msg.arg1);//输出其中arg1的值
                }
            }
        };

        //触发点击事件
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                //有可能做大量耗时操作  子线程
                new Thread(new Runnable() {
                    @Override
                    public void run() {

                        try {
                            //设置延迟时间
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                        //通知UI更新,what值为消息id
//                        handler.sendEmptyMessage(1001); 空消息,只是发了一个地址
                        //获取设置的消息
                        Message message = Message.obtain();
                        message.what = 1002;
                        message.arg1 = 1003;//arg1 和 arg2 是使用的低成本替代方案,如果只需要存储几个整数值。
                        message.arg2 = 1004;//伴随what输入的数,int类型
                        message.obj = MainActivity.this;
                        //发送消息,msg 是消息内容
                        //打包发送给handleMessage(Message msg),由它进行解包出what找到目标
                        handler.sendMessage(message);

                        //定时任务
                        handler.sendMessageAtTime(message,
                                SystemClock.uptimeMillis() + 3000);//当前时间
                        handler.sendMessageDelayed(message, 2000);

                        //handler.post的用法:
                        //可运行的
                        final Runnable runnable = new Runnable() {
                            @Override
                            public void run() {
                                int a = 1 + 2 + 3;
                            }
                        };
                        //发送可运行后运行
                        handler.post(runnable);//直接把这个方法发出去
                        runnable.run();
                        //延迟发送
                        handler.postDelayed(runnable, 2000);
                    }
                }).start();
            }
        });
    }
}
/*
异步下载文件并更新进度条:
* 主线程,在主线程创建Handler -->start* 点击按钮 * 发起下载 * 开启子线程做下载 * 下载完成后通知主线程 -->主线程更新进度条
在manifest设置权限:<uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
*/

public class DownloadActivity extends Activity {

    private Handler mHandler;
    //提取what
    public static final int DOWNLOAD_MESSAGE_CODE = 100001;
    public static final int DOWNLOAD_MESSAGE_FAIL_CODE = 100002;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_download);

        final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);

        findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //点击按钮之后开启一个子线程做下载
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //下载方法自定义,输入参数APPURL
                        download("http://download.sj.qq.com/upload/connAssitantDownload/upload/MobileAssistant_1.apk");
                    }
                }).start();

            }
        });

        //创建Handler
        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case DOWNLOAD_MESSAGE_CODE:
                        if (msg.obj != null)//信息中的obj不为空,则传入进度值
                            progressBar.setProgress((Integer) msg.obj);//将百分号去掉
                        break;
                    case DOWNLOAD_MESSAGE_FAIL_CODE://下载失败
                }
            }
        };
    }

    //下载并更新进度条
    private void download(String appUrl) {
        try {
            URL url = new URL(appUrl);
            //用参数appurl开启URL连接
            URLConnection urlConnection = url.openConnection();
            //获取输入流
            InputStream inputStream = urlConnection.getInputStream();
            //获取文件的总长度,便于下面算出进度
            int contentLength = urlConnection.getContentLength();

            //获取存储地址      Studio提示Environment.getExternalStorageDirectory()过时了
            //放在imooc地址下面                     ,要用Context#getExternalFilesDir代替
            String downloadFolderName = Environment.getExternalStorageDirectory()
                    + File.separator + "imooc" + File.separator;//文件分隔符

            //在存储地址  文件创建  File类
            File file = new File(downloadFolderName);//前缀长度和路径path
            if (!file.exists()) {
                file.mkdir();//如果不存在就创建
            }

            //安装包文件创建
            //文件名字
            String fileName = downloadFolderName + "imooc.apk";
            //给apk文件它的前缀长度和路径path
            File apkFile = new File(fileName);
            if (apkFile.exists()) {
                apkFile.delete();//存在就删了
            }

            //下载进度条
            int downloadSize = 0;
            byte[] bytes = new byte[1024];
            int length = 0;

            //输出流=apk文件输出流
            //实时输出进度值
            OutputStream outputStream = new FileOutputStream(fileName);
            //输入流的比特值长度,存在有效字节则写入输出流
            while ((length = inputStream.read(bytes)) != -1) {
                //写入输出流
                outputStream.write(bytes, 0, length);
                //下载大小为
                downloadSize += length;
                //打包信息发送给Handler ,更新UI线程进度条
                Message message = Message.obtain();
                message.obj = downloadSize * 100 / contentLength;
                message.what = DOWNLOAD_MESSAGE_CODE;
                mHandler.sendMessage(message);
            }
            //关闭输入输出流
            inputStream.close();
            outputStream.close();

        } catch (MalformedURLException e) {
            notifyDownloadFaild();//通知下载失败
            e.printStackTrace();
        } catch (IOException e) {
            notifyDownloadFaild();//通知下载失败
            e.printStackTrace();
        }
    }

    //通知下载失败,依然要把msg传给Handler
    private void notifyDownloadFaild() {
        Message message = Message.obtain();
        message.what = DOWNLOAD_MESSAGE_FAIL_CODE;
        mHandler.sendMessage(message);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">


    <Button
        android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:max="100"
        android:progress="100" />
</LinearLayout>
/*
倒计时:避免handler的内存泄露           创建静态内部类,设置倒计时间隔、最大值、最小值
第一个message  传过去最大值,添加时间间隔      直接运行     接收msg,循环发送消息控制
前面两个都需要点击button跳到子线程去实现,这个不需要子线程。
 */

public class CountDownTimeActivity extends AppCompatActivity {

    public static final int COUNTDOWN_TIME_CODE = 100001;
    // 倒计时间隔
    public static final int DELAY_MILLIS = 1000;
    // 倒计时最大值
    public static final int MAX_COUNT = 10;

    private TextView mCountdownTimeTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //得到控件,在其上面显示变化
        mCountdownTimeTextView = (TextView) findViewById(R.id.countdownTimeTextView);

        //创建了一个handler,继承Handler    实例化
        CountdownTimeHandler handler = new CountdownTimeHandler(this);

        //第一个message  传过去最大值,开始运行。
        Message message = Message.obtain();
        message.what = COUNTDOWN_TIME_CODE;
        message.arg1 = MAX_COUNT;
        handler.sendMessageDelayed(message, DELAY_MILLIS);//添加时间间隔

    }

    //避免handler的内存泄露    创建静态内部类
    public static class CountdownTimeHandler extends Handler {
        static final int MIN_COUNT = 0;//倒计时最小值

        //持有弱引用CountDownTimeActivity,GC回收时会被回收掉
        final WeakReference<CountDownTimeActivity> mWeakReference;

        CountdownTimeHandler(CountDownTimeActivity activity) {
            mWeakReference = new WeakReference<>(activity);
        }

        //接收msg
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            //用本Activity的对象,用activity得到控件去操作
            CountDownTimeActivity activity = mWeakReference.get();

            switch (msg.what) {
                case COUNTDOWN_TIME_CODE:
                    int value = msg.arg1;//此时是最大值
                    //在TextView也就是页面set显示出来
                    activity.mCountdownTimeTextView.setText(String.valueOf(value--));//递减

                    //循环发送消息控制,一次次发送
                    if (value >= MIN_COUNT) {
                        Message message = Message.obtain();
                        message.what = COUNTDOWN_TIME_CODE;
                        message.arg1 = value;
                        sendMessageDelayed(message, DELAY_MILLIS);//添加时间间隔
                    }
                    break;
            }
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值