本人工作已经一年多了,具体点说已经一年多3个月了,实习的早,过早的受到了社会的蹂躏。今年6月多份毕业了,然后就到了一个比较大的公司,具体名字就不说了,就是妹子超级超级多。。。。在学校学的是游戏,cx之类的,但是鬼使神差的毕业后跟着同学就干上了应用,多亏了我的第一个老板--李金波,超级感谢~
好了,废话不多说了,接下来就开启Android多线程与异步任务的学习吧,由于内容有点多,分几篇博客来和大家扯淡~
学习Android当然就避免不了学习java,java中也有多线程还有线程之间的同步问题等等~设计到的知识也是相当庞大的,建议小伙伴们看看传智播客张孝祥的java多线程同步那几个视频,讲的是相当牛逼~,可惜祥哥已经走了,为啥干程序员的都活不长!时间充足的话,我后面也会分享线程同步的心得,当然得感谢祥哥,祝愿祥哥一切安好~
我们将分5部分来讲解Android的多线程与异步任务
1:java多线程基础
(1):ThreadPoolExecutor
(2):ScheduleExecutor
(3):线程同步(synchronized,lock,semaphore)
2:在Android中使用的线程
3:在用Handler异步时不可或缺的组件
4:使用AsyncTask快速实现异步任务
5:检测程序中是否有需要使用多线程的地方
其中第一部分,在最后面的篇章中我争取会给大家讲解java中的多线程的使用和线程之间的同步的问题,让各位大侠见笑了,因为吃的饭还没有你们吃的盐多~、
本篇博客我们着重讲解第二个部分----在Android中使用的线程
在引出这个话题的时候,我们要想到三个问题:
1:为何使用多线程?
2:在Android中如何使用多线程或者异步操作?
3:多线程和界面之间的交互
首先来看第一个问题--为何使用多线程?我们使用多线程往往是涉及到一些复杂的IO操作(文件操作,网络操作,数据库操作),复杂运算,定时操作。想必大家都碰到过应用程序弹出“无响应”的对话框,那是因为在Android主线程中进行了超时的操作,过多的阻塞了主线程就会导致应用出现ANR的Waring~
下面给大家举一个为什么要使用多线程的小例子:假如我们现在有一个需求,需要点击按钮的时候出现一个Dialog(对话框),这个对话框显示5秒钟后就自动消失,我们怎么实现呢?如果我们在按钮的点击事件中先调用Dialog.show();然后调用Thread.sleep(5000);最后调用Dialog.dismiss();是不是就可以了呢?答案是否定的!因为你会发现这样的现象,我们好像从始至终就没有看见Dialog~,这是为什么呢?
Android中的绘制都是线程不安全的,很多人就会疑问线程不安全的那还不乱了套?这个不用担心了,因为Android只有一个绘制线程,那就是UI线程(主线程)。只存在一个线程就不存在什么线程安全不安全的问题了~回到我们的问题,由于Android中控件的绘制是在UI线程中,当我们调用Dialog.show()这句代码的时候,距离Dialog绘制出来还有一定的时间间隔,而此时我们调用了Thread.sleep(5000);让主线程休眠了5秒钟,哥啊,主线程都休息了,还绘制个毛线啊!当然就看不见了~所以我们需要开启一个线程去让Dialog经过5秒钟去消失掉~(当然也有其他的方法~)
明白了为何使用多线程的问题,我们接下来看一下使用多线程或者异步操作的几种方式
1:Thread
2:Handler
3:AsyncTask
4:AsyncTaskLoader 这个类是API Level 11 中有的,也就是3.0(不过V4包中有~)
值得一提的是AsyncTaskLoader的子类CursorLoader这个用来关联数据库来进行操作也是不错的~具体的会再讲解
相信大家用的最多的就是Hander和AsyncTaskLoader了,本人平时用的最多的也就是这两个~,现在的项目中涉及到了聊天才用到了CursorLoader,不过感觉还凑活~
大家不要着急~,Handler和AsyncTask后面会重点剖析~第一篇就给大家说一个大概的轮廓,大家知道有这么几种方式就可以了,不要着急啊,么么哒~
最后说一下第三个小问题:多线程如何和界面交互呢?Android中提供的方式有:
1:Activity.runOnUIThread(Runnable);
2:View.post(Runnable); View.postDelay(Runnable);
3:Handler
4:AsyncTask
线程和界面交互的时候我们要严格明确下面两个原则:
(1)不要阻塞UI线程
(2)不要在UI线程外更新界面
Tips:
1:对于耗时操作,我们应该放到非主线程中运行,从而避免阻塞主线程~
2:为了保证良好的用户体验,建议对超过50ms的操作,都使用线程处理~
为什么超过了50ms都要放在线程中处理呢?不是导致应用程序无响应是10秒钟吗?为大家算一下1秒钟=1000ms,1000ms/50ms=20 fps ,也就是1秒钟刷新的帧数为20帧,这是人眼感觉不到很卡顿的帧数,假如低于了20帧,就会感觉滑动页面之类的操作很卡顿~所以低于50ms对于用户体验还是很重要的~
下面给大家贴出一个小例子,方便大家的理解,上代码喽~
首先贴出布局文件的代码~
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="从主线程加载" android:id="@+id/loadFromMainThread"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="从异步线程加载1" android:id="@+id/loadFromOtherThread1"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="从异步线程加载2" android:id="@+id/loadFromOtherThread2"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="利用Asyncthread加载图片" android:id="@+id/loadFromAsync"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/image"/> </LinearLayout>
接下来就是主体代码部分
package com.example.XiaoYuAndroidThread1; import android.app.Activity; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.ImageView; import java.io.IOException; import java.net.URL; public class MyActivity extends Activity { private ImageView image; public static final String url = "http://pic.sc.chinaz.com/files/pic/pic9/201501/apic9211.jpg"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); image = (ImageView) findViewById(R.id.image); //直接在主线程中下载图片 findViewById(R.id.loadFromMainThread).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Drawable drawable = loadImageFromNework(url); image.setImageDrawable(drawable); } }); //在开启的新线程中下载图片1 findViewById(R.id.loadFromOtherThread1).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { Drawable drawable = loadImageFromNework(url); image.setImageDrawable(drawable); } }).start(); } }); //在开启的新线程中下载图片2 findViewById(R.id.loadFromOtherThread2).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { final Drawable drawable = loadImageFromNework(url); image.post(new Runnable() { @Override public void run() { image.setImageDrawable(drawable); } }); } }).start(); } }); //利用AsyncTask去下载图片 findViewById(R.id.loadFromAsync).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new DownloadImageTask().execute(url); } }); } private class DownloadImageTask extends AsyncTask<String,Void,Drawable>{ @Override protected Drawable doInBackground(String... params) { return loadImageFromNework(params[0]); } @Override protected void onPostExecute(Drawable drawable) { image.setImageDrawable(drawable); } } //利用Drawable从网络上加载图片 private Drawable loadImageFromNework(String imageUrl) { Drawable drawable = null; try { drawable = Drawable.createFromStream(new URL(imageUrl).openStream(), "meinv.jpg"); } catch (IOException e) { Log.e("xiaoyu", e.getMessage()); } if (drawable == null) { Log.e("xiaoyu", "drawable is null"); } else { Log.e("xiaoyu", "drawable not null"); } return drawable; } }
第一个按钮为在主线程中加载图片,当然这张图片很小,假如图片很大的话,会造成主线程的阻塞,造成应用程序ANR~
第三个按钮的实现逻辑和第二个按钮是一样的,将加载网络数据的耗时操作放在了Thread中,但是和第二个按钮不同的是在得到drawable后,通过View的post方法将更新UI的操作放到了主线程中去更新,所以点击第三个按钮就不会导致程序的崩溃。
第四个按钮通过AsyncTask来更新UI,后面会详细剖析~
加载图片的方法也是极其的简单,通过Drawable中的createFromStream这个方法来实现,第一个参数传递一个IoStream,我们通过new Url(url).openStream();来实现,第二个参数没有必要传递,可以传递null,这里随便起了一个名字~
最后别忘记了在AndroidManifist文件中声明 访问网络的权限~
以上就是本篇的全部内容,接下来会为大家继续推出这一系列的博客~
就不给大家截图片了,我这里不是很方便~
测试代码地址:http://download.csdn.net/detail/ly985557461/8424333
原文地址:http://blog.csdn.net/ly985557461/article/details/43494837