概述:
在android 系统中,当应用启动的时候, android 系统会使用单个执行线程为应用启动新的 Linux 进程。默认情况下,同一应用
的所有组件在同一个的进程和线程(称为“主”线程)中运行。如果某个应用组件启动且该应用已存在进程(因为存在该应用的其
他组件),则该组件会在此进程内启动并使用相同的执行线程。 但是,您可以安排应用中的其他组件在单独的进程中运行,并为任何
进程创建额外的线程。
进程:
默认情况下,一个应用里面的所有组件都是运行在同一个进程中的。但是,如果您发现需要控制某个组件所属的进程,则可在清单文
件中执行此操作。如果你想将某个组件放到单独的进程中去执行。可以在清单文件中,配置组件的 android:process 属性。支持该属性的组件有:
<Activity>、<Service>、<Receiver>、<Provider>,即android app 里面的四大组件。另外<Application>元素下也支持该属性,这是整个应用的默认
进程。
进程生命周期:
Android 系统将尽量长时间地保持应用进程,但为了新建进程或运行更重要的进程,最终需要清除旧进程来回收内存。 为了确定保留或终止哪些
进程,系统会根据进程中正在运行的组件以及这些组件的状态,将每个进程放入“重要性层次结构”中。 必要时,系统会首先消除重要性最低的进程,然
后是重要性略逊的进程,依此类推,以回收系统资源。重要性层次结构一共有 5 级。
1. 前台进程
用户当前操作所必需的进程。如果一个进程满足以下任一条件,即视为前台进程:
a. 用户正在交互的 Activity
(已调用 Activity
的 onResume()
方法)
b. 某个 Service
,后者绑定到用户正在交互的 Activity
c. 正在“前台”运行的 Service
(服务已调用 startForeground()
)
d. 正执行一个生命周期回调的 Service
(onCreate()
、onStart()
或 onDestroy()
)
e. 正执行其 onReceive()
方法的 BroadcastReceiver
2. 可见进程
没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。 如果一个进程满足以下任一条件,即视为可见进程:
不在前台、但仍对用户可见的 Activity
(已调用其 onPause()
方法)。例如,如果前台 Activity 启动了一个对话框,允许在其后显示上一
Activity,则有可能会发生这种情况。
绑定到可见(或前台)Activity 的 Service。
可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。
3. 服务进程
正在运行已使用 startService()
方法启动的服务且不属于上述两个更高类别进程的进程。尽管服务进程与用户所见内容没有直接关联,但是它们
通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统
会让服务进程保持运行状态。
4. 后台进程
包含目前对用户不可见的 Activity 的进程(已调用 Activity 的 onStop()
方法)。这些进程对用户体验没有直接影响,系统可能随时终止它们,
以回收内存供前台进程、可见进程或服务进程使用。 通常会有很多后台进程在运行,因此它们会保存在 LRU (最近最少使用)列表中,以确保包含用户
最近查看的 Activity 的进程最后一个被终止。如果某个 Activity 正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明
显影响,因为当用户导航回该 Activity 时,Activity 会恢复其所有可见状态。
5. 空进程
不含任何活动应用组件的进程。保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 为使总体系统资源在进程
缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。
Android 系统在内存紧张的情况下, 会执行内存释放以满足当前使用。上面优先级是android系统保留内存使用的顺序。空进程会在内存紧张的时候
马上被释放, 前台进程正在和用户交互, 这个进程Android是不会去回收的。就是说基本不可能主动去回收一个前台进程
线程:
应用启动后, 系统会分配进程。同时会为应用创建一个 主线程/UI线程 。
如果 UI 线程需要处理所有任务,则执行耗时很长的操作(例如,网络访问或数据库查询)将会阻塞整个 UI。如果 UI 线程被阻塞超过几秒钟时间
(目前大约是 5 秒钟),用户就会看到一个让人厌烦的“应用无响应”(ANR) 对话框。
线程的注意事项有两点: 1. 不能通过 额外的工作线程(也就是除了 主线程 之外的线程)操作UI。 2. 不能阻塞UI线程。
例如: 点击事件下载文件, 下载完成之后,更新textView:
public void onClick(View v) { new Thread( new Runnable() { @Override public void run() { try { // 让线程睡眠2s, 模拟耗时 Thread.sleep(2000); // 下载完成 textView.setText("下载完成"); } catch (InterruptedException e) { e.printStackTrace(); } } } ).start(); }
代码感觉没什么问题,可是一运行的时候就报错了:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
这个错误就是说, 只有创建这个view的线程才可以操作这个view。意思就是别的线程不能操作主线程的UI。 上面的代码中, textView.setText("下载完成“);这行代码就是在操作主线程UI了。报错就是因为这行代码。
Android 提供了几种途径来从其他线程访问 UI 线程。
1. Activity.runOnUiThread(Runnable)
2. View.post(Runnable)
3. View.postDelay(Runnable, long)
代码改造:
public void onClick(View v) { /*new Thread( new Runnable() { @Override public void run() { try { // 让线程睡眠2s, 模拟耗时 Thread.sleep(2000); // 下载完成 // textView.setText("下载完成"); runOnUiThread(); } catch (InterruptedException e) { e.printStackTrace(); } } } ).start();*/ runOnUiThread(new Runnable() { @Override public void run() { try { // 让线程睡眠2s, 模拟耗时 Thread.sleep(2000); // 下载完成 textView.setText("下载完成"); } catch (InterruptedException e) { e.printStackTrace(); } } }); }
这样就完成改造了。 在运行后就没问题了。 下载完成后, View 显示”下载完成“字样。 其实这个方法也可以很直观的看出他的功能, runOnUiThread(Runnable), 运行在UI
线程中, 参数是一个Runnable对象。 可猜测这个方法将Runnable里面的执行方法放到UI线程中,使用这个方法的时候, 在这个方法里面的操作就相当于在UI线程中操作了。所以我们就可以理所当然的可以更新UI组件了。。。, 其他两个方法用法都差不多。
使用上面提供出来的三种方法, 当然可以实现和UI线程的交互, 但是,随着操作日趋复杂,这类代码也会变得复杂且难以维护。 要通过工作线程处
理更复杂的交互,可以考虑在工作线程中使用 Handler
处理来自 UI 线程的消息。官方认为最好的解决方案或许是扩展 AsyncTask
类,此类简化了与 UI
进行交互所需执行的工作线程任务。
AsyncTask使用:
AsyncTask
允许对用户界面执行异步操作。它会先阻塞工作线程中的操作,然后在 UI 线程中发布结果,而无需您亲自处理线程和/或处理程序。
要使用它,必须创建 AsyncTask
子类并实现 doInBackground()
回调方法,该方法将在后台线程池中运行。要更新 UI,必须实现 onPostExecute()
以传递doInBackground()
返回的结果并在 UI 线程中运行,这样,您即可安全更新 UI。稍后,您可以通过从 UI 线程调用 execute()
来运行任务。
使用AsyncTask类来改造上面的代码:
public class MyAsyncTask extends AsyncTask<String, Void, String> { @Override protected String doInBackground(String... params) { try { // 让线程睡眠2s, 模拟耗时 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } return "下载完成"; } @Override protected void onPostExecute(String s) { super.onPostExecute(s); textView.setText(s); } }
AsyncTask 实现类, 重写doInBackgroud方法,和onPostExecute方法。同样也实现了耗时操作的功能。AsyncTask类中还有其他几个方法,可以
很好的与UI线程交互。需要详细了解的,可以去看看这个api接口。
Android 还有一个进程间的通信: IPC机制。是本地进程和远程进程间的通信。这个东西, 后面在研究。
感觉进程和线程这两个名词很容易搞混淆。所以这两者之间的关系必须要有比较深刻的认识!!!!