Android程序开发之异步加载机制 之 Handler 笔记

转载 2016年08月31日 14:24:10

1.Handler是Android中引入的一种让开发者参与处理线程中消息循环的机制。Handler的内部实现主要涉及到如下几个类: Thread、MessageQueue和Looper。


Thread是最基础的,Looper和MessageQueue都构建在Thread之上,Handler又构建在Looper和MessageQueue之上,我们通过Handler间接地与下面这几个相对底层一点的类打交道。

2.

MessageQueue

最基础最底层的是Thread,每个线程内部都维护了一个消息队列——MessageQueue。消息队列MessageQueue,顾名思义,就是存放消息的队列。假设我们在UI界面上单击了某个按钮,而此时程序又恰好收到了某个广播事件,那我们如何处理这两件事呢? 因为一个线程在某一时刻只能处理一件事情,不能同时处理多件事情,所以我们不能同时处理按钮的单击事件和广播事件,我们只能挨个对其进行处理,只要挨个处理就要有处理的先后顺序。 为此Android把UI界面上单击按钮的事件封装成了一个Message,将其放入到MessageQueue里面去,即将单击按钮事件的Message入栈到消息队列中,然后再将广播事件的封装成以Message,也将其入栈到消息队列中。也就是说一个Message对象表示的是线程需要处理的一件事情,消息队列就是一堆需要处理的Message的池。线程Thread会依次取出消息队列中的消息,依次对其进行处理。MessageQueue中有两个比较重要的方法,一个是enqueueMessage方法,一个是next方法。enqueueMessage方法用于将一个Message放入到消息队列MessageQueue中,next方法是从消息队列MessageQueue中阻塞式地取出一个Message。
3.

Looper

消息队列MessageQueue只是存储Message的地方,真正让消息队列循环起来的是Looper,这就好比消息队列MessageQueue是个水车,那么Looper就是让水车转动起来的河水,如果没有河水,那么水车就是个静止的摆设,没有任何用处,Looper让MessageQueue动了起来,有了活力。

Looper是用来使线程中的消息循环起来的。默认情况下当我们创建一个新的线程的时候,这个线程里面是没有消息队列MessageQueue的。为了能够让线程能够绑定一个消息队列,我们需要借助于Looper:首先我们要调用Looper的prepare方法,然后调用Looper的Loop方法。

需要注意的是Looper.prepare()和Looper.loop()都是在新线程的run方法内调用的,这两个方法都是静态方法。我们知道线程Thread和Looper是一对一绑定的,也就是一个线程中最多只有一个Looper对象

4.

Handler

Handler是暴露给开发者最顶层的一个类,其构建在Thread、Looper与MessageQueue之上。 
Handler具有多个构造函数,签名分别如下所示: 
1. publicHandler() 
2. publicHandler(Callbackcallback) 
3. publicHandler(Looperlooper) 
4. publicHandler(Looperlooper, Callbackcallback) 
第1个和第2个构造函数都没有传递Looper,这两个构造函数都将通过调用Looper.myLooper()获取当前线程绑定的Looper对象,然后将该Looper对象保存到名为mLooper的成员字段中。 
第3个和第4个构造函数传递了Looper对象,这两个构造函数会将该Looper保存到名为mLooper的成员字段中。 
第2个和第4个构造函数还传递了Callback对象,Callback是Handler中的内部接口,需要实现其内部的handleMessage方法,

Handler.Callback是用来处理Message的一种手段,如果没有传递该参数,那么就应该重写Handler的handleMessage方法,也就是说为了使得Handler能够处理Message,我们有两种办法: 
1. 向Hanlder的构造函数传入一个Handler.Callback对象,并实现Handler.Callback的handleMessage方法 
2. 无需向Hanlder的构造函数传入Handler.Callback对象,但是需要重写Handler本身的handleMessage方法 
也就是说无论哪种方式,我们都得通过某种方式实现handleMessage方法,这点与Java中对Thread的设计有异曲同工之处

形象图:【】



代码实现:

1.

package com.treasure_ct.study_demo.asyncloading;

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

import com.treasure_ct.study_demo.R;

/**
 * Created by treasure on 2016.09.01.
 */
public class CeshiHander extends Activity{
    private TextView textView;
    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case 0:
                    textView.setText(((String) msg.obj));
            }
            return false;
        }
    });
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_loopnum);
        textView = (TextView) findViewById(R.id.text_handler);
        textView.setText("0");
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 20; i++) {

                        Message.obtain(handler,0,String.valueOf(i + 1)).sendToTarget();
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
    }
}

2.主要思路:

1)子线程实现定义一个接口,实现success和fail的方法,传递给主类,实现

callBack接口,写handleMessage方法,把传递的Message只进行判断。

2)主类实现MyCall接口获得success和fail方法,调用runnable子线程,具体代码

子线程

package com.treasure_ct.study_demo.asyncloading;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * Created by treasure on 2016.09.01.
 */
public class Ceshi_Runable implements Runnable,Handler.Callback {
    private String url;
    private MyCall myCall;
    private Handler handler;

    public Ceshi_Runable(String url, MyCall myCall) {
        this.url = url;
        this.myCall = myCall;
        handler = new Handler(Looper.getMainLooper(),this);
    }

    @Override
    public void run() {
        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setRequestMethod("GET");
            connection.setDoInput(true);
            int code = connection.getResponseCode();
            if (code == 200) {
                InputStream is = connection.getInputStream();
                int length;
                byte[] bytes = new byte[102400];
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                while ((length = is.read(bytes))!=-1){
                    bos.write(bytes,0,length);
                }
                String s = bos.toString("UTF-8");
                handler.obtainMessage(0,s).sendToTarget();
            }else {
                RuntimeException exception = new RuntimeException("Code:" + code);
                String s = "服务器错误";
                Bundle bundle = new Bundle();
                bundle.putString("msg",s);
                bundle.putSerializable("exception",exception);
                Message message = handler.obtainMessage(1);
                message.setData(bundle);
                handler.sendMessage(message);

            }
        } catch (IOException e) {
            String s = "网络错误";
            Bundle bundle = new Bundle();
            bundle.putString("msg",s);
            bundle.putSerializable("exception",e);
            Message message = handler.obtainMessage(1);
            message.setData(bundle);
            handler.sendMessage(message);
        }
    }

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case 0:
                myCall.onSuccess((String) msg.obj);
                break;
            case 1:
                Bundle bundle = msg.getData();
                Exception exception = (Exception) bundle.getSerializable("exception");
                String msg1 = bundle.getString("msg");
                myCall.onFail(exception,msg1);
                break;
        }
        return false;
    }
    public interface MyCall{
        void onSuccess(String s);
        void onFail(Exception e,String s);
    }
}

主类

package com.treasure_ct.study_demo.asyncloading;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;
import android.widget.Toast;

import com.treasure_ct.study_demo.R;

/**
 * Created by treasure on 2016.09.01.
 */
public class CeshiHander extends Activity implements Ceshi_Runable.MyCall{
    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_loopnum);
        textView = (TextView) findViewById(R.id.text_handler);
        Ceshi_Runable runable = new Ceshi_Runable("http://www.baidu.com", this);
        new Thread(runable).start();
    }

    @Override
    public void onSuccess(String s) {
        textView.setText(s);
    }

    @Override
    public void onFail(Exception e, String s) {
        e.printStackTrace();
        Toast.makeText(CeshiHander.this, s, Toast.LENGTH_SHORT).show();
    }
}


3)图片下载带下载进度

package com.treasure_ct.study_demo.asyncloading;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * Created by treasure on 2016.09.01.
 */
public class Ceshi_Runable implements Runnable,Handler.Callback {
    private String url;
    private MyCall myCall;
    private Handler handler;

    public Ceshi_Runable(String url, MyCall myCall) {
        this.url = url;
        this.myCall = myCall;
        handler = new Handler(Looper.getMainLooper(),this);
    }

    @Override
    public void run() {
        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setRequestMethod("GET");
            connection.setDoInput(true);
            int code = connection.getResponseCode();
            int contentLength = connection.getContentLength();
            if (code == 200) {
                InputStream is = connection.getInputStream();
                int length;
                byte[] bytes = new byte[102400];
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                while ((length = is.read(bytes))!=-1){
                    bos.write(bytes,0,length);
                    handler.obtainMessage(2,bos.size(),contentLength).sendToTarget();
                }
                byte[] bytes1 = bos.toByteArray();
                Bitmap bitmap = BitmapFactory.decodeByteArray(bytes1, 0, bytes1.length);
                handler.obtainMessage(0,bitmap).sendToTarget();
            }else {
                RuntimeException exception = new RuntimeException("Code:" + code);
                String s = "服务器错误";
                Bundle bundle = new Bundle();
                bundle.putString("msg",s);
                bundle.putSerializable("exception",exception);
                Message message = handler.obtainMessage(1);
                message.setData(bundle);
                handler.sendMessage(message);

            }
        } catch (IOException e) {
            String s = "网络错误";
            Bundle bundle = new Bundle();
            bundle.putString("msg",s);
            bundle.putSerializable("exception",e);
            Message message = handler.obtainMessage(1);
            message.setData(bundle);
            handler.sendMessage(message);
        }
    }

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case 0:
                myCall.onSuccess((Bitmap) msg.obj);
                break;
            case 1:
                Bundle bundle = msg.getData();
                Exception exception = (Exception) bundle.getSerializable("exception");
                String msg1 = bundle.getString("msg");
                myCall.onFail(exception,msg1);
                break;
            case 2:
                float percent = msg.arg1 * 100.0f / msg.arg2;
                myCall.onPercent(percent);
                break;
        }
        return false;
    }
    public interface MyCall{
        void onSuccess(Bitmap s);
        void onFail(Exception e,String s);
        void onPercent(float percent);
    }
}


package com.treasure_ct.study_demo.asyncloading;

import android.app.Activity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.treasure_ct.study_demo.R;

import java.util.Locale;

/**
 * Created by treasure on 2016.09.01.
 */
public class CeshiHander extends Activity implements Ceshi_Runable.MyCall{
    private ImageView textView;
    private TextView text1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_loopnum);
        textView = (ImageView) findViewById(R.id.text_handler);
        text1 = (TextView) findViewById(R.id.text_percent);
        Ceshi_Runable runable = new Ceshi_Runable("https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1472712632&di=cb63eb9b4ffd5fe38804e1dd8d9df2ba&src=http://img3.duitang.com/uploads/item/201608/02/20160802212751_CLtXz.thumb.700_0.jpeg", this);
        new Thread(runable).start();
    }

    @Override
    public void onSuccess(Bitmap s) {
        textView.setImageBitmap(s);
    }

    @Override
    public void onFail(Exception e, String s) {
        e.printStackTrace();
        Toast.makeText(CeshiHander.this, s, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onPercent(float percent) {
        text1.setText(String.format(Locale.CHINA,"%.2f%%",percent));
    }
}

图灵机器人 小样

Model

package com.treasure_ct.chatboy_xt;

/**
 * Created by treasure on 2016.09.18.
 */
public class Entry {
    private String icon;
    private String userName;
    private String text;
    private long time;
    private boolean received;

    public String getIcon() {
        return icon;
    }

    public void setIcon(String icon) {
        this.icon = icon;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public long getTime() {
        return time;
    }

    public void setTime(long time) {
        this.time = time;
    }

    public boolean isReceived() {
        return received;
    }

    public void setReceived(boolean received) {
        this.received = received;
    }
}

自定义多布局 baseAdapter

package com.treasure_ct.chatboy_xt;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

/**
 * Created by treasure on 2016.09.18.
 */
public class MyBaseAdapter extends BaseAdapter{
    private Context context;
    private List<Entry> list;

    public MyBaseAdapter(Context context, List<Entry> list) {
        this.context = context;
        this.list = list;
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int position) {
        return list.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public int getViewTypeCount() {
        return 2;
    }

    @Override
    public int getItemViewType(int position) {
        int ret = 0;
        Entry entry = list.get(position);
        if (entry.isReceived()) {
            ret = 0;
        }else {
            ret = 1;
        }
        return ret;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View ret = null;
        int type = getItemViewType(position);
        switch (type) {
            case 0:
                ret = getLeftView(position,convertView,parent);
                break;
            case 1:
                ret = getRightView(position,convertView,parent);
                break;
        }
        return ret;
    }
    private View getLeftView(int position, View convertView, ViewGroup parent){
        View ret = null;
        if (convertView != null){
            ret = convertView;
        }else {
            ret = LayoutInflater.from(context).inflate(R.layout.chat_user_left_item,parent,false);
        }
        ViewHolder holder = (ViewHolder) ret.getTag();
        if (holder == null){
            holder = new ViewHolder(ret);
            ret.setTag(holder);
        }
        holder.bindView(position,list.get(position));
        return ret;
    }
    private View getRightView(int position, View convertView, ViewGroup parent){
        View ret = null;
        if (convertView != null){
            ret = convertView;
        }else {
            ret = LayoutInflater.from(context).inflate(R.layout.chat_user_right_item,parent,false);
        }
        ViewHolder holder = (ViewHolder) ret.getTag();
        if (holder == null){
            holder = new ViewHolder(ret);
            ret.setTag(holder);
        }
        holder.bindView(position,list.get(position));
        return ret;
    }
    private static class ViewHolder{
        private TextView name,text;
        private ImageView icon;
        public ViewHolder(View view) {
             name = (TextView) view.findViewById(R.id.userName);
             text = (TextView) view.findViewById(R.id.userText);
             icon = (ImageView) view.findViewById(R.id.userIcon);
        }
        public void bindView(int position,Entry entry){
            name.setText(entry.getUserName());
            text.setText(entry.getText());
        }
    }
}


实现方法  主类

package com.treasure_ct.chatboy_xt;

import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

import com.treasure_ct.post_network_xt.HttpUtil;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;


/**
 * 题型:图灵机器人
 * 思路:首先使用子线程处理消息,根据点击事件发来的“发送信息”,网络申请加JSON解析,将信息传给接收消息的Handler,进行显示
 * 用到两个Handler,第一个sonHandler 用来进行用户发送到子线程的任务,第二个mainHandler用来子线程处理好信息,
 * 接受网络信息,然后发回主线程,进行显示
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener,Runnable {
    private List<Entry> list;
    private MyBaseAdapter adapter;
    private EditText input;
    private ListView listView;
    private Handler sonHandler;
    private Handler mainHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 888:
                    int type = msg.arg1;
                    if (type == 100000) {
                        String text = (String) msg.obj;
                        Entry entry = new Entry();
                        entry.setText(text);
                        entry.setUserName("小T");
                        entry.setReceived(true);
                        entry.setTime(System.currentTimeMillis());
                        list.add(entry);
                        adapter.notifyDataSetChanged();
                        int count = adapter.getCount();
                        listView.setSelection(count - 1);
                    }
                    break;
            }
        }
    };
    @Override
    protected void onCreate( Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button send = (Button) findViewById(R.id.btn_send);
         input = (EditText) findViewById(R.id.edit);
         listView = (ListView) findViewById(R.id.listview);
         list = new ArrayList<>();
         adapter = new MyBaseAdapter(this,list);
        if (listView != null) {
            listView.setAdapter(adapter);
        }
        Thread thread = new Thread(this);
        thread.start();
        send.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_send:
                if (sonHandler != null) {
                    Message message = sonHandler.obtainMessage(666);
                    String s = input.getText().toString();
                    if (s != null && !s.equals("")) {
                        message.obj = s;
                        sonHandler.sendMessage(message);

                        Entry entry = new Entry();
                        entry.setReceived(false);
                        entry.setText(s);
                        entry.setTime(System.currentTimeMillis());
                        entry.setUserName("我");
                        list.add(entry);
                        adapter.notifyDataSetChanged();
                        int count = adapter.getCount();
                        listView.setSelection(count - 1);
                        input.setText("");
                    }else {
                        Toast.makeText(MainActivity.this, "请输入信息", Toast.LENGTH_SHORT).show();
                    }
                }
                break;
        }
    }

    @Override
    public void run() {
        Looper.prepare();
        Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what){
                    case 666:
                        JSONObject object = new JSONObject();
                        try {
                            object.put("key","f88cd2cdc8ea4953b51154567ca356d7");
                            object.put("info",msg.obj.toString());
                            object.put("loc","北京市海淀区");
                            object.put("userid",Build.SERIAL);
                            byte[] data = HttpUtil.doPostJson("http://www.tuling123.com/openapi/api", object.toString());
                            if (data != null) {
                                String s = new String(data, "UTF-8");
                                Log.d("-------------------", "handleMessage: "+s);
                                JSONObject jsonObject = new JSONObject(s);
                                int code = jsonObject.getInt("code");
                                if (code == 100000) {
                                    String text = jsonObject.getString("text");
                                    Message message = mainHandler.obtainMessage(888);
                                    message.obj = text;
                                    message.arg1= code;
                                    mainHandler.sendMessage(message);
                                }
                            }
                        } catch (JSONException e) {
                            e.printStackTrace();
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                }
            }
        };
        sonHandler = handler;
        Looper.loop();
    }
}


Handler消息机制和异步加载

  • 2013年04月22日 07:32
  • 836KB
  • 下载

Android消息处理机制:源码剖析Handler、Looper,并实现图片异步加载

引言我们在做 Android 开发时,常常需要实现异步加载图片/网页/其他。事实上,要实现异步加载,就需要实现线程间通信,而在 Android 中结合使用 Handler、Looper、Message...

Handler 机制浅析及Message的异步加载

最近上班没事做,就阅读了android的部分源码,打发时间也算自我学习提高自己。原本对android Handler机制较为熟悉,今天阅读源码验证了自己一些想法并将成果记录下来,以便日后复习,或也可...

学习笔记:android中Handler异步消息处理机制

在Android中,线程、Looper和MessageQueue的数量关系为1:1:1,是通过以下机制来实现这种数量比例的 app启动后,主线程会调用Looper.prepare()方法 priv...

android异步图片加载三之handler+线程池+消息队列模式+缓存

假象现在有这样一个需求:       一个微博客户端或者多媒体客户端需要从服务端拉取一些数据,该数据中包含了很多图片,如果不用异步加载方式,可能会使主线程阻塞导致ANR异常,但是异步加载后如果每...

Android用Handler+Timer 异步加载

Android AsyncTask用法   Android Service+Socket 联网交互 Android用Handler+Timer 异步加载   2013-...

Android异步加载数据更新UI(Thread+Handler)

目录:     1.异步加载应用场景+概述     2.Thread+Handler和AsyncTask实现异步对比     3.Thread+Handler异步加载图片(Demo) 1....
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android程序开发之异步加载机制 之 Handler 笔记
举报原因:
原因补充:

(最多只允许输入30个字)