欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android如何理解和使用线程与进程(异步消息处理)

程序员文章站 2022-05-01 19:18:13
进程和线程模型异步消息处理HandlerAsyncTask线程线程的基本用法更新UI异步消息处理HandlerMessageMessage是在线程之间传递的消息,它可以携带少量的信息,用于在不同线程之间交换数据HandlerHandler主要用于发送和处理数据使用Handler的sendMessage()方法,通过处理之后,最终会传递到Handler的handleMessage()中MessageQueueMessageQueue消息队列,主要用于存放所有通过Handler发送...



我们都知道,在操作系统中进程是OS分配资源的最小单位,而线程是执行任务的最小单位。一个进程可以拥有多个线程执行任务,这些线程可以共享该进程分配到的资源。当我们的app启动运行后,在该app没有其他组件正在运行的前提下,Android系统会启动一个新Linux进程来运行app,这个进程只包含了一个线程在运行。在默认情况下,app的组件都运行在该进程中,最初就包含的这个线程也被称为主线程或者UI线程。如果我们启动该app的时候,系统中已经有一个进程在运行该app的组件,那么该app也会在该进程中运行。当然,我们也可以让app中不同的组件运行在不同的进程中,也可以在任意进程中新开线程执行任务。



进程

默认情况下,同一应用的所有组件均在相同的进程中运行,且大多数应用都不应改变这一点。但是,如果我们发现需要控制某个组件所属的进程,则可在清单文件中执行此操作。

各类组件元素(< activity >、< service >、< receiver > 和 < provider >)的清单文件条目均支持 android:process 属性,此属性可指定该组件应在哪个进程中运行。您可以设置此属性,使每个组件均在各自的进程中运行,或者使某些组件共享一个进程,而其他组件则不共享。您也可设置 android:process,以便不同应用的组件在同一进程中运行,但前提是这些应用共享相同的 Linux 用户 ID 并使用相同的证书进行签署。

此外, 元素还支持 android:process 属性,用来设置适用于所有组件的默认值。

当内存不足,而其他更急于为用户提供服务的进程又需要内存时,Android 可能会决定在某一时刻关闭某个进程。正因如此,系统会销毁在被终止进程中运行的应用组件。当这些组件需再次运行时,系统将为其重启进程。

决定终止哪个进程时,Android 系统会权衡其对用户的相对重要性。例如,相较于托管可见 Activity 的进程而言,系统更有可能关闭托管屏幕上不再可见的 Activity 的进程。因此,是否终止某个进程的决定取决于该进程中所运行组件的状态。Android 会根据每个进程中运行的组件以及这些组件的状态,将它们放入“重要性层次结构”。这些进程类型包括(按重要性排序):

重要性层次结构

  1. 前台进程
    正在与用户进行交互的进程,当以下任何一个条件成立时,该进程被认为是前台进程:
  • 持有一个用户正在与之交互的 Activity(Activity 对象的 onResume() 方法已被调用)。
  • 持有一个服务(Service),且该服务已被绑定到一个正在与用户交互的 Activity 上。
  • 持有一个服务,且该服务在前台运行,即该服务 startForground() 调用。
  • 持有一个服务,且该服务正在执行其生命周期的回调方法(onCreate()、onStart()、onDestroy())。
  • 持有一个 BroadcastReceiver,且其正在执行 onReceive() 方法。
    通常,在一个给定的时间内,只有很少的前台进程存在。当系统内存匮乏,以至于它们不能全部继续运行时,它们会依序被清除。通常,这时设备已经到了内存分页状态(memory paging state),来清除那些前台进程以确保用户响应。
  1. 可见进程
    一个可视进程是没有前台组件的,但仍会影响用户在屏幕上所见内容的进程。当以下任何一个条件成立时,该进程被认为是可视进程:
  • 它正在用户的互动屏幕上运行一个 Activity(其 onResume() 方法已被调用)。
  • 它有一个 BroadcastReceiver 目前正在运行(其 BroadcastReceiver.onReceive() 方法正在执行)。
  • 它有一个 Service 目前正在执行其某个回调(Service.onCreate()、Service.onStart() 或 Service.onDestroy())中的代码。
    相比前台进程,系统中运行的这些进程数量较不受限制,但仍相对受控。这些进程被认为非常重要,除非系统为了使所有前台进程保持运行而需要终止它们,否则不会这么做
  1. 服务进程
    包含一个已使用 startService() 方法启动的 Service。虽然用户无法直接看到这些进程,但它们通常正在执行用户关心的任务(例如后台网络数据上传或下载),因此系统会始终使此类进程保持运行,除非没有足够的内存来保留所有前台和可见进程。
  2. 缓存进程
    目前不需要的进程,因此,如果其他地方需要内存,系统可以根据需要*地终止该进程。在正常运行的系统中,这些是内存管理中涉及的唯一进程:运行良好的系统将始终有多个缓存进程可用(为了更高效地切换应用),并根据需要定期终止最早的进程。只有在非常危急(且具有不良影响)的情况下,系统中的所有缓存进程才会被终止,此时系统必须开始终止服务进程。
    这些进程通常包含用户当前不可见的一个或多个 Activity 实例(onStop() 方法已被调用并返回)。只要它们正确实现其 Activity 生命周期(详情请见 Activity),那么当系统终止此类流程时,就不会影响用户返回该应用时的体验,因为当关联的 Activity 在新的进程中重新创建时,它可以恢复之前保存的状态。

线程

当我们需要执行一些耗时操作,比如说发起网络请求时,考虑到网速等其他原因,服务器未必会立刻响应我们的请求,如果不将这类操作放在子线程里去运行,就会导致主线程被阻塞住,从而影响用户对软件的正常使用
系统不会为每个组件实例创建单独的线程。在同一进程中运行的所有组件均在界面线程中进行实例化,并且对每个组件的系统调用均由该线程进行分派。

线程的基本用法

  • 新建线程类
    新建一个类,继承于Thread,重写父类的run()方法
 // 新建MyThread继承于Thread class MyThread extends Thread { @Override public void run(){ // 事务逻辑 } } // 在主线程里new出实例,调用start方法 new MyThread().start(); 
  • 实现Runnable接口
 // 创建一个实现了Runnable接口的类 class MyThread implements Runnable { @Override public void run(){ // 事务逻辑 } } // 在主线程里new出实现了Runnable接口的对象,传给Thread的构造函数中 MyThread myThread = new MyThread(); new Thread(myThread).start(); 
  • 匿名类
 new Thread(new Runnable(){ @Override public void run(){ // 事务逻辑 } }) 

更新UI

Android的UI是不安全的,我们只能在主线程中更新应用程序中的UI
Android 界面工具包并非线程安全工具包。所以您不得通过工作线程操纵界面,而只能通过界面线程操纵界面。因此,Android 的单线程模式必须遵守两条规则:

  1. 不要阻塞 UI 线程
  2. 不要在 UI 线程之外访问 Android UI 工具包

线程安全方法

在某些情况下,系统可能会从多个线程调用您实现的方法,因此编写这些方法时必须确保其满足线程安全的要求。
这一点主要适用于可以远程调用的方法,如绑定服务中的方法。如果对 IBinder 中所实现方法的调用源自运行 IBinder 的同一进程,则系统会在调用方的线程中执行该方法。但是,如果调用源自其他进程,则系统会选择线程池中的某个线程,并在此线程中(而不是在进程的界面线程中)执行该方法,线程池由系统在与 IBinder 相同的进程中进行维护。例如,即使服务的 onBind() 方法通过服务进程的界面线程调用,在 onBind() 所返回对象中实现的方法(例如,实现 RPC 方法的子类)仍会通过线程池中的线程调用。由于服务可以有多个客户端,因此多个池线程可同时使用相同的 IBinder 方法。因此,IBinder 方法必须实现为线程安全方法。


异步消息处理

Handler

  • Message
    Message是在线程之间传递的消息,它可以携带少量的信息,用于在不同线程之间交换数据

  • Handler
    Handler主要用于发送和处理数据
    使用Handler的sendMessage()方法,通过处理之后,最终会传递到Handler的handleMessage()中

  • MessageQueue
    MessageQueue消息队列,主要用于存放所有通过Handler发送的消息。这一部分消息会一直存放于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象

  • Looper
    Looper是每个线程中MessageQueue的管家,调用Looper的loop()方法后,就会进入无限循环,每当发现MessageQueue中有消息时,就将它取出,传递到Handler的handleMessage()中

异步流程:

  • 主线程中创建一个Handler对象,并重写handleMessage()方法
  • 子线程创建Message对象,通过Handler将这条消息发送出去
  • MessageQueue队列中收到该Message
  • Looper一直尝试从MessageQueue中取出数据并发送到handleMessage()方法中
    Android如何理解和使用线程与进程(异步消息处理)
    大佬总结的Handler工作原理
    Handler的例子:
    MainActivity:
package com.example.handlertest; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.View; import android.widget.Button; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; public static final int CREATE_THREAD = 1; public static final int SEND_MESSAGE = 2; public static final int CALL_THREAD_TO_SEND_MESSAGE = 3; public static final int GET_MESSAGE = 4; private MyThread myThread; private Handler handler = new Handler() { public void handleMessage(Message msg){ Log.d(TAG, msg.what + ""); switch (msg.what){ case GET_MESSAGE: Log.d(TAG, "主线程接受消息成功"); break; default: break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button createButton = (Button) findViewById(R.id.create_thread); Button sendButton = (Button) findViewById(R.id.to_thread); Button getButton = (Button) findViewById(R.id.from_thread); createButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { createThread(); } }); sendButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { sendMessageToThread(); } }); getButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { getMessageFromThread(); } }); } // 新建一个子线程 private void createThread(){ myThread = new MyThread(); myThread.start(); } // 向子线程发送一条消息 private void sendMessageToThread(){ Log.d(TAG, "向子线程发送一条消息"); Message message = new Message(); message.what = SEND_MESSAGE; myThread.handler.sendMessage(message); } // 子线程向父线程发送消息 private void getMessageFromThread(){ Message message = new Message(); message.what = CALL_THREAD_TO_SEND_MESSAGE; Bundle bundle = new Bundle(); ArrayList arr = new ArrayList(); arr.add(handler); bundle.putStringArrayList("data", arr); message.setData(bundle); myThread.handler.sendMessage(message); } } } 

MyThread:

package com.example.handlertest; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; public class MyThread extends Thread { private static final String TAG = "MyThread"; public static final int CREATE_THREAD = 1; public static final int SEND_MESSAGE = 2; public static final int CALL_THREAD_TO_SEND_MESSAGE = 3; public static final int GET_MESSAGE = 4; public Handler handler = new Handler() { public void handleMessage(Message msg) { Log.d(TAG, msg.what + ""); switch (msg.what){ case CREATE_THREAD: Log.d(TAG, "创建线程成功"); break; case SEND_MESSAGE: Log.d(TAG, "主线程发送消息成功"); break; case CALL_THREAD_TO_SEND_MESSAGE: Log.d(TAG, "子线程发送消息成功"); // 获取数据 ArrayList data = msg.getData().getParcelableArrayList("data"); Handler mainHandler = (Handler) data.get(0); Message message = new Message(); message.what = GET_MESSAGE; mainHandler.sendMessage(message); break; default: break; } } }; @Override public void run() { } } 

XML

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/create_thread" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="新建线程" /> <Button android:id="@+id/to_thread" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="向子线程发消息" /> <Button android:id="@+id/from_thread" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="子线程向主线程发送消息" /> </LinearLayout> 

子线程向主线程发消息的方式:

  1. 创建子线程时,主线程Handler以参数形式在子线程构造函数中定义
public class MyThread extends Thread { public MyThread(Handler handler){ mainHandler = handler; } @Override public void run() { Message message = new Message(); message.what = GET_MESSAGE; mainHandler.sendMessage(message); } } 
  1. 子线程获取主线程Looper,直接定义主线程需要完成的操作:
public class MyThread extends Thread { @Override public void run() { Handler Xhandler = new Handler(Looper.getMainLooper()){ @Override public void handleMessage(@NonNull Message msg) { Log.d(TAG, "11111"); } }; Message message = new Message(); mainHandler.sendMessage(message); } 
  1. 子线程构造Handler,主线程通过Message传递主线程的Handler。也就是前面的例子,第一次写的时候脑抽了,傻兮兮的整得很复杂,其实一个构造函数就行了

AsyncTask

实现原理也是基于异步消息处理机制
在doInBackground()方法中执行具体的耗时任务,在onProgressUpdate()方法中进行UI操作,在onPostExcute()方法中执行一些任务的收尾工作
总的来说,AsyncTask是对Thread+Handler良好的封装

  • execute(Params… params)
    执行一个异步任务,需要我们在代码中调用此方法,触发异步任务的执行

  • onPreExecute()
    后台任务开始执行前调用,用于进行一些界面上的初始化操作

  • doInBackground()
    此方法中所有代码都会在子线程中运行,应在此方法中执行所有的耗时任务。如果要反馈当前任务的执行进度,可以调用publiProgress(Progress…)方法来完成

  • onProgressUpdate(Progress)
    当后台任务调用publiProgress(Progress…)时,此方法很快被调用,在此方法中可以对UI进行更新

  • onPostExecute(Result)
    后台任务完成并通过return语句进行返回时,此方法执行

在使用的时候,有几点需要格外注意:

  • 异步任务的实例必须在UI线程中创建。
  • execute(Params… params)方法必须在UI线程中调用。
  • 不要手动调用onPreExecute(),doInBackground(Params…params),onProgressUpdate(Progress… values),onPostExecute(Result result)这几个方法。
  • 不能在doInBackground(Params… params)中更改UI组件的信息。
  • 一个任务实例只能执行一次,如果执行第二次将会抛出异常。

让我们用一个实际例子来学习AsyncTask,该代码修改于另一个大佬的代码

package com.example.asynctasktest; import androidx.appcompat.app.AppCompatActivity; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private static final String TAG = "ASYNC_TASK"; private Button execute; private Button cancel; private ProgressBar progressBar; private TextView textView; private MyTask mTask; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 组件绑定 execute = (Button) findViewById(R.id.execute); execute.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //注意每次需new一个实例,新建的任务只能执行一次,否则会出现异常 mTask = new MyTask(); mTask.execute(); execute.setEnabled(false); cancel.setEnabled(true); } }); cancel = (Button) findViewById(R.id.cancel); cancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //取消一个正在执行的任务,onCancelled方法将会被调用 mTask.cancel(true); } }); progressBar = (ProgressBar) findViewById(R.id.progress_bar); textView = (TextView) findViewById(R.id.text_view); } private class MyTask extends AsyncTask<String, Integer, String> { //onPreExecute方法用于在执行后台任务前做一些UI操作 @Override protected void onPreExecute() { Log.i(TAG, "onPreExecute() called"); textView.setText("loading..."); } //doInBackground方法内部执行后台任务,不可在此方法内修改UI @Override protected String doInBackground(String... params) { Log.i(TAG, "doInBackground(Params... params) called"); int time = 0; // 用一个循环去模拟下载的过程 try { while(time < 100){ publishProgress(time); time++; Thread.sleep(100); } } catch (InterruptedException e) { e.printStackTrace(); } return "下载完成"; } //onProgressUpdate方法用于更新进度信息 @Override protected void onProgressUpdate(Integer... progresses) { Log.i(TAG, "onProgressUpdate(Progress... progresses) called"); progressBar.setProgress(progresses[0]); textView.setText("loading..." + progresses[0] + "%"); } //onPostExecute方法用于在执行完后台任务后更新UI,显示结果 @Override protected void onPostExecute(String result) { Log.i(TAG, "onPostExecute(Result result) called"); textView.setText(result); execute.setEnabled(true); cancel.setEnabled(false); } //onCancelled方法用于在取消执行中的任务时更改UI @Override protected void onCancelled() { Log.i(TAG, "onCancelled() called"); textView.setText("cancelled"); progressBar.setProgress(0); execute.setEnabled(true); cancel.setEnabled(false); } } } 

XML文件如下:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/execute" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="execute"/> <Button android:id="@+id/cancel" android:layout_width="fill_parent" android:layout_height="wrap_content" android:enabled="false" android:text="cancel"/> <ProgressBar android:id="@+id/progress_bar" android:layout_width="fill_parent" android:layout_height="wrap_content" android:progress="0" android:max="100" style="?android:attr/progressBarStyleHorizontal"/> <ScrollView android:layout_width="fill_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/text_view" android:layout_width="fill_parent" android:layout_height="wrap_content"/> </ScrollView> </LinearLayout> 

本文地址:https://blog.csdn.net/woshijuzizi/article/details/107730802

相关标签: Android学习笔记