1.通过Handelr实现UI线程与子线程的异步加载,首先要在AndroidMainfest.xml中声明访问权限
<uses-permission android:name="android.permission.INTERNET"/>
2.回掉方法,我们通过创建接口来实现:
public interface MyImageCallback {
void onSuccess(Bitmap bitmap);//访问成功,并从网络上获取一张图片
void onFail(Exception e,String msg);//访问失败,抛出异常以及消息
void onProgress(Float percent);//进度条
}
3.由于访问网络是耗时操作,所以我们要开辟子线程来执行耗时操作:
public class ImageRunnable implements Runnable, Handler.Callback {
private String url;
private MyImageCallback callback;
/*
* Message 产品
* MessageQueue 仓库(正常编程的话永远用不到)
* Looper 循环
* Handler 物流
* Handler.Callback 回调接口
* 任何与网络连接都不能在主线程中运行
*
* */
private Handler handler;
public ImageRunnable(String url, MyImageCallback callback) {
this.url = url;
this.callback = callback;
/*查找到的资料:
*Handler一定要在主线程实例化吗?
* new Handler()和new Handler(Looper.getMainLooper())的区别
* 如果你不带参数的实例化:Handler handler = new Handler();
* 那么这个会默认用当前线程的looper
* 一般而言,如果你的Handler是要来刷新操作UI的,
* 那么就需要在主线程下跑。
*情况:
*1.要刷新UI,handler要用到主线程的looper。
* 那么在主线程 Handler handler = new Handler();
* 如果在其他线程,也要满足这个功能的话,
* 要Handler handler = new Handler(Looper.getMainLooper());
*
*2.不用刷新ui,只是处理消息。
* 当前线程如果是主线程的话,Handler handler = new Handler();
* 不是主线程的话,Looper.prepare();
* Handler handler = new Handler();Looper.loop();
* 或者Handler handler = new Handler(Looper.getMainLooper());
*若是实例化的时候用Looper.getMainLooper()就表示放到主UI线程去处理。
* 如果不是的话,因为只有UI线程默认Loop.prepare();Loop.loop();过,
* 其他线程需要手动调用这两个,否则会报错。
*
*
* */
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 contentLength = connection.getContentLength();
int length;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[102400];
while ((length = is.read(buffer)) != -1){
bos.write(buffer,0,length);
handler.obtainMessage(2, bos.size(), contentLength).sendToTarget();
}
byte[] bytes = bos.toByteArray();
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
/*handler数据传递的三种方式
* 方式一:
* Message message = new Message();message创建方式(不推荐)
* Message message = Message.obtain(handler,0,String.valueOf(i + 1));//推荐使用
* message.sendToTarget();
* handler.sendMessage(message);
* 方式二:
* Message message = Message.obtain();
* message.obj = String.valueOf(i + i);
* message.what = 0;
* handler.sendMessage(message);
* 方式三:
*handler.obtainMessage(0,String.valueOf(1 + i)).sendToTarget()
*
* */
handler.obtainMessage(0,bitmap).sendToTarget();
}else{
RuntimeException exception = new RuntimeException("response" + code);
String string = "服务器异常";
Bundle bundle = new Bundle();
bundle.putString("str",string);
bundle.putSerializable("exception",exception);
Message message = handler.obtainMessage(1);
message.setData(bundle);
handler.sendMessage(message);
}
} catch (IOException e) {
String str = "网络异常";
/*
* 当要传递多条数据时,
* 采用bundle打包数据(bundle使用方法类似于map集合),
* 毕竟message.obj只能赋值一种类型的数据,
* 如果是同一个进程,最好用setData,
*message.obj一般是Messenger类来用来跨进程传递可序列化的对象的,
* 这个比setData,更消耗性能一些。
* */
Bundle bundle = new Bundle();
bundle.putString("str",str);
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:
callback.onSuccess((Bitmap) msg.obj);
break;
case 1:
Bundle data = msg.getData();
String string = data.getString("str");
Exception exception = (Exception) data.getSerializable("exception");
callback.onFail(exception,string);
break;
case 2:
int size = msg.arg1;
int length = msg.arg2;
float percent = size * 100.0f / length;
callback.onProgress(percent);
break;
}
return true;//返回值true代表message数据已经传输完成
}
}
3.在UI线程中通过回调接口来获取子线程中存储的数据:
public class MainActivity extends AppCompatActivity implements MyImageCallback{
private ImageView imageView;
private TextView text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.main_image);
text = (TextView) findViewById(R.id.main_tv);
ImageRunnable runnable = new ImageRunnable("http://p4.so.qhmsg.com/t018349127914f495ce.jpg", this);
new Thread(runnable).start();
}
@Override
public void onSuccess(Bitmap bitmap) {
imageView.setImageBitmap(bitmap);
}
@Override
public void onFail(Exception e, String msg) {
e.printStackTrace();
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
}
@Override
public void onProgress(Float precent) {
text.setText(String.format(Locale.CHINA,"%.2f%%",precent));//显示加载文本图片的百分比
}
}