Android的UI更新只能在UI线程中,即主线程。子线程中如果要进行UI更新,都是要通知主线程来进行。

几种实现方式总结如下,欢迎补充。

1、runOnUiThread()

子线程中持有当前Activity引用(假如为Activity mActivity;),即可以调用mActivity的runOnUiThread(Runnable r)方法。

2、post()和postDelay()

子线程如果持有某个View的引用,要对该View进行更新,则可调用该View对象的post(Runnable r)或postDelay(Runnable r)方法

Handler对象也有post()方法。其实在Android的源码中,这些post()方法都是借助下面的第3种方法:Handler + Message来实现的。

3、Handler + Message或者Handler + Thread + Message

主线程建立时,默认情况下是有Looper的,可以处理消息队列。

在主线程建立一个Handler对象,复写其handleMessage()方法,在该方法中实现UI更新。

示例:

private static final int MSG_CODE = 1001;

private Handler mHandler = new Handler()

{

@Override

public void handleMessage(Message msg)

{

//接收并处理消息

if(msg.what == MSG_CODE)

{

//UI更新

}

}

};

public void doSomething()

{

new Thread()

{

@Override

public void run()

{

//子线程发送信息

Message msg = mHandler.obtainMessage(MSG_CODE);

msg.sendToTarget();

}

}.start();

}

4、Broadcast

子线程中发送广播,主线程中接收广播并更新UI

5、AsyncTask

AsyncTask可方便地实现新开一个线程,并将结果返回给UI线程,而不需要开发者手动去新开一个线程,也无须开发者使用Handler,非常方便。

应当注意的是AsyncTask是一个抽象类,其三个泛型参数的意义如下:
AsyncTask<Param, Progress, Result>
Param:发送给新开的线程的参数类型
Progress:表征任务处理进度的类型。
Result:线程任务处理完之后,返回给UI线程的值的类型。

该类中有四个抽象函数,onPreExecute(), doInBackground(Params... param),
onProgressUpdate(Progress... progress), onPostExecute(Result result)。
除了,doInBackground(Params...)方法,其它三个方法都运行在UI线程。

自定义一个类继承AsyncTask并至少实现
doInBackground()函数。在该函数中执行的过程中,可以随时调用publishProgress(Progress...)报告其执行进
度。此时会触发另一个方法onProgressUpdate(Progress...
progress),以便在UI线程中的某些控件(如ProgressBar)上更新任务处理的进度。

也可以等doInBackground()执行完,进入onPostExecute()方法后,再进行UI控件的更新。

可在任意时间,任意线程中,取消AsyncTask开启的任务(调用自定义的AsynTask子类的cancel(boolean mayInterruptIfRunning)方法)

使用示例如下:

//如果没记错的话,这个例子应该是之前总结的时候从官网剪下来的

public void onClick(View v) {

   new DownloadImageTask().execute("http://example.com/image.png");

}

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {

   /** The system calls this to perform work in a worker thread and

     * delivers it the parameters given to AsyncTask.execute() */

   protected Bitmap doInBackground(String... urls) {

       return loadImageFromNetwork(urls[0]);

   }

   /** The system calls this to perform work in the UI thread and delivers

     * the result from doInBackground() */

   protected void onPostExecute(Bitmap result) {

       mImageView.setImageBitmap(result);

   }

}

6、EventBus

  • 什么是EventBus

    EventBus是Android下高效的发布/订阅事件总线机制。作用是可以代替传统的Intent,Handler,Broadcast或接口函数在Fragment,Activity,Service,线程之间传递数据,执行方法。特点是代码简洁,是一种发布订阅设计模式(Publish/Subsribe),或称作观察者设计模式。

  • 下载EventBus

    1. 下载EventBus库:

    2. EventBus-2.4.0.jar放入libs即可

  • 如何使用EventBus

    1. 定义事件, 定义一个类,继承默认的Object即可,用于区分事件和传输数据。 本例为MsgEvent1和MsgEvent2
    2. 添加订阅者:EventBus.getDefault().register(this); 将所在类作为订阅者,框架会通过反射机制获取所有方法及其参数。
        订阅者所在类可以定义以下一个或多个方法用以接收事件:

         public void onEvent(MsgEvent1 msg)

        public void onEventMainThread(MsgEvent1 msg)

        public void onEventBackgroundThread(MsgEvent1 msg)

        public void onEventAsync(MsgEvent1 msg)

   3.发布者发布事件:EventBus.getDefault().post(new MsgEvent1("主线程发的消息1"));
      一旦执行了此方法, 所有订阅者都会执行第二步定义的方法。
   4. 取消订阅:EventBus.getDefault().unregister(this); 当订阅者不再被使用,或者被关闭时,最好进行取消订阅,不再接受事件消息。
   5. 注意事项:发布者post方法参数是Object类型,也就是可以发布任何事件。订阅者接受消息时,只要定义的是第二步四个方法任意一个,并且参数和发布者发布的一致,即可被执行。发布者也可以通过第二步接收消息,订阅者也可以作为发布者发消息给自己。

    • 代码实现 (本例是两个Fragment交互, 也可以是Service,Activity,Fragment以及任意类之间交互)
    • 点击左边面板的条目, 可以发送事件,右面板(另一个Fragment)接收到事件,显示界面,打印日志。
    • 代码下载 http://yunpan.cn/cctFTVuWtyIgK  访问密码 66ed

      

1.主界面搭建:
java

public class MainActivity extends FragmentActivity {

        @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} }

xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="?android:attr/dividerHorizontal"
android:orientation="horizontal"
android:showDividers="middle"
android:baselineAligned="false"
tools:context="com.itheima.eventbusdemo.MainActivity" > <fragment
android:id="@+id/left_fragment"
android:name="com.itheima.eventbusdemo.LeftFragment"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1" /> <fragment
android:id="@+id/right_fragment"
android:name="com.itheima.eventbusdemo.RightFragment"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="3" /> </LinearLayout>

2. 定一个事件类MsgEvent1 (MsgEvent2与此一致):

public class MsgEvent1 {
private String msg; public MsgEvent1(String msg) {
super();
this.msg = msg;
}
public String getMsg() {
return msg;
}
}

3. 将右面板作为订阅者, 执行方法并接收数据:

public class RightFragment extends Fragment {
private TextView tv;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 界面创建时,订阅事件, 接受消息
EventBus.getDefault().register(this);
}
@Override
public void onDestroy() {
super.onDestroy();
// 界面销毁时,取消订阅
EventBus.getDefault().unregister(this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// 布局只有一个TextView,不再贴代码
View view = inflater.inflate(R.layout.fragment_right, null);
tv = (TextView) view.findViewById(R.id.tv);
return view;
} /**
* 与发布者在同一个线程
* @param msg 事件1
*/
public void onEvent(MsgEvent1 msg){
String content = msg.getMsg()
+ "\n ThreadName: " + Thread.currentThread().getName()
+ "\n ThreadId: " + Thread.currentThread().getId();
System.out.println("onEvent(MsgEvent1 msg)收到" + content);
} /**
* 执行在主线程。
* 非常实用,可以在这里将子线程加载到的数据直接设置到界面中。
* @param msg 事件1
*/
public void onEventMainThread(MsgEvent1 msg){
String content = msg.getMsg()
+ "\n ThreadName: " + Thread.currentThread().getName()
+ "\n ThreadId: " + Thread.currentThread().getId();
System.out.println("onEventMainThread(MsgEvent1 msg)收到" + content);
tv.setText(content);
} /**
* 执行在子线程,如果发布者是子线程则直接执行,如果发布者不是子线程,则创建一个再执行
* 此处可能会有线程阻塞问题。
* @param msg 事件1
*/
public void onEventBackgroundThread(MsgEvent1 msg){
String content = msg.getMsg()
+ "\n ThreadName: " + Thread.currentThread().getName()
+ "\n ThreadId: " + Thread.currentThread().getId();
System.out.println("onEventBackgroundThread(MsgEvent1 msg)收到" + content);
} /**
* 执行在在一个新的子线程
* 适用于多个线程任务处理, 内部有线程池管理。
* @param msg 事件1
*/
public void onEventAsync(MsgEvent1 msg){
String content = msg.getMsg()
+ "\n ThreadName: " + Thread.currentThread().getName()
+ "\n ThreadId: " + Thread.currentThread().getId();
System.out.println("onEventAsync(MsgEvent1 msg)收到" + content);
} /**
* 与发布者在同一个线程
* @param msg 事件2
*/
public void onEvent(MsgEvent2 msg){
String content = msg.getMsg()
+ "\n ThreadName: " + Thread.currentThread().getName()
+ "\n ThreadId: " + Thread.currentThread().getId();
System.out.println("onEvent(MsgEvent2 msg)收到" + content);
tv.setText(content);
}
}

4. 在左面板发布消息。(任意类都可以发布消息)

public class LeftFragment extends ListFragment {

        @Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); String[] strs = new String[]{"主线程消息1", "子线程消息1", "主线程消息2"};
setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, strs));
} @Override
public void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
// 主线程
System.out.println(
"----------------------主线程发的消息1"
+ " threadName: "+ Thread.currentThread().getName()
+ " threadId: " + Thread.currentThread().getId());
EventBus.getDefault().post(new MsgEvent1("主线程发的消息1"));
break;
case 1:
// 子线程
new Thread(){
public void run() {
System.out.println(
"----------------------子线程发的消息1"
+ " threadName: "+ Thread.currentThread().getName()
+ " threadId: " + Thread.currentThread().getId());
EventBus.getDefault().post(new MsgEvent1("子线程发的消息1"));
};
}.start(); break;
case 2:
// 主线程
System.out.println(
"----------------------主线程发的消息2"
+ " threadName: "+ Thread.currentThread().getName()
+ " threadId: " + Thread.currentThread().getId());
EventBus.getDefault().post(new MsgEvent2("主线程发的消息2"));
break;
}
} }

分别点击左边条目, Log输出分析

EventBus框架原理流程图

        

1. Publisher是发布者, 通过post()方法将消息事件Event发布到事件总线
2. EventBus是事件总线, 遍历所有已经注册事件的订阅者们,找到里边的onEvent等4个方法,分发Event
3. Subscriber是订阅者, 收到事件总线发下来的消息。即onEvent方法被执行。注意参数类型必须和发布者发布的参数一致。

EventBus 部分源码解析 参考http://blog.csdn.net/superjimmy/article/details/45601515

Android线程间通信更新UI的方法(重点分析EventBus)的更多相关文章

  1. Android线程间通信机制——深入理解 Looper、Handler、Message

    在Android中,经常使用Handler来实现线程间通信,必然要理解Looper , Handler , Message和MessageQueue的使用和原理,下面说一下Looper , Handl ...

  2. Android线程间通信的几种实现方式

    1. 通过Handler机制: private void one() { handler=new Handler(){ @Override public void handleMessage(Mess ...

  3. android 线程间通信

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha 1,共享内存 变量 2,文件,数据库 3,处理器 消息机制 4, 线程 的 等待,通知 ...

  4. android线程间通讯

    近来找了一些关于android线程间通信的资料,整理学习了一下,并制作了一个简单的例子. andriod提供了 Handler 和 Looper 来满足线程间的通信.例如一个子线程从网络上下载了一副图 ...

  5. java多线程:线程间通信——生产者消费者模型

    一.背景 && 定义 多线程环境下,只要有并发问题,就要保证数据的安全性,一般指的是通过 synchronized 来进行同步. 另一个问题是,多个线程之间如何协作呢? 我们看一个仓库 ...

  6. java线程间通信1--简单实例

    线程通信 一.线程间通信的条件 1.两个以上的线程访问同一块内存 2.线程同步,关键字 synchronized 二.线程间通信主要涉及的方法 wait(); ----> 用于阻塞进程 noti ...

  7. java多线程与线程间通信

    转自(http://blog.csdn.net/jerrying0203/article/details/45563947) 本文学习并总结java多线程与线程间通信的原理和方法,内容涉及java线程 ...

  8. juc包:使用 juc 包下的显式 Lock 实现线程间通信

    一.前置知识 线程间通信三要素: 多线程+判断+操作+通知+资源类. 上面的五个要素,其他三个要素就是普通的多线程程序问题,那么通信就需要线程间的互相通知,往往伴随着何时通信的判断逻辑. 在 java ...

  9. Android子线程更新UI的方法总结

    版权声明:本文为博主原创文章,转载请注明出处:https://i.cnblogs.com/EditPosts.aspx?postid=6121280 消息机制,对于Android开发者来说,应该是非常 ...

随机推荐

  1. Android ——单元测试

    什么是单元测试 首先需要介绍一下什么是单元测试.很多人像我一样,本科并不是计算机专业出身的,如果在职的公司不要求做单元测试的话,可能对这个词并没有一个确切的概念.而即使是计算机专业出身,如果毕业以后写 ...

  2. 十个最适合 Web 和 APP 开发的 NodeJS 框架

    在浏览器以外运行 JavaScript 对于 JavaScript 爱好者来说非常神奇,同时也肯定是 web 应用程序开发界最受欢迎的进步之一.全球各地的开发者张开双臂拥抱 NodeJS. 对于新手来 ...

  3. Adobe Audition音频制作

    Adobe Audition 同义词 AU(软件(Adobe Audition))一般指Adobe Audition Adobe Audition是一个专业音频编辑和混合环境,原名为Cool Edit ...

  4. MVC 知识点学习1

    1.@Scripts.Render("~/bundles/kindeditor");@Styles.Render("~/Css/")   //(加载)引用bun ...

  5. Android中通过注解代替findViewById方法

    转自:http://www.2cto.com/kf/201405/302998.html 这篇文章主要讲解注解实现findViewById的功能,首先我们来熟悉一下在java中怎么定义一个注解和解析一 ...

  6. win10 用cmake 3.5.2 和 vs 2015 update1 编译 GPU版本(cuda 8.0, cudnn v5 for cuda 8.0)

    win10 用cmake 3.5.2 和 vs 2015 update1 编译 GPU版本(cuda 8.0, cudnn v5 for cuda 8.0)  用vs 2015打开 编译Release ...

  7. 使用sublime编写c/c++ 总结

    大块头IDE Visual studio太大了,记事本也能写代码但无疑是装逼过分了.写一些轻量级的c/c++代码使用sublime来写是个很好的选择. 三步走: 编译器(win下安装了vs就使用cl, ...

  8. 一个Java内存可见性问题的分析

    如果熟悉Java并发编程的话,应该知道在多线程共享变量的情况下,存在“内存可见性问题”: 在一个线程中对某个变量进行赋值,然后在另外一个线程中读取该变量的值,读取到的可能仍然是以前的值: 这里并非说的 ...

  9. php 删除一段字符串的中的中文

    <?php header('Content-type:text/html;charset=utf-8'); function p($arr){ echo "<pre>&qu ...

  10. 413 Request Entity Too Large

    做小视频上传,结果接口总是返回500,服务器端跟踪,根本就进不来,再次翻查,发下服务器返回的其实是413,只不过APP底层接口将所有不是200的回包都转成500了,问题定位. 有了错误码,有了描述,字 ...