android源码随笔——消息循环机制

在学习android过程中,我们是知道的,一个线程对应一个Loop,同时Activity默认的UI线程会有对应的默认生成的Loop,那么为什么这样呢?没事看看源码可以得到更多的信息。

 

我们从使用主线程的Looper分析:

Activity被被创建之前,会首先创建UI线程,也就是ActivityThread实例,其中含有入口。

publicstaticvoid main(String[] args)中含有

   Looper.prepareMainLooper();来创建该UI线程中的Looper

Looper.loop();来循环控制分发所得到的Message

由此,Activity便默认产生了一个UI线程的Loop

 

那么这时候呢,这个主Loop便在UI线程不断的循环,一直跑。

 

下面来看看Loop的源码,其实主要要注意两个方法prepare()loop()

其中主要是:

   publicstaticvoid prepare() {

        if (sThreadLocal.get() != null) {

            thrownew RuntimeException("Only one Looper may be created per thread");}

        sThreadLocal.set(new Looper());}//Prepare()用于初始化Looper实例


 

 private Looper() {

        mQueue = new MessageQueue();//同时初始化了MessageQueue()

        mRun = true;

        mThread = Thread.currentThread();

}


首先之前的判断语句保证了,一个线程里只能含有一个Looper实例,否则要出问题。

 

下面来看看sThreadLocal变量,可以来看看这个是表示的什么:

Staticfinal ThreadLocal<Looper>sThreadLocal =new ThreadLocal<Looper>();

ThreadLocal类的源码,可以了解到,其实就是一个通用容器,含有get(),set()等等方法,这里用于存放Looper实例。

 

接下来我们看看sThreadLocal.set(new Looper());这个语句都做了些啥?

   publicvoid set(T value) {

        Thread currentThread = Thread.currentThread();

        Values values = values(currentThread);

        if (values == null) {

            values = initializeValues(currentThread);

        }

        values.put(this, value);

    }


原来set中所做的工作无非就是初始化Values(如果当前线程并没有初始化Values的时候),并将对应的Looper实例放入Values中一个Object数组中(table),这个存放的Values是以Thread作为划分的。

 

OK,接下面来分析Loop方法:

publicstaticvoid loop() {

//获取当前线程存在的Looper对象

        Looper me = myLooper(); 

        if (me == null) {

            thrownew RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");

        }

//获取该Looper对象中的MessageQueue实例

        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();

        finallong ident = Binder.clearCallingIdentity();

        //一个无线循环遍历该MessageQueue对象,获取对应的含有的Message对象

        while (true) {

            Message msg = queue.next(); // might block

            if (msg != null) {

                if (msg.target == null) {  

//这里提前说明下msg.target其实就是对应的Handler对象

                    // No target is a magic identifier for the quit message.

                    return;

                }

                long wallStart = 0;

                long threadStart = 0;

                // 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);

                    wallStart = SystemClock.currentTimeMicro();

                    threadStart = SystemClock.currentThreadTimeMicro();

                }

//调用Handler的消息分发方法,下面详细介绍

                msg.target.dispatchMessage(msg);

 

          if (logging != null) {

             long wallTime = SystemClock.currentTimeMicro() - wallStart;

              long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;

             logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);

                    if (logging instanceof Profiler) {

                        ((Profiler) logging).profile(msg, wallStart, wallTime,

                                threadStart, threadTime);

                    }

                }

 

                // Make sure that during the course of dispatching the

                // identity of the thread wasn't corrupted.

                finallong 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.recycle();//消息分发以后,释放该Message实例

            }

        }

}


总而观之,loop要做的事情就是不断的在该线程中循环遍历该Looper实例所对应的MessageQueue.如果MessageQueue被塞入了Message,则将其dispatchMessage掉。

这里可以知道msg.target.dispatchMessage(msg);中的msg.target就是Message对应的Handler实例,来源可以来看Handler的源码。

我们在平时使用Handler的时候无非就是初始化,然后使用sendMessage,然后handleMessage

 

一起来分析下,首先是初始化工作:

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) {

            thrownew RuntimeException(

                "Can't create handler inside thread that has not called Looper.prepare()");

        }

        mQueue = mLooper.mQueue;

        mCallback = null;

}


看看myLooper是个啥东东。  

 publicstatic Looper myLooper() {

        returnsThreadLocal.get();

}

原来就是获取之前说的sThreadLocalset进去的Looper实例,一个线程对应一个LOOPER实例,没得选咯,只能获取到初始化Handler方法的那个线程对应的Looper实例了。

接下来就是获取到Looper对应的MessageQueue对象。

 

下面sendMessageà sendMessageDelayedà sendEmptyMessageAtTime,就是这个咯。

 

 

  public boolean sendMessageAtTime(Message msg, long uptimeMillis)

    {

        boolean sent = false;

        MessageQueue queue = mQueue;

        if (queue != null) {

            msg.target = this;

            sent = queue.enqueueMessage(msg, uptimeMillis);

        }

        else {

            RuntimeException e = new RuntimeException(

                this + " sendMessageAtTime() called with no mQueue");

            Log.w("Looper", e.getMessage(), e);

        }

        return sent;

}


      msg.target = this;这里就把自己实例赋值给了msg.target,

同时enqueueMessage  Message实例塞进了MessageQueue,链表实现的哦,这里不做详细介绍。

之前在Looper.looper中看到了

msg.target.dispatchMessage(msg);

 

看看这段源码:

    publicvoid dispatchMessage(Message msg) {

        if (msg.callback != null) {

            handleCallback(msg);

        } else {

            if (mCallback != null) {

                if (mCallback.handleMessage(msg)) {

                    return;

                }

            }

            handleMessage(msg);

        }

}


这里暂时不讨论Message以及Handler所给出的CallBack接口,

 

handleMessage(msg);便是我们自己所重写的那个方法。

这样一来,整个消息循环机制流程就清楚。

归纳下,如果我们从简单入手,就知道在UI线程中,已经为我们初始化了一个主Looper实例,归属于主线程(UI线程),接下来,Looper.loop()在该线程中就一直不断的循环,dispathMessage,分发消息。

HandlersendMessage其实就是把Message塞到了对应线程对应的Looper实例的那个MessageQueue中,然后捏,dispathMessage其实就是分发该消息,同时可以由自己的重写的hanleMessage方法来获取,做对应的工作。

当然,在非UI线程消息机制也是一样的道理,此处不详述咯

 

随笔随笔,估计会有很多不足以及错误的地方,希望大家能帮忙指正,谢谢各位看官。

 

原创作品,转载请注明http://blog.csdn.net/vc_player/article/details/7876762

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 新闻app是一款基于Android平台的小型项目应用程序,它主要用于展示各类新闻内容,为用户提供便捷的阅读体验。该项目的源码包含了应用程序的基本框架和功能实现,方便开发者进行二次开发和定制。 新闻app的源码主要包含以下几个方面的内容: 1. 用户界面设计:源码中包含了新闻app的界面布局和样式,开发者可以根据自己的需要进行修改和美化。用户界面通常包括新闻列表、新闻详情页、分类标签等,开发者可以自由设计并添加其他功能模块。 2. 数据获取与展示:源码中实现了与服务器进行数据交互的功能,通过网络请求获取新闻数据,并在界面上展示出来。开发者可以根据需要修改数据请求接口和解析方式,实现与自己的服务器交互。 3. 新闻分类与搜索:源码中提供了新闻分类和搜索功能的实现,用户可以根据自己的兴趣和需求选择不同的新闻分类进行浏览,也可以通过搜索关键词进行精确定位。 4. 用户交互与分享:源码中包含了用户的登录注册功能和新闻内容的分享功能,用户可以通过登录账号进行个性化设置和收藏喜欢的新闻内容,也可以将新闻分享到社交媒体上与他人交流。 总之,新闻app源码是一个基础框架,开发者可以在此基础上进行二次开发和定制,根据自己的需求添加功能模块和美化界面,实现自己独特的新闻应用。 ### 回答2: Android新闻App是一个基于Android平台开发的小型项目,它的主要功能是提供最新的新闻内容给用户,并且用户可以进行浏览、搜索和分享等操作。下面是这个项目的一些关键特点和所需的源码组成部分: 1. 特点: - 用户界面友好,交互性强,提供舒适的浏览体验; - 支持实时更新,提供最新的新闻内容; - 具备搜索功能,方便用户查找感兴趣的新闻; - 支持新闻分享功能,方便用户将新闻分享给朋友; - 具备图文混排的能力,可以展示新闻的文字和图片。 2. 源码组成部分: - 主界面布局代码:定义了App的整体布局结构,包括顶部导航栏、底部工具栏和新闻显示区域等。 - 数据源代码:负责获取新闻数据,可以通过API接口获取最新的新闻内容,也可以从本地数据库获取已缓存的新闻数据。 - 新闻列表适配器代码:用于将新闻数据展示在界面上,包括标题、描述和图片等。 - 新闻详情界面代码:用于显示单篇新闻的详细内容,包括标题、正文和相关图片等。 - 搜索功能代码:实现了按关键字搜索新闻的功能,可以在已有的新闻数据中进行筛选。 - 分享功能代码:集成了社交媒体的分享SDK,方便用户将新闻内容分享给朋友。 - 图片加载和缓存代码:处理了新闻中的图片加载和本地缓存,提高了图片加载速度和用户体验。 通过以上的源码组成部分,可以完成一个基本的新闻App,用户可以在界面上浏览最新的新闻内容,进行搜索和分享操作。这个小项目可以帮助开发者理解Android开发框架和开发方式,提高编码能力和UI设计能力。 ### 回答3: 新闻app是基于Android平台开发的一个小型应用程序,可以提供用户各种最新的新闻资讯。以下是关于这个项目的源码介绍。 该项目源码主要由Java语言编写,使用了Android Studio作为开发工具。代码结构清晰,包含了主要的几个模块。 1. 用户界面模块:这个模块负责显示新闻列表和新闻详情等信息,主要包含布局文件和相应的逻辑代码。列表界面使用RecyclerView控件展示新闻列表,详情界面使用WebView展示新闻内容。 2. 网络请求模块:这个模块负责与后台服务器进行数据交互,使用了Android的HttpURLConnection类来发送请求和接收响应。请求参数可以根据实际需要进行修改,例如可以根据新闻类别进行请求。 3. 数据解析模块:这个模块负责解析从服务器返回的JSON格式的数据,转换成Java对象供应用程序使用。可以使用Android提供的JSON解析库,如Gson。 4. 数据存储模块:这个模块负责缓存新闻数据,以提高应用程序的响应速度。可以使用SharedPreferences或SQLite数据库来存储新闻数据。同时也可以使用图片缓存库,如Glide或Picasso来缓存新闻图片。 5. 用户交互模块:这个模块负责处理用户的交互行为,例如点击新闻列表项跳转到新闻详情界面,下拉刷新获取最新数据等。可以使用Android提供的相关控件和事件监听器来实现用户交互。 除了以上几个主要的模块,还可以根据需要添加其他功能,例如搜索栏、分享按钮等。 总体来说,这个新闻app的源码提供了一个完整的开发框架,初学者可以通过阅读和理解源码来学习Android应用程序的开发流程和一些常用技术。同时,也可以根据实际需求进行二次开发,添加新的功能和改进用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值