Handler入门

1.Handler

因为安卓开发中,除了主线程,其他线程不允许修改视图,所以需要Handler去做消息传递

常见方法

//发送延时消息
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//发送定时消息
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}
  //发送普通消息,这里可以看出,他使用了延时消息,只是事件为0
public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }

MainActivity

package com.example.handlertest;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private String TAG="Ms";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        /*
        UI线程,做轻量级的事情
         */
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textview = findViewById(R.id.textView);
        //创建handler
        Handler handler=new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                //处理消息
                /*
                子线程,做大量耗时操作,操作完成后,通知UI更新
                 */
                Log.e(TAG, "handleMessage: "+msg.what );
                if(msg.what == 1001){
                    textview.setText("好好学习 天天向上");
                }
            }
        };


        Button button = findViewById(R.id.btn);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //做大量耗时操作
                new Thread(()->{
                    handler.sendEmptyMessage(1001);
                }).start();
            }
        });
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <TextView
        android:id="@+id/textView"
        android:text="Hello World!"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/btn"
        android:text="New Button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

UI线程做轻量级操作,子线程处理耗时操作,发送标识

1.异步下载文件更新进度条

DownloadActivity

package com.example.handlertest;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.ProgressBar;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class DownloadActivity extends Activity {
    private Handler mHandler;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dowload);
        /*
        主线程点击 start
        按键发起下载
        开启子线程做瞎下载
         下载过程中通知主线程
         主线程更新进度
         */
        ProgressBar progressbar = findViewById(R.id.progressBar);
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(()->{
                    download("https://img.ixintu.com/upload/jpg/20210522/949cf3d6c57299d6b7683eb5868e8ffd_24455_644_541.jpg!con");
                }).start();
            }
        });
        mHandler=new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                switch (msg.what){
                    case 100001:
                        progressbar.setProgress((Integer) msg.obj);
                        break;

                }
            }
        };
    }

    private void download(String appurl) {
        try {
            URL url=new URL(appurl);
            URLConnection urlConnection = url.openConnection();
            InputStream inputStream = urlConnection.getInputStream();
            //获取文件总长度
            int contentLength=urlConnection.getContentLength();
            String path = "data"+ File.separator+"data"+File.separator+"com.example.handlertest" +File.separator
                    + "aaa" + File.separator;
            File file=new File(path);
            if(!file.exists()){
                file.mkdir();
            }
            String filename=path+"as.jpg";
            File file1 = new File(filename);
            if(file1.exists()){
                file1.delete();
            }
            int downloadSize=0;
            byte[] bytes=new byte[1024];
            int length=0;
            FileOutputStream fileOutputStream = new FileOutputStream(filename);
            while ((length=inputStream.read(bytes))!=-1){
                fileOutputStream.write(bytes,0,length);
                downloadSize+=length;
                Message message=new Message();
                message.obj=downloadSize*100/contentLength;
                message.what=100001;
                mHandler.sendMessage(message);
            }
            inputStream.close();
            fileOutputStream.close();
        } catch (MalformedURLException e) {
            Message message=new Message();
            message.what=100002;
            mHandler.sendMessage(message);
            e.printStackTrace();
        } catch (IOException e) {
            Message message=new Message();
            message.what=100002;
            mHandler.sendMessage(message);
            e.printStackTrace();
        }
    }
}

File.separator就是/ 由于我用的虚拟机所以安装的软件再data/data/下使用File.separator拼接

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.handlertest">
    <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"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.HandlerTest"
        tools:targetApi="31">
        <activity
            android:name=".DownloadActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

添加权限后,最好把这个软件卸载一下再运行,如虚拟机里,长按这个项目卸载

activity_dowload.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

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

    <ProgressBar
        android:max="100"
        android:progress="100"
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

Handler可能会导致内存泄露,因为他需要传递上下文,因为是异步的,他的上下文可能会摧毁,但是GC不会回收掉,Handler中一直持有上下文引用,所以会导致内存泄漏

2.倒计时的实现

package com.example.countdowntime;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.TextView;

import java.util.concurrent.TimeUnit;

public class MainActivity extends AppCompatActivity {

    public static final int COUNTDOWN_TIME_CODE = 100001;
    public static final int DELAY_MILLIS = 1000;
    int i=10;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //得到控件
        TextView contdownTimeTextView = findViewById(R.id.cTextView);
        Handler handler=new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                if(msg!=null && msg.what==COUNTDOWN_TIME_CODE){
                    i-=1;
                    contdownTimeTextView.setText(String.valueOf(i));
                }
            }
        };

        new Thread(()->{
            for (int j = 0; j < 10; j++) {
                try {
                    Message message=Message.obtain();
                    message.what= COUNTDOWN_TIME_CODE;
                    handler.sendMessageDelayed(message, DELAY_MILLIS);
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }
    
}

布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="16dp"
    android:paddingLeft="16dp"
    android:paddingTop="16dp"
    android:paddingRight="16dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <TextView
        android:id="@+id/cTextView"
        android:textSize="25sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/maxTime"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</RelativeLayout>

对handler进行优化

package com.example.countdowntime;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;

import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;

public class MainActivity 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 contdownTimeTextView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //得到控件
        contdownTimeTextView = findViewById(R.id.cTextView);

        //创建了一个handler 静态的
        CountdownTimeHandler handler=new CountdownTimeHandler(MainActivity.this);

        //新建了一个message
        Message message=Message.obtain();
        message.what= COUNTDOWN_TIME_CODE;
        message.arg1= MAX_COUNT;

        //第一次发送这个message
        handler.sendMessageDelayed(message, DELAY_MILLIS);

    }
    public static class CountdownTimeHandler extends Handler{

        final WeakReference<MainActivity> mWeakReference;


        public CountdownTimeHandler(MainActivity activity) {
            this.mWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            //获取activity的弱引用
            MainActivity activity = mWeakReference.get();
            switch (msg.what){
                case COUNTDOWN_TIME_CODE:
                    int values = msg.arg1;
                    activity.contdownTimeTextView.setText(String.valueOf(values--));

                    //循环发消息控制
                    if(values >= 0){
                    Message msg1=Message.obtain();
                    msg1.what=COUNTDOWN_TIME_CODE;
                    msg1.arg1=values;
                        sendMessageDelayed(msg1,DELAY_MILLIS);
                    }
                    break;
            }
        }
    }
}

使用弱引用对其优化,创建静态handler用完即回收

这里的handleMessage中Message需要重新构建,不能直接拿之前的msg否则会报错,再重新构建从消息池里拿消息,依旧是原来的消息

2.Message

常见方法

public static Message obtain(Message orig) {
    Message m = obtain(); //使用消息池
    m.what = orig.what; //message的标识 发送消息后 后续判断标识 再进行业务
    m.arg1 = orig.arg1;	//message携带的参数
    m.arg2 = orig.arg2;
    m.obj = orig.obj; //上下文
    m.replyTo = orig.replyTo;	//实现异步通信
    m.sendingUid = orig.sendingUid;
    m.workSourceUid = orig.workSourceUid;
    if (orig.data != null) {
        m.data = new Bundle(orig.data);
    }
    m.target = orig.target;
    m.callback = orig.callback;

    return m;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值