文章类型: ANDROID
关键词: Android,多线程,异步任务,异常操作
内容摘要: 带你飞之Android多线程与异步任务

带你飞之Android多线程与异步任务(第一篇)

2017/9/12 17:22:58    来源:apple    阅读:

本人工作已经一年多了,具体点说已经一年多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>

父布局为LinearLayout,里面竖直方向摆放了4个按钮,最下面是一个ImageView,用来显示图片~

接下来就是主体代码部分

package com.example.AndroidThread;  
  
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;  
    }  
}

这里要声明一下,点击第二个按钮的时候,程序会崩溃(这是正常的~),因为在UI线程外更新了ImageView,所以程序崩溃了,这是为了程序的演示效果~

第一个按钮为在主线程中加载图片,当然这张图片很小,假如图片很大的话,会造成主线程的阻塞,造成应用程序ANR~

第三个按钮的实现逻辑和第二个按钮是一样的,将加载网络数据的耗时操作放在了Thread中,但是和第二个按钮不同的是在得到drawable后,通过View的post方法将更新UI的操作放到了主线程中去更新,所以点击第三个按钮就不会导致程序的崩溃。

第四个按钮通过AsyncTask来更新UI,后面会详细剖析~

加载图片的方法也是极其的简单,通过Drawable中的createFromStream这个方法来实现,第一个参数传递一个IoStream,我们通过new Url(url).openStream();来实现,第二个参数没有必要传递,可以传递null,这里随便起了一个名字~

最后别忘记了在AndroidManifist文件中声明 访问网络的权限~

以上就是本篇的全部内容,接下来会为大家继续推出这一系列的博客~

就不给大家截图片了,我这里不是很方便~

Android中异步更新UI的资源,通过Thread,AsyncTask去更新界面的UI,让用户的体验更棒。测试代码地址:

android thread 异步操作_AndroidThread.zip


↑ 上一篇文章:Java基础学习之InputStream的read()方法陷阱 关键词:Java基础学习之InputStream的read()方.. 发布日期:2017/9/12 17:16:49
↓ 下一篇文章:带你飞之Android多线程与异步任务(第二篇) 关键词:Android,多线程,异步任务,第二篇,,androi.. 发布日期:2017/9/12 17:32:27
相关文章:
带你飞之Android多线程与异步任务(第二篇) 关键词:Android,多线程,异步任务,第二篇,,android,thread,Async,异步 发布日期:2017-09-12 17:32
Android,UI主线程与子线程 关键词:Android,UI,主线程,子线程,多线程,Android,thread,工作 发布日期:2017-06-26 15:41
android开发中在文件AndroidManifest.xml中出现警告"Not targeting the latest versions of Android" 关键词:android,警告,开发,文件,AndroidManifest.xml,Not,targeti.. 发布日期:2017-09-20 11:26
相关目录:.NETANDROIDJAVA软件开发
我要评论
正在加载评论信息......