一.基本概念
1.操作一些耗时的操作时,如I/O读写大文件,数据库操作以及网络下载需要很长的时间,为了不堵塞用户界面,出现ANR的响应提示窗口,这个时候我们考虑使用Thread线程来解决。
2.Android中的进程和线程:
在android系统之中,如果有一个应用程序组件是第一次被启动,而这个时候应用程序也没有其他的组件在运行,则Android系统会为应用程序创建一个linux进程(Linux Process),这个Linux进程包含一个线程(Tread),称之为主线程(MainThread)或UI线程(UI Thread)
当一个组件在启动的时候,如果该Process已经存在,那么该组件就直接通过这个process被启动起来,并且运行在这个process的UI Thread之中。
3.进程
默认情况下,同一个应用程序内的所有组件都是运行在同一个进程之中的,大部分应用程序都是按照这种的方式运行的。
在具体的应用中,很多的时候需要通过在manifest文件中进行设置,通过修改四大组件在Manifest.xml中的代码块(<activity><service><provider><receiver>)中的Android:process属性指定组件运行的进程,使其运行在不同的process中。
<application>元素也支持android:process属性,用于指定所有组件的默认进程。
4.进程的重要性的层次结构
进程有5中层次,按其重要的程度递减分为
(1)前台进程(正在和用户交互)
(2)可见进程(没有任何的前台组件,但仍然会影响用户在屏幕上所见内容的进程)
(3)服务进程(虽然不直接和用户所见的内容关联,但是会执行一些用户关心的操作,若系统不足以维持前台进程和可见进程,才会牺牲服务进程空间)
(4)后台进程(包含用户不可见的Activity的进程,这些进程对用户体验没有直接的影响,可以随时的在任意的时间终止他们,以回收内存资源),系统通过LRU(最近最少使用)列表进行多个后台进程的管理,确保最近使用的Activity会后被终止。
(5)空进程(进程不含有任何的应用组件,该进程主要的作用是缓存,以改善在此进程中运行组件的启动时间,系统会经常的终结此类的进程)
5.线程
Android 是单线程模型,我们创建的Service,Activity以及Broadcast均是在一个主线程处理,这里我们可以理解为一个UI线程。(应用程序是一个默认的单线程单任务程序),也就是说默认一个主线程控制所有的交互。我们编写的代码穿插在主线程的逻辑之中,比如对用户触摸事件的检测和相应,对用户输入的处理,自定义View的绘制等,如果我们插入的代码比较耗时,如网络请求和数据库的读取,就会阻塞UI线程其他的逻辑的执行,从而导致界面的卡顿。如果卡顿的时间超过5秒,系统就会报ANR错误,所以执行耗时的操作的时候,我们需要另起线程执行。
在新线程执行完耗时的逻辑后,往往需要将结果反馈给界面,进行UI更新,Android的UI toolkit不是线程安全的,不可以在非UI线程进行UI的更新,所有对界面的更新必须在UI线程中进行、
1.Android 的单线程模式遵循两个原则
(1).不要阻塞UI线程
(2).不要在UI线程外访问UI组件
线程
创建线程:基础操作都在UI线程中运行,耗时的操作可以创建新的线程去完成
继承Thread类
实现Runnable接口
Android提供了四种常用的操作多线程的方式,分别是:
1.Handler+Thread
2.AsyncTask
3.ThreadPoolExecutor
4.IntentService
二.实现多任务
在Android中,我们把除了UI线程外的,其他所有的线程都叫工作线程,也就是说Android只会存在两种线程:UI主线程()和工作线程(workthread)
我们把耗时的操作放在工作线程中做,操作完成后再通知UI主线程去做相应的响应。
这就需要掌握线程之间通信的方式,在Android中提供了两种线程之间的通信方式;
AsyncTask机制(AsyncTask异步执行任务)
Handler机制(创建新线程Thread,用handler负责线程间的通信和消息)
1.使用AsyncTask
AsyncTask是android框架提供的异步处理的辅助类,他可以实现耗时操作在其他的线程执行,而处理结果在Main线程执行。
他屏蔽掉了多线程和Handler的概念,进行了较高程度的封装。
使用AsyncTask的代码很容易被理解,因为他们都具有一些特定职责的方法,如预处理的方法onPreExecute,后台执行任务的方法dolnBackground,更新进度的方法publishProgress,返回结果的方法onPostExecute等等。
2.Handler机制
Handler机制是通过消息队列进行通信机制的,通过使用Handler,Looper,MessageQueue,和Message这几个类协调来完成。
(1)Handler:在android里负责发送和处理消息,通过它可以实现其他的线程与Main线程之间的消息通信。UI主线程在创建的时候会自动的创建一个消息对列和消息循环。主线程的Looper通过创建一个Handler对象,对外界提供了访问消息对列的渠道。
通过Handler.handleMessage()读取消息队列中的消息,在新的线程中调用主线程的Handler的postXXX和sendmessage方法来实现与主线程之间的通信。
使用post方法实现多任务的主要步骤如下;
(1)创建一个Handler对象
(2)将要执行的操作写在线程对象的run方法中。
(3)使用post方法运行线程对象
(4)如需循环执行,如果在线程对象的run方法中再次调用post方法。
详细的步骤如下
工作线程通过方法发送信息到主线程的消息队列。
一个例子
package com.example.mynotification;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
final static String TaG="HANDLER_TEST";
TextView txt=null;
Handler handler=new Handler(){
public void handleMessage(Message message ){
Log.i(MainActivity.TaG,"onCreate:the main Tread id:"+Thread.currentThread().getId());
txt.setText("这是工作线程文本");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt=findViewById(R.id.hello);
Log.i(TaG,"onCreate:the main Tread id:"+Thread.currentThread().getId());
// txt.setText("这是一段文本");
new ActivityThread().start();
}
class ActivityThread extends Thread{
public void run(){
Log.i(TaG,"onCreate:the work Tread id:"+Thread.currentThread().getId());
handler.sendMessage(new Message());
}
}
}
下面再举一个Handler和Runnable一起实现一个延迟显示的例子
package com.example.handlerpostdemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Handler countHandler=new Handler();
Runnable mRunToast=new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,"hello toast",Toast.LENGTH_SHORT).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((Button)findViewById(R.id.btnShowToast)).setOnClickListener(this);
}
public void onClick(View view){
switch (view.getId()){
case R.id.btnShowToast:
countHandler.postAtTime(mRunToast, SystemClock.uptimeMillis()+5*1000);
break;
}
}
}
可以运行试一下
(2)Looper:负责管理线程的消息队列和消息循环
Looper.myLooper();得到当前线程的Looper对象
Looper.getMainLooper();可以获得当前的进程的主线程的Looper
Looper.prepare();创建消息队列
Looper.loop();进入消息循环。
(3)Message:线程之间通信的消息载体,Message充当消息封装的功能,里面可以存放任何想要传递的消息。
(4)MessageQueue:消息队列先进先出,它的作用是保存有待线程处理的消息。
三.理解服务