皮皮网

皮皮网

【雇员管理系统 源码】【apache solr源码调试】【工业溯源系统源码】looper 源码

时间:2024-11-23 10:37:02 分类:焦点

1.android中looper的实现原理,为什么调用looper.prepare就在当前线程关联了一个lo
2.android里面所说的源码looper是什么意思啊?
3.Handler 执行流程及源码解析
4.找到卡顿来源,BlockCanary源码精简分析

looper 源码

android中looper的实现原理,为什么调用looper.prepare就在当前线程关联了一个lo

       å®žé™…上:消息发送和计划任务提交之后,它们都会进入某线程的消息队列中,我们可以把这个线程称之为目标线程。不论是主线程还是子线程都可以成为目标线程。上例中之所以在主线程中处理消息,是因为我们要更新UI,按照android中的规定我们必须由主线程更新UI。所以我们让主线程成为了目标线程。

       é‚£ä¹ˆå¦‚何控制让某个线程成为目标线程呢?

       è¿™å°±å¼•å‡ºäº†Looper的概念。Android系统中实现了消息循环机制,Android的消息循环是针对线程的,每个线程都可以有自己的消息队列和消息循环。Android系统中的通过Looper帮助线程维护着一个消息队列和消息循环。通过Looper.myLooper()得到当前线程的Looper对象,通过Looper.getMainLooper()得到当前进程的主线程的Looper对象。

       å‰é¢æåˆ°æ¯ä¸ªçº¿ç¨‹éƒ½å¯ä»¥æœ‰è‡ªå·±çš„消息队列和消息循环,然而我们自己创建的线程默认是没有消息队列和消息循环的(及Looper),要想让一个线程具有消息处理机制我们应该在线程中先调用Looper.prepare()来创建一个Looper对象,然后调用Looper.loop()进入消息循环。如上面的源码所示。

       å½“我们用Handler的构造方法创建Handler对象时,指定handler对象与哪个具有消息处理机制的线程(具有Looper的线程)相关联,这个线程就成了目标线程,可以接受消息和计划任务了。Handler中的构造方法如下:

       [java] view

       plaincopyprint?源码

       public Handler() {

        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 = null;

        }

       public Handler(Looper looper) {

        mLooper = looper;

        mQueue = looper.mQueue;

        mCallback = null;

        }

       public Handler() {

        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 = null;

        }

        public Handler(Looper looper) {

        mLooper = looper;

        mQueue = looper.mQueue;

        mCallback = null;

        }

       åœ¨ä¸Šè¿°çš„计时器的例子中,之所以可以在主线程中处理消息而我们自己并没有调用Looper.prepare()等方法,是因为Android系统在Activity启动时为其创建一个消息队列和消息循环,当我们用无参的Handler构造方法创建对象时又用了当前线程的Looper对象,及将handler与主线程中的Looper对象进行了关联。

       android中是使用Looper机制来完成消息循环的,但每次创建线程时都先初始化Looper比较麻烦,因此Android为我们提供了一个HandlerThread类,他封装了Looper对象,是我们不用关心Looper的开启和释放问题。

       ä¸ç®¡æ˜¯ä¸»çº¿ç¨‹è¿˜æ˜¯å…¶ä»–线程只要有Looper的线程,别的线程就可以向这个线程的消息队列中发送消息和任务。

       æˆ‘们使用HandlerThread类代替上一篇文章中的子线程,并用HandlerThread类中的Looper对象构造Handler,则接受消息的目标线程就不是主线程了,而是HandlerThread线程。代码如下:

       [java] view

       plaincopyprint?

       public class clockActivity extends Activity {

        /** Called when the activity is first created. */

        private String TAG="clockActivity";

        private Button endButton;

        private TextView textView;

        private int timer=0;

        private boolean isRunning=true;

        private Handler handler;

        @Override

        public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        endButton=(Button)findViewById(R.id.endBtn);

        textView=(TextView)findViewById(R.id.textview);

        endButton.setOnClickListener(new View.OnClickListener() {

        @Override

        public void onClick(View v) {

        // TODO Auto-generated method stub

       isRunning=false;

        }

        });

        HandlerThread thread=new HandlerThread("myThread");

        handler=new Handler(thread.getLooper());//与HandlerThread中的Looper对象关联

        thread.start();

        Runnable r=new Runnable(){

       @Override

        public void run() {

        // TODO Auto-generated method stub

       if(isRunning){

        textView.setText("走了"+timer+"秒");

        timer++;

        handler.postDelayed(this, );//提交任务r,延时1秒执行

       }

        }

        };

        handler.postDelayed(r, );

        }

       }

       ã€€ã€€public class clockActivity extends Activity {

        /** Called when the activity is first created. */

        private String TAG="clockActivity";

        private Button endButton;

        private TextView textView;

        private int timer=0;

        private boolean isRunning=true;

        private Handler handler;

        @Override

        public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        endButton=(Button)findViewById(R.id.endBtn);

        textView=(TextView)findViewById(R.id.textview);

        endButton.setOnClickListener(new View.OnClickListener() {

        @Override

        public void onClick(View v) {

        // TODO Auto-generated method stub

        isRunning=false;

        }

        });

        HandlerThread thread=new HandlerThread("myThread");

        handler=new Handler(thread.getLooper());//与HandlerThread中的Looper对象关联

        thread.start();

        Runnable r=new Runnable(){

        @Override

        public void run() {

        // TODO Auto-generated method stub

        if(isRunning){

        textView.setText("走了"+timer+"秒");

        timer++;

        handler.postDelayed(this, );//提交任务r,延时1秒执行

        }

        }

        };

        handler.postDelayed(r, );

        }

       }

       ã€€ã€€æ­¤æ—¶å¤„理任务会在handlerThread线程中完成。当然这个例子会出线异常:依然是因为在非主线程中更新了UI。这样做只是为了大家能够理解这种机制。

       ã€€ã€€æ·±å…¥ç†è§£Android消息处理机制对于应用程序开发非常重要,也可以让我们对线程同步有更加深刻的认识,希望这篇文章可以对朋友们有所帮助。

android里面所说的looper是什么意思啊?

       Looper即:有消息循环的线程。

       在Android里线程分为有消息循环的源码线程和没有消息循环的线程,有消息循环的源码线程一般都会有一个Looper,这个事android的源码新概念。主线程(UI线程)就是源码雇员管理系统 源码一个消息循环的线程。针对这种消息循环的源码机制,引入一个新的源码机制Handle,有消息循环,源码就要往消息循环里 面发送相应的源码消息,自定义消息一般都会有对应的源码处理,消息的源码发送和清除,消息的源码处理,把这些都封装在Handle里面,源码注意Handle只是源码针对那些有Looper的线程,不管是apache solr源码调试UI线程还是子线程,只要有Looper,就可以往消息队列里面添加东西,并做相应的处理。

Handler 执行流程及源码解析

       本文深入解析了Handler的执行流程及源码,围绕Looper、MessageQueue、Message、Handler之间的协作运行机制,详细介绍了从sendMessage到handlerMessage的代码执行流程。

       在UI线程中,Looper是自动创建的,通过调用Looper.prepareMainLooper()方法,此方法内部调用了Looper的prepare()方法来创建Looper对象,并将其存储在ThreadLocal中,实现线程内部的工业溯源系统源码数据存储。对于子线程,则需手动创建Looper,方法与UI线程一致,同样通过Looper.prepare()完成。

       Handler在初始化时,通过ThreadLocal获取当前线程的Looper与MessageQueue。发送消息时,有三种方式:sendMessage、obtainMessage与post(runable),它们实质上操作相同,差异仅在于对Message的处理。最终,所有消息都会通过sendMessage方法调用到MessageQueue的enqueueMessage实现。

       MessageQueue内部使用单链表维护消息列表,主要包含enqueueMessage与next两个操作:enqueueMessage实现数据插入,龟股军师源码next通过死循环检查并删除链表中的消息。当MessageQueue中出现新消息时,Looper会立即检测到并处理。

       Looper的loop()方法内有一个死循环,通过messageQueue.next()检查消息队列,获取并删除新消息。检测到新消息后,调用msg.target.dispatchMessage(msg)处理消息,此方法在Looper内执行,切换到Handler创建时的线程,由Handler发送的消息最终回到Handler内部,执行dispatchMessage(msg)方法。

       Handler处理消息分为三种情况:执行run()方法,实现线程切换;使用Callback接口的实例作为mCallback,用于不使用Handler派生类的卡牌shader源码情况;重写handlerMessage(msg)方法处理具体业务。至此,从sendMessage到handlerMessage的整个流程得以清晰展现。

       整体流程总结如下:

       1. 在Handler初始化时,获取线程的Looper与MessageQueue;

       2. sendMessage方法最终调用enqueueMessage插入Message到队列,并将Handler赋值给Message对象的target属性;

       3. MessageQueue在插入Message后,Looper检测到新消息,并开始处理;

       4. Looper的loop方法通过traget属性获取到Handler对象,执行dispatchMessage方法;

       5. 最终调用继承自Handler的handlerMessage方法处理具体业务。

找到卡顿来源,BlockCanary源码精简分析

       通过屏幕渲染机制我们了解到,Android的屏幕渲染是通过vsync实现的。软件层将数据计算好后,放入缓冲区,硬件层从缓冲区读取数据绘制到屏幕上,渲染周期是ms,这让我们看到不断变化的画面。如果计算时间超过ms,就会出现卡顿现象,这通常发生在软件层,而不是硬件层。卡顿发生的原因在于软件层的计算时间需要小于ms,而计算的执行地点则在Handler中,具体来说是在UI的Handler中。Android进程间的交互通过Binder实现,线程间通信通过Handler。

       软件层在收到硬件层的vsync信号后,会在Java层向UI的Handler中投递一个消息,进行view数据的计算。这涉及到测量、布局和绘制,通常在`ViewRootImpl`的`performTraversals()`函数中实现。因此,view数据计算在UI的Handler中执行,如果有其他操作在此执行且耗时过长,则可能导致卡顿,我们需要找到并优化这些操作。

       要找到卡顿的原因,可以通过在消息处理前后记录时间,计算时间差,将这个差值与预设的卡顿阈值比较。如果大于阈值,表示发生了卡顿,此时可以dump主线程堆栈并显示给开发者。实现这一功能的关键在于在Looper中设置日志打印类。通过`Looper.loop()`函数中的日志打印,我们可以插入自定义的Printer,并在消息执行前后计算时间差。另一种方法是在日志中添加前缀和后缀,根据这些标志判断时间点。

       BlockCanary是一个用于检测Android应用卡顿的工具,通过源码分析,我们可以了解到它的实现逻辑。要使用BlockCanary,首先需要定义一个继承`BlockCanaryContext`的类,并重写其中的关键方法。在应用的`onCreate()`方法中调用BlockCanary的安装方法即可。当卡顿发生时,BlockCanary会通知开发者,并在日志中显示卡顿信息。

       BlockCanary的核心逻辑包括安装、事件监控、堆栈和CPU信息的采集等。在事件发生时,会创建LooperMonitor,同时启动堆栈采样和CPU采样。当消息将要执行时,开始记录开始时间,执行完毕后停止记录,并计算执行时间。如果时间差超过预设阈值,表示发生了卡顿,并通过回调传递卡顿信息给开发者。

       堆栈和CPU信息的获取通过`AbstractSampler`类实现,它通过`post`一个`Runnable`来触发采样过程,循环调用`doSample()`函数。StackSampler和CpuSampler分别负责堆栈和CPU信息的采集,核心逻辑包括获取当前线程的堆栈信息和CPU速率,并将其保存。获取堆栈信息时,通过在`StackSampler`类中查找指定时间范围内的堆栈信息;获取CPU信息时,从`CpuSampler`类中解析`/proc/stat`和`/proc/mpid/stat`文件的CPU数据,并保存。

       总结而言,BlockCanary通过在消息处理前后记录时间差,检测卡顿情况,并通过堆栈和CPU信息提供详细的卡顿分析,帮助开发者定位和优化性能问题。