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

来源:互联网 发布:北京日报新闻网络热线 编辑:程序博客网 时间:2024/06/07 13:15

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();    }}


0 0