Android Handler 原理分析及实例代码

Android Handler 原理分析

Handler一个让无数android开发者头疼的东西,希望我今天这边文章能为您彻底根治这个问题

今天就为大家详细剖析下Handler的原理

Handler使用的原因

1.多线程更新Ui会导致UI界面错乱
2.如果加锁会导致性能下降
3.只在主线程去更新UI,轮询处理

Handler使用简介

其实关键方法就2个一个sendMessage,用来接收消息

另一个是handleMessage,用来处理接收到的消息

下面是我参考疯狂android讲义,写的一个子线程和主线程之间相互通信的demo

对原demo做了一定修改

public class MainActivity extends AppCompatActivity { public final static String UPPER_NUM="upper_num"; private EditText editText; public jisuanThread jisuan; public Handler mainhandler; private TextView textView; class jisuanThread extends Thread{ public Handler mhandler; @Override public void run() { Looper.prepare(); final ArrayList<Integer> al=new ArrayList<>(); mhandler=new Handler(){ @Override public void handleMessage(Message msg) { if(msg.what==0x123){ Bundle bundle=msg.getData(); int up=bundle.getInt(UPPER_NUM); outer: for(int i=3;i<=up;i++){ for(int j=2;j<=Math.sqrt(i);j++){ if(i%j==0){ continue outer; } } al.add(i); } Message message=new Message(); message.what=0x124; Bundle bundle1=new Bundle(); bundle1.putIntegerArrayList("Result",al); message.setData(bundle1); mainhandler.sendMessage(message); } } }; Looper.loop(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); editText= (EditText) findViewById(R.id.et_num); textView= (TextView) findViewById(R.id.tv_show); jisuan=new jisuanThread(); jisuan.start(); mainhandler=new Handler(){ @Override public void handleMessage(Message msg) { if(msg.what==0x124){ Bundle bundle=new Bundle(); bundle=msg.getData(); ArrayList<Integer> al=bundle.getIntegerArrayList("Result"); textView.setText(al.toString()); } } }; findViewById(R.id.bt_jisuan).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Message message=new Message(); message.what=0x123; Bundle bundle=new Bundle(); bundle.putInt(UPPER_NUM, Integer.parseInt(editText.getText().toString())); message.setData(bundle); jisuan.mhandler.sendMessage(message); } }); } }

Hanler和Looper,MessageQueue原理分析

1.Handler发送消息处理消息(一般都是将消息发送给自己),因为hanler在不同线程是可使用的

2.Looper管理MessageQueue

Looper.loop死循环,不断从MessageQueue取消息,如果有消息就处理消息,没有消息就阻塞

public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } }

这个是Looper.loop的源码,实质就是一个死循环,不断读取自己的MessQueue的消息

3.MessQueue一个消息队列,Handler发送的消息会添加到与自己内联的Looper的MessQueue中,受Looper管理

private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }

这个是Looper构造器,其中做了2个工作,

1.生成与自己关联的Message

2.绑定到当前线程

主线程在初始化的时候已经生成Looper,

其他线程如果想使用handler需要通过Looper.prepare()生成一个自己线程绑定的looper

这就是Looper.prepare()源码,其实质也是使用构造器生成一个looper

private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }

4.handler发送消息会将消息保存在自己相关联的Looper的MessageQueue中,那它是如何找到这个MessageQueue的呢

public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }

这个是Handler的构造方法,它会找到一个自己关联的一个Looper

public static Looper myLooper() { return sThreadLocal.get(); }

没错,他们之间也是通过线程关联的,得到Looper之后自然就可以获得它的MessageQueue了

5.我们再看下handler如发送消息,又是如何在发送完消息后,回调HandlerMessage的

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }

这个就是Handler发送消息的最终源码,可见就是将一个message添加到MessageQueue中,那为什么发送完消息又能及时回调handleMessage方法呢

大家请看上边那个loop方法,其中的for循环里面有一句话msg.target.dispatchMessage(msg);

public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }

这就是这句话,看到了吧里面会调用hanlerMessage,一切都联系起来了吧

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

时间: 2024-09-08 19:49:58

Android Handler 原理分析及实例代码的相关文章

Android 自定义View之倒计时实例代码

Android 自定义View之倒计时实例代码 需求: 大多数app在注册的时候,都有一个获取验证码的按钮,点击后,访问接口,最终用户会收到短信验证码.为了不多次写这个获取验证码的接口,下面将它自定义成一个view,方便使用. 分析一下,这是一个TextView,点击的时候变色,不能再点击,同时里面的倒计时开始显示.那么就有了下面的代码 代码: /** * 通过selector选择器来改变背景,其中倒计时运行时为android:state_enabled="true", * 不显示倒计

Android 底部导航控件实例代码_Android

一.先给大家展示下最终效果 通过以上可以看到,图一是简单的使用,图二.图三中为结合ViewPager共同使用,而且都可以随ViewPager的滑动渐变色,不同点是图二为选中非选中两张图片,图三的选中非选中是一张图片只是做了颜色变化. 二. 需求 我们希望做可以做成这样的,可以在xml布局中引入控件并绑定数据,在代码中设置监听回调,并且配置使用要非常简单! 三.需求分析 根据我们多年做不明确需求项目的经验,以上需求还算明确.那么我们可以采用在LinearLayout添加子View控件,这个子Vie

Android 动态添加Fragment的实例代码_Android

1.fragment1布局及代码 布局 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width=&quo

Android自定义控件下拉刷新实例代码_Android

实现效果: 图片素材: --> 首先, 写先下拉刷新时的刷新布局 pull_to_refresh.xml: <resources> <string name="app_name">PullToRefreshTest</string> <string name="pull_to_refresh">下拉可以刷新</string> <string name="release_to_refre

Android okhttputils现在进度显示实例代码_Android

OkHttpUtils是一款封装了okhttp的网络框架,支持大文件上传下载,上传进度回调,下载进度回调,表单上传(多文件和多参数一起上传),链式调用,整合Gson,自动解析返回对象,支持Https和自签名证书,支持cookie自动管理,扩展了统一的上传管理和下载管理功能. //download the new app private void downLoadNewApp(NewVersion.XianzaishiRfBean version) { if (StringUtils.isEmpt

Android ViewPagerIndicator详解及实例代码

Android ViewPagerIndicator详解及实例代码 关于自定义View的属性零碎知识 自定义View和自定义属性的知识不再此提及,这里着重说的是属性在自定义View中的获取方式,自定义的属性如下: <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="Wisely"> <attr name=&

Android 动态添加Fragment的实例代码

1.fragment1布局及代码 布局 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width=&quo

Android 底部导航控件实例代码

一.先给大家展示下最终效果 通过以上可以看到,图一是简单的使用,图二.图三中为结合ViewPager共同使用,而且都可以随ViewPager的滑动渐变色,不同点是图二为选中非选中两张图片,图三的选中非选中是一张图片只是做了颜色变化. 二. 需求 我们希望做可以做成这样的,可以在xml布局中引入控件并绑定数据,在代码中设置监听回调,并且配置使用要非常简单! 三.需求分析 根据我们多年做不明确需求项目的经验,以上需求还算明确.那么我们可以采用在LinearLayout添加子View控件,这个子Vie

Android自定义手机界面状态栏实例代码

前言 我们知道IOS上的应用,状态栏的颜色总能与应用标题栏颜色保持一致,用户体验很不错,那安卓是否可以呢?若是在安卓4.4之前,答案是否定的,但在4.4之后,谷歌允许开发者自定义状态栏背景颜色啦,这是个不错的体验!若你手机上安装有最新版的qq,并且你的安卓SDK版本是4.4及以上,你可以看下它的效果: 实现这个效果有两个方法: 1.在xml中设置主题或自定义style: Theme.Holo.Light.NoActionBar.TranslucentDecor Theme.Holo.NoActi