Handler实践的三种效果及Handler内存泄漏

一、异步下载更新进度条

activity_down_load.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:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="New Button"
        android:id="@+id/button"/>

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

DownloadActivity.java

package com.administrator.handlerproject;
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 java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class DownloadActivity extends Activity {

    private Handler mHandler;
    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_down_load);

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

        /**
         * 主线程 -->start
         * 点击按钮 |
         * 发起下载 |
         * 开启子线程做下载 |
         * 下载完成后通知主线程 | -->主线程更新进度条
         */

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        download("http://download.sj.qq.com/upload/connAssitantDownload/upload/MobileAssistant_1.apk");
                    }
                }).start();

            }
        });

        mHandler =  new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);

                switch (msg.what){
                    case DOWNLOAD_MESSAGE_CODE:
                        progressBar.setProgress((Integer) msg.obj);
                    case DOWNLOAD_MESSAGE_FAIL_CODE:
                }
            }
        };
    }
    private void download(String appUrl){
        try {
            URL url = new URL(appUrl);
            URLConnection urlConnection = url.openConnection();

            InputStream inputStream = urlConnection.getInputStream();

            /**
             * 获取文件的总长度
             */
            int contentLength = urlConnection.getContentLength();

            String downloadFolderName = Environment.getExternalStorageDirectory()
                    + File.separator+"imooc"+File.separator;

            File file = new File(downloadFolderName);
            if (!file.exists()){
                file.mkdir();
            }

            String fileName = downloadFolderName + "imooc.apk";

            File apkFile = new File(fileName);

            if (apkFile.exists()){
                apkFile.delete();
            }

            int downloadSize = 0;
            byte[] bytes = new  byte[1024];

            int length = 0;

            OutputStream outputStream = new FileOutputStream(fileName);
            while ((length = inputStream.read(bytes)) != -1){
                outputStream.write(bytes,0,length);
                downloadSize += length;
                /**
                 * update 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();
        }
    }

    private  void  notifyDownloadFaild(){
        Message message = Message.obtain();
        message.what = DOWNLOAD_MESSAGE_FAIL_CODE;
        mHandler .sendMessage(message);
    }
}

二、倒计时效果(内存泄漏处理)

当Activity被销毁的时候,被延时的消息会在被处理之前存在于主线程的消息队列中,而这个消息中又包含了Handler的引用,而Handler是一个匿名内部类的实例,其持有外面的MainActivity的引用。这些引用会一直保持到该消息被处理,从而阻止了MainActivity被垃圾回收器回收。因此这就导致了MainActivity无法被回收,进而导致MainActivity持有的很多资源都无法回收,这就是我们常说的内存泄漏。

activity_main.xml

<?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="com.administrator.handlerxuexi.MainActivity">

    <TextView
        android:id="@+id/countdownTimeTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@null"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

MainActivity.java

package com.administrator.handlerxuexi;

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

import java.lang.ref.WeakReference;

public class MainActivity extends AppCompatActivity {

    /**
     * 倒计时标记handle code
     */
    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
        CountdownTimeHandler handler = new CountdownTimeHandler(this);

//        Handler handler = new Handler(){}强引用会出现内存泄漏,
        //新建了一个message
        Message message = Message.obtain();
        message.what = COUNTDOWN_TIME_CODE;
        message.arg1 = MAX_COUNT;

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

    }

    /**
     * 创建弱引用的CountdownTimeHandler类继承Handler
     * 
     */
    public  static  class  CountdownTimeHandler extends Handler{
        static  final  int  MIN_COUNT = 0;
        final WeakReference<MainActivity> mWeakReference;//括号内参数是当前Activity对象

        CountdownTimeHandler(MainActivity activity){//括号内参数是当前Activity对象
            mWeakReference = new WeakReference<> (activity );
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //创建一个当前的Activity接收
            MainActivity activity =mWeakReference.get();//获得上面CountdownTimeHandler内定义的mWeakReference对象

            switch (msg.what){
                case COUNTDOWN_TIME_CODE:
                    int value = msg.arg1;
                    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;
            }
        }
    }
}

三、用Handler实现打地鼠游戏

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF">
    
    <ImageView
        android:id="@+id/image_view"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:src="@mipmap/ic_launcher"
        android:visibility="gone"/>

    <Button
        android:id="@+id/start_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:text="点击开始"/>

    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:textAppearance="?android:attr/textAppearanceLarge"/>
    
</RelativeLayout>

MainActivity.java

package com.administrator.diglett;

import android.app.Activity;
import android.content.DialogInterface;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.lang.ref.WeakReference;
import java.util.Random;

public class MainActivity extends AppCompatActivity implements View.OnClickListener,View.OnTouchListener{


    public static final int CODE = 123;
    private TextView mResultTextView;
    private ImageView mDiglettImageView;
    private Button mStarrButton;

    public int[][] mPosition = new  int[][]{
            {342,180},{432,880},
            {521,256},{429,780},
            {456,976},{145,665},
            {123,678},{564,567},
    };

    private  int mTotalCount;
    private  int mSuccessCount;

    public static final int MAX_COUNT = 10;

    private DialettHandler mHandler = new DialettHandler(this);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();

        setTitle("打地鼠");
    }

    private void initView(){
        mResultTextView = (TextView)findViewById(R.id.text_view);
        mDiglettImageView = (ImageView)findViewById(R.id.image_view);
        mStarrButton = (Button)findViewById(R.id.start_button);

        mStarrButton.setOnClickListener(this);
        mDiglettImageView.setOnTouchListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.start_button:
            start();
            break;
        }
    }

    private void start() {
        //发送消息 handler.sendmessagedelayer
        mResultTextView.setText("开始啦");
        mStarrButton.setText("游戏中.....");
        mStarrButton.setEnabled(false);
        next(0);
    }
    private void next(int delayTime){
        //让地鼠每次都会有下一个
        int position = new Random().nextInt(mPosition.length);

        Message message = Message.obtain();

        message.what = CODE;
        message.arg1 = position;

        mHandler.sendMessageDelayed(message,delayTime);
        mTotalCount ++;
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        v.setVisibility(View.GONE);
        mSuccessCount ++;
        mResultTextView.setText("打到了"+mSuccessCount + "只,共" +(MAX_COUNT-1) + "只");
        return false;
    }

    /**
     * 创建弱引用的CountdownTimeHandler类继承Handler来处理Handler的内存泄漏问题
     */
    public static class DialettHandler extends Handler{
        public static final int RANDOM_NUBER = 500;
        public final WeakReference<MainActivity> mWeakReference;//括号内参数是当前Activity对象

        public DialettHandler(MainActivity activity) {//括号内参数是当前Activity对象
            mWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //创建一个当前的Activity接收
            MainActivity activity = mWeakReference.get();//拿到上面的Activity
            switch (msg.what){
                case CODE:
                    if(activity.mTotalCount>MAX_COUNT){
                        activity.clear();
                        Toast.makeText(activity,"地鼠打完了!",Toast.LENGTH_LONG).show();
                        return;
                    }
                    int position =msg.arg1;
                    activity.mDiglettImageView.setX(activity.mPosition[position][0]);
                    activity.mDiglettImageView.setY(activity.mPosition[position][1]);
                    activity.mDiglettImageView.setVisibility(View.VISIBLE);

                    int randomTime = new Random().nextInt(RANDOM_NUBER) + RANDOM_NUBER;
                    activity.next(randomTime);

                    break;
            }
        }
    }

    private void clear() {
        mTotalCount = 0;
        mSuccessCount = 0;
        mDiglettImageView.setVisibility(View.GONE);
        mStarrButton.setText("点击开始");
        mStarrButton.setEnabled(true);
    }
}

猜你喜欢

转载自blog.csdn.net/weimeig/article/details/80252918