Android-进程和线程

简介:

当一个应用(application)组件首次启动时,Android系统会为这个应用启动一个新的进程(process),并为之开启一个单独的线程(thread);如果一个应用组件启动时它所再的应用已经创建了一个进程,则它直接运行在这个进程中。默认情况下,一个应用所以的组件(Activity/Service/BroadCastReceiver/ContentProvider)都运行在一个同一个进程的同一个线程中(main thread)。当然,也可以指定你的应用组件运行在单独的进程中,你也可以在你的进程中创建多个线程。

进程


默认情况下,应用程序的所有组件都运行在相同的进程中,通常情况下,我们也不需要去改变它们。当然,如你需要,你可以去通过修改 manifest 文件的配置,为你的应用组件指定特定的进程

 manifest 文件中四大组件节点<activity><service><receiver>, 和 <provider>—都支持 android:process 这个属性来指定当前组件特有的进程。于是可能应用组件之间可能就会出现如下关系:

  • 几个应用横向组件运行在单独的进程中
  • 同一个应用的不同组件运行在相同的进程中(共享进程)
  • 不同应用的组件运行在同一个进程中。(当然这个需要两个应用共享 Linux user id 并且提供相同的签名认证)

在某些情况下 Android 会关闭的进程,例如,在 low memory 情况下,其他优先级更高的应用进程(例如电话)起来需要更多内存服务用户时,系统会杀掉那些不重要的进程,同时,依附在这个进程中的应用组件将会被销毁释放。当然,用户再次唤醒它的时候进程会重新启动。

Android 决定关掉哪个进程,时通过权衡进程对用户的重要性来判断的,如何权衡进程对用户的重要性?


进程的生命周期

Android 系统会尽可能的让应用进程存活的越久,但是当系统内存有限,最终不得不杀掉旧进程释放内存来给那些新的对用户更重要的进程。系统如何决定“谁去谁留”?系统会根据运行在进程中的应用组件及应用组件的状态来划分重要性的等级。一共划分了五个等级:

  • 前台进程(Foreground process)

 当前正在被用户请求服务的进程。满足以下条件:

*包含一个 Activity 正在和用户交互(在 Resume 状态下)

*包含一个绑定到 Activity 的 Service ,这个Activity 正在和用户交互。

*包含一个前台Service(Service 调用了startForeground()方法)

*包含一个正在执行 onReceive() 方法的 BroadcastReceiver

总之,在给定的时间内,前台进程不多,它们优先级最高,是最后被列为被杀对象的。

  • 可见进程(Visible process)

一个进程不包含前台组件(Foreground components),但包含能被用户看的见的组件(可见不可交互)。如下几种条件:

*包含一个 Activity,这个 Activity 不坑交互但是可见(处于 onPause()状态),例如 Activity 打开一个半透明的 Dialog

*包含一个 Service 这个 Service 绑定到一个可见但不可交互的 Activity 。

  • Service 进程

一个进程包含一个 Service ,例如调用了 startService ()方法的,但是不包含上面两种更高优先级的组件。例如在后台播放的音乐,或在后台下载数据的 service。

  • 后台进程

一个进程包含 Activity ,但是 Activity 不再对用户可见了。例如一个应用,这个应用没有上面提到的几种组件,这个应用被按了home 键,Activity 处于 onStop()状态。

  • 空进程

一个进程不存在活动的应用组件。系统为什么会有这样的进程?主要是为了缓存,以便下次启动一个应用的时候直接使用它,从而提高启动速度。系统进程会杀掉一些这样的进程以保存进程资源缓存和 Linuxe 内涵缓存资源的平衡。

因为一个运行着 service 的进程优先级比一个运行着后台 activity 的优先级高,这就是为什么要使用一个 service 来处理一个耗时的操作会比简单开启一个工作线程来处理要好的原因。也是为啥在广播中开启线程来处理操作比不上启动一个 service 来操作更保险的原因。

线程(Threads)


当一个应用启动,系统就为这个应用启动了一个线程,这个线程叫做“main” 。这个线程非常重要,因为它负责着整个应用的事件分发及组件的绘制。和 UI 组件交互的操作都是在这个线程中完成的,因此这个线程有叫做 UI 线程(UI Thread)。

系统不会为每个组件创建独立的线程。所以同一个进程里面的所有组件都运行在 UI 线程中。 系统对这些组件的调用、分发都是从 UI 线程开始的。所以,UI 组件的事件回调 (例如 onKeyDown()等事件)都是在 UI 线程中发生的。

例如,当用户点击一个 button ,点击事件从 UI 线程分发(dispatch)到这个 button,button 接收到这会开始设置他的 pressed state ,然后发送一个刷新请求到事件列表,UI 线程从事件列表中取出并通知 button 重会。

由于 UI 的单线程模式,你不能再 UI 线程中处理密集耗时的工作,这将会导致 UI 阻塞,影响用户体验,甚至导致 “application not response”(ANR).对于 UI 线程,要遵循下面简单的两条规则:

1  避免 UI 线程阻塞。

2 避免从其他线程访问 UI 组件。

  • 工作线程(Worker thread)

由于上面我们讨论的 UI 线程不能阻塞,不能做耗时操作,所以我们把这样的操作放在新的线程中来完成,我们称这样的线程为工作线程。

例如,下面代码演示从网络下载一张图片并显示在  ImageView 中。


public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            Bitmap b = loadImageFromNetwork("http://example.com/image.png");
            mImageView.setImageBitmap(b);
        }
    }).start();
}


首先,我们看到,开启工作线程,下载是没问题的,满足上面说的第一条原则,但是再看代码 


mImageView.setImageBitmap(b);


这里,在工作线程中对 ImageView 进程操作,这里违背来第二条原则,这会引起意想不到的错误或异常。我们可通过以下几种方式来修正这个问题:

例如使用View.post(Runnable)来修改这个问题。


Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
For example, you can fix the above code by using the View.post(Runnable) method:

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            final Bitmap bitmap =
                    loadImageFromNetwork("http://example.com/image.png");
            mImageView.post(new Runnable() {
                public void run() {
                    mImageView.setImageBitmap(bitmap);
                }
            });
        }
    }).start();
}


现在这个实现符合了线程安全要求,网络操作在工作线程中处理和对 UI 的刷新彼此分离。然而,这样会使代码变的很复杂,不好维护。对于工作线程和 UI 交互比较复杂的可以考虑使用 handler 来处理。或许  AsyncTask 也是极其不错的方案。

  • 使用 AsyncTask

AsyncTask 实现 UI 的异步交互。AsyncTask 内部存在一组工作线程(线程池管理),用于处理耗时(阻塞)操作,并且会将操作的结果发布到 UI 线程中进行处理。对与线程的管理及和 UI 交互的细节不用你自己去处理,使用简单方便。

实现 AsyncTask 必须实现 doInBackGround()  方法和 onPostExecute()方法。在doInBackground()方法中处理阻塞操作,在onPostExecute()方法中处理 UI 刷新。我们需要在 UI 线程中调用 AsyncTask 的 execute()方法来开始工作。

例如:


public void onClick(View v) {
    new DownloadImageTask().execute("http://example.com/image.png");
}

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
    /** The system calls this to perform work in a worker thread and
      * delivers it the parameters given to AsyncTask.execute() */
    protected Bitmap doInBackground(String... urls) {
        return loadImageFromNetwork(urls[0]);
    }

    /** The system calls this to perform work in the UI thread and delivers
      * the result from doInBackground() */
    protected void onPostExecute(Bitmap result) {
        mImageView.setImageBitmap(result);
    }
}


现在,关于 UI 阻塞线程安全的解决方案的代码变的简单了。更多关于 AsyncTask 的使用细节,点击  AsyncTask  。

  • 线程安全方法(Thread-safe methods)

在以下解决方案中,你的方法实现可能会被多个线程调用,所以这些方法必须是线程安全的。

这里重要提及的事关于被远程调用的方法,例如,一个 bound service。通过 IBinder 实现的方法调用来自运行这个 IBinder 的进程,方法执行发生着调用者的线程。然而,当方法调用来自另一个进程时,情况就不一样了。方法的执行将发生在一个线程中,这个线程来自一个线程池,这个线程池由运行者这个Binder服务的进程维持。简单的说就是当 boud service (通过 IBinder 通信) 提供的事一个本地服务,服务方法的执行方式在本进程中,并且能能够明确的知道调用发生在哪个线程里。如果 bound service(通过 IBinder 通信)是一个远程服务,那么如果调用来自另一个进程则调用发生在一个不确定的线程中(启用线程池来处理远程请求),所以这种情况下必须保证服务方法是线程安全的。图解:

类似的,像 contentProvider 处理来自另一个进程的请求,也是一种远程服务调用。其实 ContentResolver 和 ContentProvider 已经帮我们封装了进程之间的交互细节。contentProvider 不可能只服务于一个客户端进程,甚至可能同时服务于多个客户端的请求。所以contentProvider 的 insert()、delete()、update()和 getType()等这样的方法都是在一个线程池的线程中执行的。所以这些方法的实现也要确保是线程安全的。

目录
相关文章
|
1月前
|
消息中间件 并行计算 安全
进程、线程、协程
【10月更文挑战第16天】进程、线程和协程是计算机程序执行的三种基本形式。进程是操作系统资源分配和调度的基本单位,具有独立的内存空间,稳定性高但资源消耗大。线程是进程内的执行单元,共享内存,轻量级且并发性好,但同步复杂。协程是用户态的轻量级调度单位,适用于高并发和IO密集型任务,资源消耗最小,但不支持多核并行。
43 1
|
10天前
|
并行计算 数据处理 调度
Python中的并发编程:探索多线程与多进程的奥秘####
本文深入探讨了Python中并发编程的两种主要方式——多线程与多进程,通过对比分析它们的工作原理、适用场景及性能差异,揭示了在不同应用需求下如何合理选择并发模型。文章首先简述了并发编程的基本概念,随后详细阐述了Python中多线程与多进程的实现机制,包括GIL(全局解释器锁)对多线程的影响以及多进程的独立内存空间特性。最后,通过实例演示了如何在Python项目中有效利用多线程和多进程提升程序性能。 ####
|
15天前
|
Linux 调度 C语言
深入理解操作系统:进程和线程的管理
【10月更文挑战第32天】本文旨在通过浅显易懂的语言和实际代码示例,带领读者探索操作系统中进程与线程的奥秘。我们将从基础知识出发,逐步深入到它们在操作系统中的实现和管理机制,最终通过实践加深对这一核心概念的理解。无论你是编程新手还是希望复习相关知识的资深开发者,这篇文章都将为你提供有价值的见解。
|
12天前
|
Java
java小知识—进程和线程
进程 进程是程序的一次执行过程,是系统运行的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程 线程,与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比
23 1
|
17天前
深入理解操作系统:进程与线程的管理
【10月更文挑战第30天】操作系统是计算机系统的核心,它负责管理计算机硬件资源,为应用程序提供基础服务。本文将深入探讨操作系统中进程和线程的概念、区别以及它们在资源管理中的作用。通过本文的学习,读者将能够更好地理解操作系统的工作原理,并掌握进程和线程的管理技巧。
34 2
|
19天前
|
调度 Python
深入浅出操作系统:进程与线程的奥秘
【10月更文挑战第28天】在数字世界的幕后,操作系统悄无声息地扮演着关键角色。本文将拨开迷雾,深入探讨操作系统中的两个基本概念——进程和线程。我们将通过生动的比喻和直观的解释,揭示它们之间的差异与联系,并展示如何在实际应用中灵活运用这些知识。准备好了吗?让我们开始这段揭秘之旅!
|
1月前
|
存储 消息中间件 人工智能
进程,线程,协程 - 你了解多少?
本故事采用简洁明了的对话方式,尽洪荒之力让你在轻松无负担的氛围中,稍微深入地理解进程、线程和协程的相关原理知识
41 2
进程,线程,协程 - 你了解多少?
|
29天前
|
Python
Python中的多线程与多进程
本文将探讨Python中多线程和多进程的基本概念、使用场景以及实现方式。通过对比分析,我们将了解何时使用多线程或多进程更为合适,并提供一些实用的代码示例来帮助读者更好地理解这两种并发编程技术。
|
1月前
|
消息中间件 并行计算 安全
进程、线程、协程
【10月更文挑战第15天】进程、线程和协程是操作系统中三种不同的执行单元。进程是资源分配和调度的基本单位,每个进程有独立的内存空间;线程是进程内的执行路径,共享进程资源,切换成本较低;协程则更轻量,由用户态调度,适合处理高并发和IO密集型任务。进程提供高隔离性和安全性,线程支持高并发,协程则在资源消耗和调度灵活性方面表现优异。
45 2
|
1月前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
47 4

相关实验场景

更多
下一篇
无影云桌面