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

Android之Handler与多线程

2016-07-06 22:53 513 查看
声明: 本人菜鸟一枚, 本博客是本人自学的内容, 适用于初学者, 不喜勿喷, 谢谢大家

Handler介绍

Handler常用API

Handle内部实现原理

Handler内存泄漏问题分析

Handler介绍

对于像我这样的菜鸟来说, 刚开始学Android的时候, 如果想要实现类似下载的功能, 可能会这样写:

public void downloadClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
//模拟下载

try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//下载完成后的操作
textView.setText("下载完成");
}
}).start();
}


这样写带来的后果是: 崩了

错误日志

android.view.ViewRootImpl$CalledFromWrongThreadException:
Only the original thread that created a view


对于熟悉Android开发的人看来是非常可笑的事情, 因为在多线程操作时我们忽略了两点:

不允许阻塞UI线程(主线程)

不能在UI线程之外访问Android UI工具包

以上是Android开发不可逾越的红线, 必须遵守.

但是我们还需要在子线程和主线程之间传递数据, 该怎么办呢? 那就得用我们的Handler

Handler常用API

Handler可以完成下述两点工作:

消息调度和将来的某个时间点执行一个Runnable

多个任务加入到一个队列中

对于刚才的代码我们可以这样改

private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
textView.setText("下载成功");
break;
}

}
};

public void downloadClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
//模拟下载

try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(1);

}
}).start();
}


列举一下常用的API

handler.sendEmptyMessage(1);//发送空消息,
Message msg = handler.obtainMessage();  //从全局的消息池中返回一个Message对象
msg.what = 2;//设置标记
handler.sendMessage(msg);
handler.sendEmptyMessageAtTime(3, System.currentTimeMillis() + 3000);//表示在3s后发送一个空消息
handler.sendEmptyMessageDelayed(4, 3000); //含义同时, 延迟发送消息
//其他的都大同小异, 就不一一列举了


Handle内部实现原理

Handler实现机制:

1. Message对象, 表示要传递的一个消息, 内部使用数据结构实现消息池, 用于重复利用, 避免大量创建消息对象, 造成内存浪费

2. MessageQueue对象, 存放消息对象的消息队列, 先进先出原则

3. Looper对象负责管理当前线程的消息队列(MessageQueue), 用于循环检查消息队列, 从消息队列中一个一个的取出消息对象, 传入handlerMessage() 方法中

4. Handler对象负责把消息push到消息队列中, 以及接收并处理Looper从消息队列中取出的消息

图示说明:



Android启动程序时会在UI线程创建一个MessageQueue

Handler内存泄漏问题分析

如果我们仔细看我们之前写得程序在创建handler处有个黄色的叹号, 将详细信息调出来会是下面的样子



这就引出了我们所说的Handler的内存泄漏问题

到底哪儿出了问题呢??

大家仔细想想我们学习Java的时候, 在讲内部类的时候会讲到, 当我们创建一个内部类对象时, 我们的内部类对象默认会依附于外部类对象的存在而存在. 所以大家试想下面的例子:

handler.postDelayed(new Runnable() {
@Override
public void run() {
System.out.println("Test");
}
}, 1000*60*5);
finish();


在这种情况下,当执行完了postDelayed方法之后当前的Activity会立即finsh(), 但是大家要想到此时我们的handler是依附于外部类的 , 所以此时的Activity并没有真正的关掉

问题的解决

1. 定义一个内部类时会默认拥有外部类对象的引用, 所以最好我们定义一个静态的内部类

2. 使用弱引用, 即使用 引用的强弱分为: 强引用 => 软引用 => 弱引用 ,如果实在不清楚之间的关系请直接百度一下

private MyHandler myHandler= new MyHandler(this);

private static class MyHandler extends Handler{

WeakReference<HandlerMemoryActivity> weakReference;

public MyHandler(HandlerMemoryActivity activity) {
weakReference = new WeakReference<HandlerMemoryActivity>(activity);
}

@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
HandlerMemoryActivity activity = weakReference.get();
if (activity != null){
//这里写对Activity的操作
}
}
}


座右铭: 少说话, 多做事
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息