2019-04-23 22:49:55 github_37130188 阅读数 353
  • 精通Android多线程视频教程

    通过本课程的学习,让你透彻精通Android多线程编程,课程内容包括: 1.UI线程和非UI线程 2.使用Handler发送post请求 3.使用Handler处理Message消息 4.Handler、Looper、MessageQueue三者的关系 5.UI线程Handler和工作线程Handler 6.使用HandlerThread 7.更新UI的方法 8.使用AsyncTask

    4397 人正在学习 去看看 郭宏志

Android提供了四种常用的操作多线程的方式,分别是:

  1. Handler+Thread
  2. AsyncTask
  3. ThreadPoolExecutor
  4. IntentService

 

Handler + Thread

Android主线程包含一个消息队列(MessageQueue),该消息队列里面可以存入一系列的Message或Runnable对象。通过一个Handler你可以往这个消息队列发送Message或者Runnable对象,并且处理这些对象。

每次你新创建一个Handler对象,它会绑定于创建它的线程(也就是UI线程)以及该线程的消息队列,从这时起,这个handler就会开始把Message或Runnable对象传递到消息队列中,并在它们出队列的时候执行它们。

Handler Thread原理图

Handler(会绑定于创建它的线程(也就是UI线程)以及该线程的消息队列)可以把一个Message对象或者Runnable对象压入到消息队列中,进而在UI线程中获取Message或者执行Runnable对象。

Handler把压入消息队列有两类方式,Post和sendMessage:

优缺点

1. Handler用法简单明了,可以将多个异步任务更新UI的代码放在一起,清晰明了

2. 处理单个异步任务代码略显多

适用范围

  1. 多个异步任务的更新UI

 

 

AsyncTask

AsyncTask是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程。内部通过Handler + Thread原理。

AsyncTask通过一个阻塞队列BlockingQuery<Runnable>存储待执行的任务,利用静态线程池THREAD_POOL_EXECUTOR提供一定数量的线程,默认128个。默认采用串行任务执行器,通过静态串行任务执行器SERIAL_EXECUTOR控制任务串行执行,循环取出任务交给THREAD_POOL_EXECUTOR中的线程执行,执行完一个,再执行下一个。

优缺点

1. 处理单个异步任务简单,可以获取到异步任务的进度

2. 可以通过cancel方法取消还没执行完的AsyncTask

3. 处理多个异步任务代码显得较多

适用范围

单个异步任务的处理

 

ThreadPoolExecutor

ThreadPoolExecutor提供了一组线程池,可以管理多个线程并行执行。这样一方面减少了每个并行任务独自建立线程的开销,另一方面可以管理多个并发线程的公共资源,从而提高了多线程的效率。所以ThreadPoolExecutor比较适合一组任务的执行。Executors利用工厂模式对ThreadPoolExecutor进行了封装,使用起来更加方便。

适用范围

1. 批处理任务

 

IntentService

IntentService继承自Service,是一个经过包装的轻量级的Service,用来接收并处理通过Intent传递的异步请求。客户端通过调用startService(Intent)启动一个IntentService,利用一个work线程依次处理顺序过来的请求,处理完成后自动结束Service。

特点

1. 一个可以处理异步任务的简单Service

2018-07-09 14:50:12 xinzhou201 阅读数 292
  • 精通Android多线程视频教程

    通过本课程的学习,让你透彻精通Android多线程编程,课程内容包括: 1.UI线程和非UI线程 2.使用Handler发送post请求 3.使用Handler处理Message消息 4.Handler、Looper、MessageQueue三者的关系 5.UI线程Handler和工作线程Handler 6.使用HandlerThread 7.更新UI的方法 8.使用AsyncTask

    4397 人正在学习 去看看 郭宏志

怎样在Android面试中聊聊多线程

96 
AssIstne 
2017.02.14 16:27* 字数 1191 阅读 759评论 1

多线程可以说是Android面试的高频问题了, 而多线程涉及的内容非常多, 因此在面试当中往往不知道从何说起, 本文并不是为了科普多线程或者研究多线程的知识, 而是尝试组织语言以便在面试当中更好地忽悠面试官.

语言表达在面试当中虽说很重要, 不过更重要的还是相关知识技能过硬.

假如在一场Android面试当中, 面试官让你聊聊多线程, 你可以试试这样回答.

Android中的线程

在Android当中, 当应用启动的时候, 系统会给应用分配一个进程, 顺便一提, 大部分应用都是单进程的, 不过也可以通过设置来使不同组件运行在不同的进程中, 在创建进程的同时会创建一个线程, 应用的大部分操作都会在这个线程中运行, 所以称为主线程, 同时所有的UI控件相关的操作也要求在这个线程中操作, 所以也称为UI线程.

UI线程和工作线程

因为所有的UI控件的操作都在UI线程中执行, 如果在UI线程中执行耗时操作, 例如网络请求等, 就会阻塞UI线程, 导致系统报ANR(Application Not Response)错误. 因此对于耗时操作需要创建工作线程来执行而不能直接在UI线程中执行. 这样就需要在应用中使用多线程, 但是Android提供的UI工具包并不是线程安全的, 也就是说不能直接在工作线程中访问UI控件, 否则会导致不能预测的问题, 因此需要额外的机制来进行线程交互, 主要是让其他线程可以访问UI线程.

线程交互 - Handler机制

在Android当中, 工作线程主要通过Handler机制来访问UI线程. 当然还有一些封装好的类例如AsyncTask可以使用, 但是本质仍是使用Handler.

Handler机制主要由4部分组成, Looper, 消息队列, 消息类和Handler组成, 其中Looper和消息队列是和线程绑定的, 每个线程只会有一个Looper和一个消息队列, 当Looper启动时, 它会无限循环尝试从消息队列中获取消息实例, 如果没有消息则会阻塞等待. 当Handler发送消息时会把消息实例放入消息队列中, Looper从中取得消息实例然后就会调用Handler的相关方法, 因为Looper是线程绑定的, 如果绑定的是UI线程, 那么此时Handler的方法就会在UI线程中得到执行, 线程间就是这样进行交互的.

java中的线程

而Handler机制的底层实现则是使用java多线程相关的类.

java当中主要使用Thread和Executor来实现多线程. Thread用于直接创建线程, 在Android中也可以直接使用这个类, Looper中就包含一个Thread实例. Executor是一个接口, 大部分java中自带的实现都使用了线程池来管理多线程.

线程池

因为在系统中创建线程是一个比较耗费资源的事, 所以不能频繁创建和释放线程, 因此在效率上考虑通常会使用线程池, 同时也便于线程的管理. Android中的AsyncTask就使用了线程池.

线程安全

另一个在使用多线程时需要注意的是线程安全的问题, 因为同一进程中的线程可以共享内存, 虽然这种方式效率很高, 但是会导致线程干扰和内存一致性的问题.

解决这些问题的主要方法是使用Synchronized关键字来加锁. 基本原理就是线程要对对象进行操作前需要先获取锁, 如果一个线程正在操作某个对象, 那么它就会持有相应的锁, 后来的线程想要操作这个对象, 只能等待前面的线程释放锁之后才有机会获取锁并进行操作.

死锁和活锁

引入锁之后仍有可能出现一些问题, 例如死锁, 饥饿(Starvation)和活锁.

多线程工具包

同时java还提供不少工具来使用多线程, 例如刚刚提到的Executor, 另外常用的还有线程安全的集合, 例如ConcurrentMap, 可以用来避免内存一致性的问题.

如果你是面试官, 你被忽悠到了吗? 欢迎在讨论区说说你的看法 :P

2017-02-26 11:37:59 Hacker_ZhiDian 阅读数 482
  • 精通Android多线程视频教程

    通过本课程的学习,让你透彻精通Android多线程编程,课程内容包括: 1.UI线程和非UI线程 2.使用Handler发送post请求 3.使用Handler处理Message消息 4.Handler、Looper、MessageQueue三者的关系 5.UI线程Handler和工作线程Handler 6.使用HandlerThread 7.更新UI的方法 8.使用AsyncTask

    4397 人正在学习 去看看 郭宏志

在很多编程语言中,线程都是一个重要的组成部分,多线程的支持可以给程序员更加灵活的程序功能实现代码编写方式,线程一般用于处理一些比较耗时的任务(下载文件、复制或者移动文件。。。)。那么Android作为一个最热门的移动操作系统,当然支持多线程编程(严格来说应该是java支持多线程编程,Android使用的是java编程语言)。下面来看一下怎么去使用Android多线程:

Android的线程和java的线程使用的都是相同的语法,如果你熟悉java,那么一定不会感到难,新建一个子线程:

Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
           // 这里加入我们要做的事情的代码
        }
    });
thread.start();

开启一个子线程的标准写法就是这样,在子线程的run方法里面我们可以加入我们想要做的事情的代码逻辑,但是值得注意的是:子线程里面是不可以更新UI的,如果要更新UI必须在UI线程(主线程)中完成。例:

import android.sax.StartElementListener;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private TextView textView = null;
    private Button button = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = (TextView) findViewById(R.id.textView);
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                thread.start();
            }
        });
    }

    Thread thread = new Thread(new Runnable() {
       @Override
       public void run() {
            textView.setText("子线程更新UI测试");
        }
    });
}

布局文件就不贴了,比较简单,一个TextView控件和一个Button按钮控件
如果采用以上写法,程序运行单击按钮的时候会崩溃退出,如图:
这里写图片描述
看看LogCat打印的日志:
这里写图片描述
大致意思就是只有创建了这个View对象的才能够对这个View的UI进行操作(即只有UI线程才能更新UI)。那么我们怎么才能通过子线程来更新UI呢?直接更新肯定是不行的,Android为我们提供了一个类:Handler,这个类可以对子线程发出的消息进行处理,那么我们就能通过将Handler类对象定义在主线程中然后对子线程发来的消息进行处理(更新UI)来达到子线程更新UI的目的,我们仍然以上面那个例子来看,我们把MainActivity.java改一下:


import android.os.Handler;
import android.os.Message;
import android.sax.StartElementListener;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private static final int UPDATE_UI = 1;
    private TextView textView = null;
    private Button button = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = (TextView) findViewById(R.id.textView);
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                thread.start();
            }
        });
    }

    /*
     * 新建一个Handler 对象,重写handleMessage方法用于处理 子线程发送过来的消息
     */
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            /*
             * 对发送过来的消息进行处理
             */
            switch (msg.what) {
                case UPDATE_UI:
                    textView.setText("子线程更新UI测试");
                    break;
            }
        }
    };

    Thread thread = new Thread(new Runnable() {
       @Override
       public void run() {
           /*
            * 新建一个 Message 对象做为消息发送给Handler对象去处理
            */
           Message message = new Message();
           message.what = UPDATE_UI;
           handler.sendMessage(message);
        }
    });
}

可以看到,我们在MainActivity.java里面加了一个新的全局对象:handler,这个对象就是用于处理子线程发送过来的消息的(在handleMessage方法里面)。而在子线程中,我们加了几行代码: 其实就是发送消息的代码,将消息发送到消息队列然后让Handler对象去处理。ok,再运行一下:
这里写图片描述
成功更新了UI,而且并没有报错。这就是典型的异步通信的例子:子线程执行的结果返回给主线程然后由主线程进行对应的处理。我们来看一下这里面涉及到的一些东西:

Message:其实就是一个普通的类,保存了一些数据,可以储存少量信息数据,在不同的线程中作为“通信者”的角色,可以用于不同的线程交换数据。

Handler:处理者的意思,它本身也用于在子线程中发送消息进入消息队列中,它也对对消息队列中通过它自己发送的Message对象取出并且进行一些处理(通过handleMessage方法),对于Handler对象来说,它不分线程,只要是它发送的消息,它都会接收到

消息队列(MessageQueue):储存通过Handler对象发送的消息,这些消息在被Handler对象取出处理之前会一直存在消息队列中,每个线程只会有一个MessageQueue对象,Android已经帮我们封装起来了,我们并不需要管它

Looper:管理每个线程中的MessageQueue,负责把消息队列中的数据取出,然后交给Handler对象去处理这条消息,每个线程也只会有一个Looper对象

整个过程:
这里写图片描述
画的不好,理解原理就行了。

其实为了方便我们在子线程更新UI操作,Android提供了一个更加好用的类:AsyncTask,下面来看一下这个类的用法:
首先,它是一个抽象类,我们必须继承它,并且要为它提供3个泛型参数,一般的写法:

public class MyAsyncTask extends AsyncTask<Void, Integer, Boolean> {
    ....
}

之后必须重写里面的一个抽象方法:doInBackground方法,这个方法即为进行我们要执行的耗时操作的方法,我们在这个方法里面进行我们需要的耗时操作,其实,我们一般会重写里面的4个方法:

onPreExecute() : 这个方法会在任务(doInBackground方法)开始之前调用,用于一些初始化的操作

doInBackground(Void…) : 这个方法就是在后台进行的耗时操作的方法,里面的所有代码都会在Android新建的一个子线程中运行,并且这个方法不可以进行UI操作(这个方法是在子线程中执行的),我们可以调用publishProgress(Intger…)方法来调用专门的UI更新方法来进行UI的更新。这个方法的返回值会传递给onPostExecute方法用于收尾

onProgressUpdate(Interge…) : 这个方法里进行UI的更新,当在doInBackground方法中调用了publishProgress方法之后,就会调用这个方法来及时的进行UI更新

onPostExecute(Boolean result) : 这个方法用于收尾,当doInBackground方法执行完成之后就会调用这个方法,主要是对于操作进行判断是否成功…

可以看到,我们在继承AsyncTask传入的三个泛型参数:一个参数类型是doInBackGround方法的参数类型,第二个参数的参数类型是onProgressUpdate方法的参数类型,(这里传入Integer用于进度的UI更新),第三个参数的参数类型为onPostExecute方法的参数类型,并且也是doInBackground方法返回值类型(这里传入Boolean用于判断执行的结果),当然,我们可以根据自己的需要来传入对应的参数类型。那么一个基本的自定义AsyncTask就成了这样:

import android.os.AsyncTask;

/**
 * Created by Administrator on 2017/2/26.
 */

public class MyAsyncTask extends AsyncTask<Void, Integer, Boolean> {

    @Override
    public void onPreExecute() {

    }

    @Override
    protected Boolean doInBackground(Void... params) {
        int percent = 0;
        try {
            while (true) {
                // 执行一些操作,
                publishProgress(percent); // 调用onProgressUpdate方法更新UI
                if (percent >= 100) {
                    return true;
                }
            }
        } catch(Exception e) {
            return false;
        }
    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
        // Integer... progress 这种形式的写法相当于一个int类型的数组,其他同理
        // 在这里更新UI
    }

    @Override
    protected void onPostExecute(Boolean result) {
        // 这里做一些操作的结果提示
    }
}

OK,如果我们要调用这个类的对象,我们只需编写:

MyAsyncTask myAsyncTask = new MyAsyncTask();
myAsyncTask.execute();

因为我们定义的MyAsyncTask中第一个参数为Void,所以这里调用execute方法不需要传入参数,但是如果换成别的类型那就需要了,这里根据具体需求来运用。 接下来仍然以上面那个例子,我们用AsyncTask来实现它:
在原来的工程基础上新建一个类MyAsyncTask.java:

import android.content.Context;
import android.os.AsyncTask;
import android.widget.TextView;
import android.widget.Toast;

import org.w3c.dom.Text;

/**
 * Created by Administrator on 2017/2/26.
 */

public class MyAsyncTask extends AsyncTask<Void, Integer, Boolean> {

    private Context myContext = null;
    private TextView myTextView = null;
    private String string;

    public MyAsyncTask(Context context, TextView textView, String str) {
        myContext = context;
        myTextView = textView;
        string = str;
    }

    @Override
    protected Boolean doInBackground(Void... params) {
        Log.i("ThreadTest", "MyAsyncTask thread: " + Thread.currentThread().getId()); // 打印出当前线程的 id 信息
        int percent = 100;
        try {
            publishProgress(percent);
            return true;
        } catch(Exception e) {
            return false;
        }
    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
        // Integer... progress 这种形式的写法相当于一个int类型的数组,其他同理
        // 在这里更新UI
        if(100 == progress[0]) {
            myTextView.setText(string);
        }
    }

    @Override
    protected void onPostExecute(Boolean result) {
        // 这里做一些操作的结果提示
        String str = "操作";
        if(result) {
            str += "完成";
        } else {
            str += "失败";
        }
        Toast.makeText(myContext, str, Toast.LENGTH_SHORT).show();
    }
}

我们还要对MainActivity.java进行小小的改动:

这里写图片描述

就是将按钮的点击事件成了执行MyAsyncTask对象中的方法,并且用一个LogCat信息打印出 MainActivity 所在的线程 Id,对于MyAsyncTask来说,基本的和上面的用法差不多,只不过用AsyncTask来进行这么简单的处理实在是有点大材小用,下面看结果:

这里写图片描述

开始运行的界面和上面没多大区别,点击按钮之后出现操作成功的提示并且TextView的文字也更新了。并且LogCat 中打印的信息中我们可以发现 MyAsyncTask 和 MainActivity 确实不是在同一个线程之中(MyAsyncTask 的 doInBackground 方法在子线程中执行), 那么一个AsyncTask的简单用法就完成了。

如果博客中有什么不正确的地方还请多多指点。
谢谢观看。。。

2017-08-21 16:38:42 geek_waj 阅读数 104
  • 精通Android多线程视频教程

    通过本课程的学习,让你透彻精通Android多线程编程,课程内容包括: 1.UI线程和非UI线程 2.使用Handler发送post请求 3.使用Handler处理Message消息 4.Handler、Looper、MessageQueue三者的关系 5.UI线程Handler和工作线程Handler 6.使用HandlerThread 7.更新UI的方法 8.使用AsyncTask

    4397 人正在学习 去看看 郭宏志
(3/12)保证APP流畅的关键因素——多线程_Android中的多线程
2019-05-22 21:41:54 jinguo504 阅读数 148
  • 精通Android多线程视频教程

    通过本课程的学习,让你透彻精通Android多线程编程,课程内容包括: 1.UI线程和非UI线程 2.使用Handler发送post请求 3.使用Handler处理Message消息 4.Handler、Looper、MessageQueue三者的关系 5.UI线程Handler和工作线程Handler 6.使用HandlerThread 7.更新UI的方法 8.使用AsyncTask

    4397 人正在学习 去看看 郭宏志

前言

  • 多线程的应用在Android开发中是非常常见的,常用方法主要有:

目录

1. 多线程基础知识

  • 在了解Android多线程实现方式前,需了解一些多线程基础知识,如线程、进程等

  • 具体请看文章:Android多线程:你必须要了解的多线程基础知识汇总

2. Android多线程实现方式

Android多线程实现方式包括:

3. 基础使用

Android多线程实现的基础使用包括:

  • 继承Thread类

  • 实现Runnable接口

  • Handler

3.1 继承Thread类

  • 简介

3.2 实现Runnable接口

  • 简介

3.3 Handler

  • 简介

4. 复合使用

Android多线程实现的复合使用包括:

  • AsyncTask

  • HandlerThread

  • IntentService

称为”复用“的主要原因是:这3种方式的本质原理都是Android多线程基础实现(继承Thread类、实现Runnable接口、Handler)的组合实现。下面,我将详细讲解。

4.1 AsyncTask

  • 简介

4.2 HandlerThread

  • 简介

4.3 IntentService

  • 简介

5. 高级使用

Android多线程的高级使用主要是线程池(ThreadPool)。

5.1 简介

5.2 具体使用 & 工作原理

Android多线程:线程池ThreadPool 全面解析

 

6. 对比

下面,将对比各种多线程实现方式,包括原理、功能 & 应用场景。

7. 其他

7.1 线程同步:Synchronized关键字

7.2 线程变量:ThreadLocal

Android开发-多线程

阅读数 525

android 多线程 AsyncTask

博文 来自: qq_33248299

Android多线程

阅读数 209

Android中的多线程

阅读数 1172

没有更多推荐了,返回首页