安卓多任务实现的基本原理

41 篇文章 2 订阅
27 篇文章 0 订阅

安卓多任务实现的基本原理

一.基本概念

操作一些耗时操作时候,如I/O读写大文件,数据库操作以及网络下载需要很长时间,为了不阻塞用户界面,出现ANR(应用程序无响应)的响应提示窗口,这个时候我们考虑使用Thread线程来进行解决.

二.Android中的进程和线程

在Android系统中,如果有一个应用程序组件时第一次被启用,而且这时候,应用程序也没有其他的组件来运行,则Android系统会为应用程序创建一个linux的进程,这个Linux进程包含一个线程,称为主线程或者UI线程.

当一个组件在被启动时,如果该process已经存在了,那么该组件就直接通过这个process被启动起来,并且运行在这个process的UI线程中.

1.进程
  • 默认情况下,同一个应用程序内的所有组件都是运行在同一个进程中的,大部分应用程序都是按照这种方式运行的;
  • 在具体应用中,很多时候需要通过在manifest文件中进行设置,通过修改四大组件在Manifest.xml的代码块中的android:proess属性指定组件运行的进程,使其运行在不同的process中。
  • 中的元素也支持android:proess属性,用于指定所有组件的默认进程。
2.进程的重要性层次结构

进程有5中层次,按其重要程度递减分为

1.前台进程(用户当前操作所必须的进程)

  • 拥有一个正在与用户交互的Activity
  • 拥有一个Service,这个Service绑定了某一个正在与用户交互的Activity
  • 拥有一个前台Service
  • 拥有一个Service且它正在执行声明周期回调方法
  • 拥有一个BroadcastReceiver且这个BroadcastReceiver正在执行onRece方法

2.可见进程

  • 没有任何前台组件,但是仍然会影响用户在屏幕上所见内容的进程
  • 拥有一个可见但是不可与用户交互的Activity
  • 拥有一个Service,这个Service绑定了一个可见但是不可与用户进行交互的Activity

3.服务进程

  • 由startService()方法启动的Service进程,虽然不直接和所见内容关联,但是会执行一些用户关心的操作,例如后台播放音乐或者下载数据等等
  • 若系统不足以维持前台进程和可见进程,才会牺牲服务进程的空间

4.后台进程

  • 包含用户不可见的Activity的进程,这些进程对用户体验没有直接影响,可以随时在任意时间终止它们,以回收内存资源.
  • 系统通过LRU(最近最少使用)列表进行多个后台进程的管理,确保最近使用的Activity最后被终止

5.空进程

  • 进程不含有任何应用组件,该进程主要作用是缓存,以改善在此进程中运行组件的启动时间。
  • 系统会经常终正此类进程
3.线程

Android是单线程模型,我们创建的Service、Activity以及Broadcast均是在一个主线程处理,这里我们可以理解为uI线程。(应用程序是一个默认的单线程单任务程序)

Ul Thread中运行着许多重要的逻辑,如系统事件处理,用户输入事件处理,ul绘制,Service,Alarm等

我们编写的代码穿插在主线程的逻辑中,比如对用户触摸事件的检测和响应,对用户输入的处理,自定义View的绘制等。如果我们插入的代码比价耗时,如网络请求或数据库读取,就会阻塞uI线程其他逻辑的执行,从而导致界面卡顿。

如果卡顿时间超过5秒,系统就会报ANR错误。所以,执行耗时的操作,我们需要另起线程执行。

在新线程执行完耗时的逻辑后,往往需要将结果反馈给界面,进行uI更新。Android的ul toolkit不是线程安全的,不能在非uI线程进行uI的更新
所有对界面的更新必须在uI线程进行

安卓的单线程模式遵从两个原则

  • 1.不要阻塞UI进程
  • 2.不要在UI线程之外访问UI组件

创建线程: 基础操作都在UI线程中运行,耗时操作可以创建新的线程去完成

  • 继承Thread类
  • 实现Runnable接口

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

  • Handle + Thread
  • AsyncTask
  • ThreadPoolExecutor
  • IntentService

————————————————————————————————————————————————————————————————————

三、实现多任务

1.多任务的实现原理
  • 在Android中,我们把除uI线程外的,其他所有的线程都叫做工作线程,也就是说Android只会存在两种线程:UI主线程(ul thread)和工作线程(work thread)
  • 我们把耗时的操作放在工作线程中去做。操作完成后,再通知UI主线程做出相应的响应。
  • 这就需要掌握线程间通信的方式。在Android中提供了两种线程间的通信方式:
    • AsyncTask机制
    • Handler机制
(1)使用AsyncTask
  • AsyncTask是Android框架提供的异步处理的辅助类,它可以实现耗时操作仕具他线程执行,而处理结果在Main线程执行
  • 它屏蔽掉了多线程和Handler的概念,进行了较高程度的封装。
  • 使用AsyncTask的代码很容易被理解,因为他们都有一些具有特定职责的方法,如:预处理的方法onPreExecute,后台执行任务的方法dolnBackground,更新进度的方法publishProgress,返回结果的方法onPostExecute等等
(2)Handle机制
  • Handler机制是通过消息队列进行通信的机制,通过使用Handler,LooperMessageQueue,和Message这几个类协调来完成
    • Handler:在android里负责发送和处理消息,通过它可以实现其他线程与Main线程之间的消息通讯
    • Looper:负责管理线程的消息队列和消息循环
    • MessageQueue:消息队列,先进先出,它的作用是保存有待线程处理的消息
(3)Handler类
  • UI主线程在创建时会被自动创建一个消息队列和消息循环,主线程的Looper通过创建一个Handler对象,对外界提供了访问消息队列的渠道
    • 主线程通过Handler.handleMessage()读取消息队列中的消息
    • 工作线程通过方法发送消息到主线程的消息队列Handler.sendMessage() Handler.post()

————————————————————————————————————————————————————————————————

四、.Android实现多线程的两种操作模式

1Android有两种方式实现多线程操作UI:
  • 第一种是创建新线程Thread,用handler负责线程间的通信和消息。
  • 第二种方式AsyncTask异步执行任务
2.使用Handler实现多任务

在新的线程中调用主线程中的Handler的postXX和sendmessage方法来实现与主线程进行通信

使用post方法实现多任务的主要步骤

  • 创建一个Handler对象;
  • 将要执行的操作卸载线程对象的run方法中;
  • 使用post方法运行线程对象
  • 如果需要循环执行,需要在线程对象的run方法中再次调用post方法
3.Handler机制实现异步任务demo
  • xml文件中添加一个TextView

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".AsynchronousTask.HandlerAsyTaskActivity">
    
        <TextView
            android:id="@+id/textview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="hello"
            android:textAllCaps="false"
            tools:ignore="MissingConstraints" />
    
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
  • java文件 编辑代码

    package com.example.handleractivity.AsynchronousTask;
    
    import androidx.annotation.NonNull;
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.widget.TextView;
    
    import com.example.handleractivity.R;
    
    public class HandlerAsyTaskActivity extends AppCompatActivity {
    
        private final static int TEST = 1;
    
        private TextView textView;
        
        //这是自己创建的Handler,实现异步任务,这里用来更改UI
        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                textView.setText("这是工作线程文本");
            }
        };
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_handler_asy_task);
    
            textView = findViewById(R.id.textview);
    //        textView.setText("这是一段文本");
            new ActivityThread().start();
        }
    
        /*
        这是一个线程不安全的行为
         */
        class ActivityThread extends Thread{
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    //            textView.setText("这是工作线程文本");
    
                //使用handler发送消息至消息队列
                Message message = new Message();
                message.what = TEST;
                mHandler.sendMessage(message);
            }
        }
    }
    

我们设置一个线程,让其休眠3秒钟,然后执行 textView.setText(“这是工作线程文本”)操作,三秒之后程序会崩溃。

原因是: 不能在除了UI线程之外的线程里边进行更改UI的操作。

那怎么才能实现呢?

使用Handler实现异步任务,自己新建一个Handler,然后在工作线程中使用handler来传递信息。handler里面进行更改UI的操作,就可了

五、Handler机制方法调用

1.使用Runnable和Handler实现五秒钟之后出现弹窗
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".HandlerPost.HandlerPostActivity">

    <Button
        android:id="@+id/btnShowToast"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="开启Toast"/>

    <TextView
        android:text="你好"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>
public class HandlerPostActivity extends AppCompatActivity implements View.OnClickListener{

    //写一个Handler
    Handler countHandler = new Handler();

    /*线程1:启动一个Toast显示线程*/
    Runnable mRunToast = new Runnable() {
        @Override
        public void run() {
            Toast.makeText(HandlerPostActivity.this,"hello Toast",Toast.LENGTH_SHORT).show();
        }
    };

    private Button mbtnShowToast;

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

        mbtnShowToast = findViewById(R.id.btnShowToast);
        mbtnShowToast.setOnClickListener(this);
    }

    //添加事件监听
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnShowToast:
                /**
                 * SystemClock.uptimeMillis()表示开机到当前的一个累计时间
                 */
                countHandler.postAtTime(mRunToast, SystemClock.uptimeMillis()+5*1000);
                break;
        }
    }
}
2.开辟一个工作线程配合Handler实现计数器的功能
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".HandlerPost.HandlerPostActivity"
    android:orientation="vertical">

    <Button
        android:id="@+id/btnStart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="启动"/>

    <Button
        android:id="@+id/btnStop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="关闭"/>

    <Button
        android:id="@+id/btnShowToast"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="开启Toast"/>

    <TextView
        android:id="@+id/tvCount"
        android:text="你好"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>
public class HandlerPostActivity extends AppCompatActivity implements View.OnClickListener {

    Handler countHandler = new Handler();
    int count = 0;

    /*线程1:启动一个Toast显示线程*/
    Runnable mRunToast = new Runnable() {
        @Override
        public void run() {
            Toast.makeText(HandlerPostActivity.this, "hello Toast", Toast.LENGTH_SHORT).show();
        }
    };

    /*线程2:文本区计数器线程*/
    Runnable mRunCount = new Runnable() {
        @Override
        public void run() {
            textCount.setText("count: " + String.valueOf(count++));
            //再调用一次,以此形成一个类似于循环的操作,一秒加一次
            countHandler.postDelayed(mRunCount, 1000);
        }
    };

    private Button mbtnShowToast;
    private TextView textCount;

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

        mbtnShowToast = findViewById(R.id.btnShowToast);
        mbtnShowToast.setOnClickListener(this);

        findViewById(R.id.btnStart).setOnClickListener(this);
        findViewById(R.id.btnStop).setOnClickListener(this);
        textCount = findViewById(R.id.tvCount);
    }

    //添加事件监听
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnShowToast:
                /**
                 * SystemClock.uptimeMillis()表示开机到当前的一个累计时间
                 */
                countHandler.postAtTime(mRunToast, SystemClock.uptimeMillis() + 5 * 1000);
                break;
            case R.id.btnStart:
                //表示推迟一秒执行
                countHandler.postDelayed(mRunCount, 1000);
                break;
            case R.id.btnStop:
                //移除回调的过程
                countHandler.removeCallbacks(mRunCount);
                break;
        }
    }
}

运行效果;
在这里插入图片描述

六、AsyncTask实现多任务

  • 使用Handler类来在子线程中更新UI线程总会启动一些匿名的子线程,太多的子线程给系统带来了巨大的负担
  • Andoroid提供了一个工具类AdyncTask,来实现异步执行任务
  • AsyncTask是抽象类,具有三种泛型:Params、Progress、Result
    • **Params: **表示启动任务执行的参数,比如HTTP请求的URL
    • Progress: 表示后台任务执行的百分比
    • **Result: **表示后台执行任务最终返回的结果,比如String,Integer等
  • 通过继承一个AsyncTask类定义一个异步任务类
  • Android提供一个让程序员编写后台操作更为容易和透明AsyncTask,使得后台线程能够在UI主线程外进行处理
  • 使用AsyncTask,不需要自己来写后台线程,无需终结后台线程,只需要创建AsyncTask类,并实现其中的抽象方法以及重写某些方法

下面是一个实例,点击按钮,启动进度条。进度条加载完毕,把“执行完毕”显示在文本上

1.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".AsynchronousTask.AsyncTaskDemo">

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Press it"
        android:textAllCaps="false" />

    <TextView
        android:id="@+id/txt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:textSize="30sp"
        android:text="hello world" />

    <ProgressBar
        android:id="@+id/progressbar"
        style="@style/Widget.AppCompat.ProgressBar.Horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:visibility="invisible"
        android:layout_marginTop="50dp" />


</LinearLayout>
2.java文件
public class AsyncTaskDemo extends AppCompatActivity implements View.OnClickListener {

    private final static String TAG = "AsyncTaskDemo";

    private Button mBtn;
    private ProgressBar progressBar;
    private TextView mTxt;

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

        mBtn = findViewById(R.id.btn);
        mBtn.setOnClickListener(this);

        progressBar = findViewById(R.id.progressbar);
        mTxt = findViewById(R.id.txt);
    }

    @Override
    public void onClick(View v) {
        TimeTickLoad timeTickLoad = new TimeTickLoad();
        timeTickLoad.execute(1000);
    }


    class TimeTickLoad extends AsyncTask<Integer,Integer,String>{
        @Override
        protected void onPreExecute() {
            Log.i(TAG, "onPreExecute");
            progressBar.setVisibility(ProgressBar.VISIBLE);
            super.onPreExecute();
        }

        @Override
        protected void onPostExecute(String s) {
            Log.i(TAG, "onPostExecute");
            super.onPostExecute(s);
            mTxt.setText(s);
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            mTxt.setText("onProgressUpdate");
            Log.i(TAG, "onProgressUpdate");
            //更新进度条
            progressBar.setProgress(values[0]);
            super.onProgressUpdate(values);
        }

        /**
        这个方法是使用AsyncTask必须进行覆写的一个方法,耗时的操作在此方法内执行
        */
        @Override
        protected String doInBackground(Integer... integers) {
            //这里不能撰写UI相关操作
            Log.i(TAG, "hello");
//            mTxt.setText("doInBackground");
            for (int i = 0; i < 10; i++) {
                //执行此操作会自动调用onProgressUpdate()
                publishProgress(i*10);
                try {
                    Thread.sleep(integers[0]);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            return "执行完毕";
        }
    }
}

AsyncTask的周期:

在这里插入图片描述

从打印的日志可以看出,首先运行的是onPreExecute()方法,然后运行的是doInBackground(),最后运行的是onPostExecute

运行效果:
在这里插入图片描述

3.实现步骤
  • 使用execute方法出发异步任务的执行

  • 使用onPreExecute()表示执行预处理 : 在本例中实现绘制一个进度条控件

  • 使用doInBackground()用于执行较为费时的操作:在本例中就是计算进度

    这个方法是AsyncTask的关键,必须进行覆写

  • 使用onProgressUpdate()对进度条控件根据进度值做出具体的响应

  • 使用onPostExecute()可以对后台任务的结果做出处理

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值