您的位置:首页 > 移动开发 > Android开发

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

2016-08-31 14:24 281 查看
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();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐