Android异步更新UI教程总结与demo
2017-04-27 10:54
429 查看
概述
我们在Android开发中可能会遇到下面错误:Only the original thread that created a view hierarchy can touch its views
意思是我们只能在主线程更新UI,我们知道UI线程(主线程)如果被阻塞5秒,就会ANR,所以我们耗时的操作都会新开启线程,这就必然涉及到后续的UI的更新,今天我们就来讨论下异步更新UI的使用方法总结。主要讲用法,具体的底层实现还是需要自己去慢慢摸索。
分类
1.Activity.runOnUiThread(Runnable);2.View.post(Runnable),View.postDelay(Runnable,long);
3.Thread+Handler;
4.AsyncTask.
这几种方法底层都是用的Handler,只是封装的不一样。
实践
下面我们就一个一个来讲他们的用法1.Activity.runOnUiThread(Runnable);
(1)原理:
runOnUiThread(runnable)是Activity的内部类,我们先看看源代码:
public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } }
我们可以看到,也是用Handler实现的。我们简单解释下这个类,如果你在UI线程操作,这个动作将立即实现,如果非UI线程,这个动作将Post到UI线程的队列中。
(2)使用:
new Thread(new Runnable() { @Override public void run() { //耗时操作 runOnUiThread(new Runnable() { public void run() { //更新UI } }); } }).start();
2.View.post(Runnable)
(1)原理:
源代码
public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.post(action); } // Postpone the runnable until we know on which thread it needs to run. // Assume that the runnable will be successfully placed after attach. getRunQueue().post(action); return true; }
我们可以看到也会用到Handler,然后将action加到消息队列中。
(2)用法:
在开启的线程执行下面这个方法
listView.post(new Runnable() { @Override public void run() { listView.setAdapter(new NewsListBaseAdapter(list,MainActivity.this));//更新UI } });
3. Thread+Handler;
(1)Handler简介:
我们先来看看Android消息机制:
Android的消息机制主要指 Handler 的运行机制,Handler的运行需要底层的MessageQueue 和 Looper 的支撑。
MessageQueue:消息队列,它的内部存储了一组消息,以队列的形式对外提供插入和删除的工作,其内部存储结构采用单链表的数据结构来存储消息列表。
Looper:可理解为消息循环。
由于MessageQueue只是一个消息存储单元,不能去处理消息,而Looper会以无限循环的形式去查找是否有新的消息,如果有的话就处理,否则就一直等待着。
Looper还有一个特殊的概念,就是ThreadLocal,它的作用可以在每个线程中存储数据。
Handler创建的时候会采用当前线程的Looper来构造消息循环系统,Handler内部需要使用ThreadLocal来获取每个线程的Looper。ThreadLocal可以在不同的线程中互不干扰地存储并提供数据。
注意:线程默认是没有Looper的,如果需要使用Handler就必须为线程创建Looper。主线程,UI线程,它就是ActivityThread,ActivityThread被创建时就会初始化Looper,这也是在主线程中默认可以使用Handler的原因。
Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态,如果对UI控件加锁会有两个确定:首先加上锁机制会使UI访问逻辑变得负责;其次锁机制会降低UI的访问效率,锁机制会阻碍某些线程的执行。鉴于这个两个缺点,最简单且高效的方法就是采用单线程模型来处理UI操作,只需要通过Handler切换一下UI访问的执行线程即可。
Handler创建完成后,其内部的 Looper 以及 MessageQueue就可以和Handler一起工作,Handler的post方法将一个 Runnable 投递到 Handler 内部的 Looper中去处理,也可以通过send发送一个消息(post最终也是通过send来完成的)。当Handler的send方法被调用时,它会调用 MessageQueue 的 enqueueMessage 方法将这个消息放入消息队列中,然后Looper发现有新消息到来时,就会处理这个消息,最终消息中的Runnable或者Handler的 handleMessage方法就会被调用。注意 Looper 是运行在创建Handler所在的线程中的,这样一来Handler中的业务逻辑就可以切换到创建Handler所在的线程中去执行。
下面我们在来看看handler发送消息的方法:
1.post(Runnable)
2.postAtTime(Runnable,long)
3.postDelay(Runnable,long)
4.sendEmptyMessage(int what)
5.sendMessage(Message)
6.senMessageAtTime(Message,long)
7.sendMessageDelayed(Message,long)
post方式添加一个实现Runnable接口的匿名对象到消息对列中,在目标收到消息后就可以以回调的方式在自己的线程中执行
Message对象所具有的属性:
(2)用法
在子线程发送消息:
Message msg=handler.obtainMessage(); msg.obj=list;//发送了一个list集合 //sendMessage()方法,在主线程或者Worker Thread线程中发送,都是可以的,都可以被取到 handler.sendMessage(msg);
在主线程处理:
handler=new MyHandler(); class MyHandler extends Handler { @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.i(">>>>>>>",Thread.currentThread().getName()); list= (List<NewsBean.Second.Third>) msg.obj;//接收传过来的集合 listView.setAdapter(new NewsListBaseAdapter(list,MainActivity.this));//更新UI } }
4. AsyncTask.
(1)简介:
AsyncTask实际上是一个线程池,在代码上比handler要轻量级但是实际上要比Handler要耗资源,Handler仅仅发送了一个消息队列,连线程池对没有开。
主要方法:
1.onPreExecute(),(可选方法)最新用户调用excute时的接口,任务执行之前调用该方法,可以在这里显示进度对话框
2.doInBackground(Params…),后台执行比较耗时的操作,不能直接操纵UI。在该方法中使用
3.publishProgress(progress…)来更新任务的进度。
4.onProgressUpdate(Progress…),在主线程中执行,显示进度条
5.onPostExecute(Result),此方法可以从doinbackground得到的结果来更新UI,在主线程中执行,执行的结果作为参数返回。
6.onCancelled(Object)调用此方法可以随时取消操作。
AsyncTask是个抽象类,定义了三种泛型:
params: 启动任务执行的输入参数,如:http请求的URL
progress:后台任务执行的百分比
result:返回结果,如:String、list集合等
(2)使用:
新建一个类继承AsyncTask,重写方法。
public class NewsListAsyncTask extends AsyncTask<String,Void,List<NewsList>>{ private ListView listView; private Context context; public static List<NewsList> list; public NewsListAsyncTask() { } public NewsListAsyncTask (ListView listView,Context context) { this.listView=listView; this.context=context; } @Override protected List<NewsList> doInBackground(String... params) { //在这里做耗时操作 list=getJsonData(params[0]); return list; } //从网络中获取数据 private List<NewsList> getJsonData(String param) { List<NewsList> list =new ArrayList<>(); String jsonString=""; try { jsonString=readString(new URL(param).openStream()); } catch (IOException e) { e.printStackTrace(); } NewsList newslist=null; JSONObject jsonobject=null; try { jsonobject=new JSONObject(jsonString); } catch (JSONException e) { e.printStackTrace(); } try { try { jsonobject = jsonobject.getJSONObject("result"); } catch (JSONException e) { e.printStackTrace(); } JSONArray jsonarray = new JSONArray(); jsonarray = jsonobject.getJSONArray("data"); for (int i = 0; i < jsonarray.length(); i++) { jsonobject = jsonarray.getJSONObject(i); newslist = new NewsList(); newslist.realtype=jsonobject.getString("realtype"); newslist.url = jsonobject.getString("url"); newslist.picture = jsonobject.getString("thumbnail_pic_s"); newslist.time = jsonobject.getString("date"); newslist.title = jsonobject.getString("title"); list.add(newslist); } } catch (JSONException e) { e.printStackTrace(); } return list; } //通过字符流读取 private String readString(InputStream is) { InputStreamReader isr = null; String result = ""; String line = ""; try { isr = new InputStreamReader(is, "utf-8"); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } BufferedReader bufferedReader = new BufferedReader(isr); try { while ((line = bufferedReader.readLine()) != null) { result += line; } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return result; } @Override protected void onPostExecute(List<NewsList> newsLists) { super.onPostExecute(newsLists); //在这里更新UI,我这里是用baseadapter往listview中添加数据 NewsListAdapter adapter=new NewsListAdapter(context,newsLists); listView.setAdapter(adapter); list=newsLists; } }
在主线程中调用:
//URL为访问的网络地址 new OtherNewsListAsyncTask(listView,InternationalNewsActivity.this).execute(URL);
最后附上Demo,里面有这里面其中三种方法的使用,另外demo中有惊喜。
CSDN地址:http://download.csdn.net/detail/simon_crystin/9826686
GitHub地址:https://github.com/Simon986793021/ScienceNews
相关文章推荐
- android开发教程之handler异步更新ui
- android AsyncTask介绍 异步更新UI
- Android使用Handler异步更新UI
- Android 异步更新UI----handler+thread
- Android开发——异步更新UI的四种方式
- Android 通过广播来异步更新UI
- Android-初识Handler-子线程异步更新UI
- Android之Handler更新UI总结
- Android 使用线程更新UI(附带计时器demo)
- Handler后台异步更新UI的总结
- Android异步更新UI的四种方式
- Android 异步更新UI----AsyncTask
- 多线程异步处理:AsyncTask异步更新UI界面 android开发教程
- android异步UI刷新实例总结
- Android异步更新UI-线程池-Future-Handler实例分析
- android: Handler实现异步更新UI
- Android 异步更新UI----AsyncTask
- Android使用handler和Runnable结合,自定义View更新UI的Demo
- 多线程异步处理:AsyncTask异步更新UI界面 android开发教程 分类: Android Activity 2014-07-10 14:13 112人阅读 评论(0) 收藏
- android进行异步更新UI的四种方式