IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android Handler的使用 -> 正文阅读

[移动开发]Android Handler的使用

参考文章:
Android Handler用法解析
Android:为什么子线程不能更新UI

因为 Android 的UI更新是在主线程中做的,如果在主线程做一些耗时操作,比如进行网络请求,弱网环境下就极为容易阻塞住UI更新,画面看起来就像是被卡住了。
从Android4.0开始,Android不允许再主线程中进行网络请求,不允许在子线程中进行UI更新。
既然不允许在主线程进行网络请求,为什么还不允许在子线程更新UI呢?
假如允许多线程更新UI,但是访问UI是没有加锁的,一旦多线程抢占了资源,那么界面将会乱套更新了,体验效果就不言而喻了。而且,加了锁会让性能降低,所以在Android中规定必须在主线程更新UI。
那是不是在子线程更新UI就一定不行呢?
答案是:在onCreate方法中,如果你调用子线程不做太多耗时操作的话,是可以在onCreate方法中调用子线程去更新UI的。
下面的代码,在子线程更新UI就不会报错

public class MainActivity extends AppCompatActivity {
    private TextView mTextView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = findViewById(R.id.text);
        new Thread(new Runnable() {
            @Override
            public void run() {
                mTextView.setText("测试是否报出异常");
            }
        }).start();
    }
}

原因是ViewRootIml的checkThread方法会检查当前线程是否是主线程,不是的话会报错,而ViewRootImpl对象是在onResume方法回调之后才创建,所以上面的代码并不会报错。

@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}

当我们在子线程做了一些耗时操作后,要更新UI时,我们需要通知给主线程,这个通知使用的工具就是Handler。
我们需要在主线程中定义Handler
如果写成下面这种写法,就会容易产生内存泄漏


public class MainActivity extends AppCompatActivity {
    private ImagerView iv;
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case value:
                iv.setImageResoure(...)
                break;
            }
        }
    };

原因是,当Activity finish后,延时消息会继续存在主线程消息队列中1分钟,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致 Activity泄露。
因此我们需要写成下面这种形式

private static class MyHandler extends Handler {
     private final WeakReference<MainActivity> mTarget;

     public MyHandler(MainActivity activity) {
         mTarget = new WeakReference<MainActivity>(activity);
     }

     @Override
     public void handleMessage(@NonNull Message msg) {
         super.handleMessage(msg);
         if (msg.what == 0) {
             Log.e("myhandler", "change textview");
             // 当没有任何强引用到widget对象时使用get时突然返回null
             MainActivity ma = mTarget.get();
             ma.textView.setText("hahah");
         }
     }
 }

我们需要将 MyHandler 定义成 static ,因为静态内部类不会引用外部类对象。而且,当我们要使用外部类对象的时候需要将其定义成 WeakReference ,这样一旦外部类销毁的时候使用weakWidget.get()就可以得到真实的Widget对象,因为弱引用不能阻挡垃圾回收器对其回收,你会发现当没有任何强引用到MainActivity对象时使用get时返回null。
然后在主线程创建一个类型为MyHandler的私有属性:

private Handler myHandler= new MyHandler(this);

创建一个线程,在子线程中向主线程发送消息

new Thread(new Runnable() {
    @Override
    public void run() {
        myHandler.sendEmptyMessage(0);
    }
}).start();

还有一种情况是:如果主线程想通知子线程一些消息,那么就需要使用下面的代码:
先在主线程声明Handler

private Handler threadHandler;

然后创建一个线程

HandlerThread handlerThread = new HandlerThread("test Handler");
handlerThread.start();

然后,真正创建在主线程声明的 Handler 对象

threadHandler = new Handler(handlerThread.getLooper()) {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        if (msg.what==2) {
            Toast.makeText(MainActivity.this, "主线程:", Toast.LENGTH_SHORT).show();
        }
    }
};

最后,我们需要在主线程调用

threadHandler.sendEmptyMessage(2);

这样 Handler 中的 handleMessage 方法处理消息。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-02-04 11:09:00  更:2022-02-04 11:11:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 13:24:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码