handler使用步骤 1.在主线程定义了一个Handler private Handler handler = new Handler() 2.使用handler会重写handler里面的handlerMessage方法 public void handlerMessage(Message msg){} 3.拿着我们在主线程创建的handler去子线程发消息 handler.sendMessage(msg); 4.handlerMessage方法就会执行,在这个方法里面去更新ui
源码解读 Android 消息机制(Message MessageQueue Handler Looper)看这里:
https://blog.csdn.net/u011240877/article/details/72892321
网页源码查看器:
以下是使用handler实现,AsyncTask实现的请见这里:https://github.com/liuchenyang0515/webcv_AsyncTask
MainActivity.java
代码语言:javascript复制import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private EditText et_path;
private TextView tv_result;
private Handler handler = new Handler() {
// 这个方法是在主线程里面执行的
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 所以就可以在主线程里面更新ui了
// 1.区分一下发送的是哪条消息
switch (msg.what) {
case REQUESTSUCESS:
String content = (String) msg.obj;
tv_result.setText(content);
break;
case REQUESTNOTFOUND:
Toast.makeText(MainActivity.this, "请求资源不存在", Toast.LENGTH_SHORT).show();
break;
case REQUESTEXCEPTION:
Toast.makeText(MainActivity.this, "服务器忙,请稍候访问", Toast.LENGTH_SHORT).show();
break;
}
}
};
private static final int REQUESTSUCESS = 0; // ctrl shift U切换大小写
private static final int REQUESTNOTFOUND = 1; // 请求失败
private static final int REQUESTEXCEPTION = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_path = (EditText) findViewById(R.id.et_path);
tv_result = (TextView) findViewById(R.id.tv_result);
}
// 不能在主线程进行耗时的操作,比如连接网络,拷贝大数据,睡眠等操作。
// 连接谷歌网络。warning:java.net.SocketTimeoutException: connect timed out
// 只要主线程超时 info:The application may be doing too much work on its main thread.
// 在4.0之后谷歌强制要求连接网络不能在主线程进行访问
// 只有主线程(UI线程)才可以更新UI
// 2.点击按钮进行查看,指定路径的源码
public void click(View v) {
// 只要不new Thread,全都属于主线程
new Thread() {
public void run() {
try {
// 2.1获取源码路径
String path = et_path.getText().toString().trim();
// 2.2创建URL对象,指定我们要访问的网址(路径)
URL url = new URL(path);
// 2.3拿到httpurlconnection对象,用于发送或者接收数据
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 2.4设置发送get请求
conn.setRequestMethod("GET"); // get要求大写,默认是get请求
// 2.5设置请求超时时间
conn.setConnectTimeout(5000);
// 2.6获取服务器返回的状态码
int code = 0;
code = conn.getResponseCode();
// 2.7如果code==200说明请求成功
Log.d(TAG, code "====================================");
if (code == 200) {
// 2.8获取服务器返回的数据,是以流的形式返回的,由于把流转换成字符串是很常见的操作
// 所以抽出一个工具类Utils
InputStream in = conn.getInputStream();
// 2.9使用定义的工具类把in转换城String
final String content = StreamTools.readStream(in);
// 2.9.0创建message对象
/*runOnUiThread(new Runnable() {
@Override
public void run() {
tv_result.setText(content);
}
});*/
Message msg = Message.obtain();// 使用msg的静态方法可以减少对象的创建
msg.what = REQUESTSUCESS;
msg.obj = content;
// 2.9.1拿着我们创建的handler(助手) 告诉系统,我要更新ui
handler.sendMessage(msg);
// 发了一条消息,消息(msg)里把数据放到了msg里
// 接着handleMessage方法就会执行
/*// 更新UI的逻辑,写在这里会报错
tv_result.setText(content);*/
} else {
// 请求资源不存在 Toast就是一个view,相当于更新UI,不能在子线程更新UI,不能在子线程直接使用Toast
/*runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "请求资源不存在", Toast.LENGTH_SHORT).show();
}
});*/
Message msg = Message.obtain();
msg.what = REQUESTNOTFOUND; // 代表哪条消息
handler.sendMessage(msg);
}
} catch (Exception e) {
e.printStackTrace();
/*runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "服务器忙,请稍候访问", Toast.LENGTH_SHORT).show();
}
});*/
Message msg = Message.obtain();
msg.what = REQUESTEXCEPTION; // 代表哪条消息
handler.sendMessage(msg);
}
}
}.start();
}
}
运行效果:
这里点击“查看”时为什么要选择另外开启一个线程?因为getResponseCode()会报错
handler的作用是用来发消息和处理消息的 Looper的作用是去消息队列里面取消息 Looper是在主线程一创建Looper就有了
还有一点:
A toast is a view containing a quick little message for the user.
Toast就是一个view,相当于更新UI,不能在子线程更新UI,不能在子线程直接使用Toast
不管什么版本的手机,只要做耗时的操作(比如连接网络、拷贝大的数据等)就自己开一个子线程,获取数据后想要更新UI,就使用Handler就行。
如果仅仅只是更新UI,那么用runOnUiThread就可以了。这是一个在android.app包下的Activity类里面的方法
public final void runOnUiThread (Runnable action)
在UI线程上运行指定的操作。如果当前线程是UI线程,则立即执行该操作。如果当前线程不是UI线程,则将操作发布到UI线程的事件队列。
那Handler没用了吗?不是,有时候是可以通过Handler发送消息,携带数据这个时候就必须使用Handler了。
用runOnUiThread操作如下,可以达到一样的效果。
代码语言:javascript复制public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private EditText et_path;
private TextView tv_result;
private static final int REQUESTSUCESS = 0; // ctrl shift U切换大小写
private static final int REQUESTNOTFOUND = 1; // 请求失败
private static final int REQUESTEXCEPTION = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_path = (EditText) findViewById(R.id.et_path);
tv_result = (TextView) findViewById(R.id.tv_result);
}
public void click(View v) {
// 只要不new Thread,全都属于主线程
new Thread() {
public void run() {
try {
// 2.1获取源码路径
String path = et_path.getText().toString().trim();
// 2.2创建URL对象,指定我们要访问的网址(路径)
URL url = new URL(path);
// 2.3拿到httpurlconnection对象,用于发送或者接收数据
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 2.4设置发送get请求
conn.setRequestMethod("GET"); // get要求大写,默认是get请求
// 2.5设置请求超时时间
conn.setConnectTimeout(5000);
// 2.6获取服务器返回的状态码
int code = 0;
code = conn.getResponseCode();
// 2.7如果code==200说明请求成功
Log.d(TAG, code "====================================");
if (code == 200) {
// 2.8获取服务器返回的数据,是以流的形式返回的,由于把流转换成字符串是很常见的操作
// 所以抽出一个工具类Utils
InputStream in = conn.getInputStream();
// 2.9使用定义的工具类把in转换城String
final String content = StreamTools.readStream(in);
// 2.9.0创建message对象
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_result.setText(content);
}
});
} else {
// 请求资源不存在 Toast就是一个view,相当于更新UI,不能在子线程更新UI,不能在子线程直接使用Toast
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "请求资源不存在", Toast.LENGTH_SHORT).show();
}
});
}
} catch (Exception e) {
e.printStackTrace();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "服务器忙,请稍候访问", Toast.LENGTH_SHORT).show();
}
});
}
}
}.start();
}
}
图片查看器:
代码语言:javascript复制import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Base64;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private EditText et_path;
private ImageView iv;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Bitmap bitmap = (Bitmap) msg.obj;
iv.setImageBitmap(bitmap);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 1.找到我们关心的控件
et_path = (EditText) findViewById(R.id.et_path);
iv = (ImageView) findViewById(R.id.iv);
}
// 2.点击按钮进行查看指定路径的图片
public void click(View view) {
new Thread() {
@Override
public void run() {
try {
// 2.1获取图片路径
String path = et_path.getText().toString().trim();
// path不变,Base64加密写出的file路径,也就是改一下文件名字
File file = new File(getCacheDir(), Base64.encodeToString(path.getBytes(), Base64.DEFAULT));
Log.d(TAG, file.toString());// 可以看到全路径名,写出的file名已经加密
if (file.exists() && file.length() > 0) {
// 使用缓存的图片
Log.d(TAG, "使用缓存图片");
Bitmap cacheBitmap = BitmapFactory.decodeFile(file.getCanonicalPath());
// 把cacheBitmap显示到iv上
Message msg = Message.obtain();
msg.obj = cacheBitmap;
handler.sendMessage(msg);
} else {
Log.d(TAG, "第一次连接网络");
// 2.2创建URL对象
URL url = new URL(path);
// 2.3获取httpurlconnection
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 2.4设置请求的方式
conn.setRequestMethod("GET");
// 2.5设置超时时间
conn.setConnectTimeout(5000);
// 2.6获取服务器返回的状态码
int code = conn.getResponseCode();
if (code == 200) {
// 2.7获取图片的数据,不管是什么数据(txt文本 图片数据)都是以流的形式返回
InputStream in = conn.getInputStream();
// 2.7.1缓存图片,谷歌提供了缓存目录
BufferedInputStream bis = new BufferedInputStream(in);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
int len = -1;
byte[] buf = new byte[8192];
while ((len = bis.read(buf, 0, 8192)) != -1) {
bos.write(buf, 0, len);
}
bis.close();
bos.close();
// 2.8通过位图工厂获取bitmap
//Bitmap bitmap = BitmapFactory.decodeStream(in);
Bitmap bitmap = BitmapFactory.decodeFile(file.getCanonicalPath());
// 2.9把bitmap显示到iv上
// 使用msg的静态方法可以减少对象的创建
Message msg = Message.obtain();
msg.obj = bitmap;
handler.sendMessage(msg);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
}
activity_main.xml
代码语言:javascript复制<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@ id/et_path"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="click"
android:text="查看" />
<ImageView
android:id="@ id/iv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
运行结果:
关于cache和files目录的区别可以看这里:
https://blog.csdn.net/hxqneuq2012/article/details/53128572
用runOnUiThread操作如下,可以达到一样的效果。
代码语言:javascript复制public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private EditText et_path;
private ImageView iv;
/* private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Bitmap bitmap = (Bitmap) msg.obj;
iv.setImageBitmap(bitmap);
}
};*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 1.找到我们关心的控件
et_path = (EditText) findViewById(R.id.et_path);
iv = (ImageView) findViewById(R.id.iv);
}
// 2.点击按钮进行查看指定路径的图片
public void click(View view) {
new Thread() {
@Override
public void run() {
try {
// 2.1获取图片路径
String path = et_path.getText().toString().trim();
// path不变,Base64加密写出的file路径,也就是改一下文件名字
File file = new File(getCacheDir(), Base64.encodeToString(path.getBytes(), Base64.DEFAULT));
Log.d(TAG, file.toString());// 可以看到全路径名,写出的file名已经加密
if (file.exists() && file.length() > 0) {
// 使用缓存的图片
Log.d(TAG, "使用缓存图片");
final Bitmap cacheBitmap = BitmapFactory.decodeFile(file.getCanonicalPath());
// 把cacheBitmap显示到iv上
runOnUiThread(new Runnable() {
@Override
public void run() {
iv.setImageBitmap(cacheBitmap);
}
});
/*Message msg = Message.obtain();
msg.obj = cacheBitmap;
handler.sendMessage(msg);*/
} else {
Log.d(TAG, "第一次连接网络");
// 2.2创建URL对象
URL url = new URL(path);
// 2.3获取httpurlconnection
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 2.4设置请求的方式
conn.setRequestMethod("GET");
// 2.5设置超时时间
conn.setConnectTimeout(5000);
// 2.6获取服务器返回的状态码
int code = conn.getResponseCode();
if (code == 200) {
// 2.7获取图片的数据,不管是什么数据(txt文本 图片数据)都是以流的形式返回
InputStream in = conn.getInputStream();
// 2.7.1缓存图片,谷歌提供了缓存目录
BufferedInputStream bis = new BufferedInputStream(in);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
int len = -1;
byte[] buf = new byte[8192];
while ((len = bis.read(buf, 0, 8192)) != -1) {
bos.write(buf, 0, len);
}
bis.close();
bos.close();
// 2.8通过位图工厂获取bitmap
//Bitmap bitmap = BitmapFactory.decodeStream(in);
final Bitmap bitmap = BitmapFactory.decodeFile(file.getCanonicalPath());
// 2.9把bitmap显示到iv上
runOnUiThread(new Runnable() {
@Override
public void run() {
iv.setImageBitmap(bitmap);
}
});
/*// 使用msg的静态方法可以减少对象的创建
Message msg = Message.obtain();
msg.obj = bitmap;
handler.sendMessage(msg);*/
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
}