android进程和线程

转载请注明出处:http://blog.csdn.net/zhouli_csdn/article/details/45367353


进程和线程(Processes and Threads)

如果一个应用启动它的一个组件(activity,service,receiver,provider)时,没有其它的组件正在运行,安卓系统会为该应用创建一个进程。默认情况下,同一应用的所有组件运行在同一个进程和线程(主线程)。如果一个应用组件启动时已经存在一个进程(假设为A)(因为已经存在一个该应用的组件),那么该组件将会在同一进程A中运行。当然,你也可以让不同的组件运行在不同的进程中,并为他们创建其它的线程。

本文讨论了安卓进程和线程在应用中是怎么工作的。

进程

          默认情况下,同一应用程序的所有组件运行在同一进程中,并且不能改变这种情况。但是,如果你想控制一个组件在一个单独的进程中,你可以在manifest文件中配置。
manifest文件为每一种组件都支持android:process属性配置该组件运行的进程。你可以设置这个属性使每一个组件运行在自己的进程中,或者一些运行在一个进程中,而其他的运行在各自的进程中。你也可以设置andorid:process属性,使具有相同linux user ID和相同签名(are signed with the same certificates)的不同应用程序的组件运行在同一进程中。

Application元素可以设置所有组件的默认进程。

在内存不足并且其他要为 用户 服务的进程请求的情况下,Android可能关闭一个进程。运行在该进程中的组件也会被销毁,当有任务时,会被重新启动。

在决定结束哪一个进程依据运行在进程中的组件的状态。

进程生命周期(Process lifecycle

Android会尽可能的保持一个应用程序进程直到需要为新的进程或者更重要的进程回收内存。Android依据进程中正在运行的组件和这些组件的状态划分了一个重要性层次结构。最低等级的将会被最先杀死,然后是更低等级的,直到可以回复系统资源。

Andorid为进程划分了五个重要性层次结构,前面的是最重要的。

1.前台进程(Foreground process

一个进程正在为用户正在做的事情服务。如果一个进程有如下条件会被认为前台进程:
    1.拥有一个Activity(与用户交互的Activity的onResume方法被调用)
    2.拥有一个绑定到正与用户交互的Activity的Service
    3.拥有一个Service调用了startForeground()方法
    4.拥有一个正在执行它的生命周期函数(onCreate,onStart或onStop)的Service
    5.拥有一个正在执行onReceive方法的BroadcastReceiver
一般来说,任何时候只有少量的前台进程存在。只有在系统内存严重不足(通常这时候设备到达了内存分页状态)下,才会杀死前台进程。

2.可见进程(Visible process

一个没有前台组件,但是仍然可以影响用户屏幕上的内容。如果一个进程具备以下条件被认为可见进程:
    1.拥有一个不在前台的Activity(onPause方法被调用)。例如前台Activity打开一个对话框,并且允许之前的Activity在后面看见。
    2.拥有一个绑定到可见或者前台Activity 的Service。
可见进程一般不会被杀死,除非这样做可以保证所有前台进程运行。

3.服务进程(Service process

   拥有一个由startService方法启动的Service,并且不在1和2这两个等级中。尽管服务用户看不见,但是他们一般做用户比较关心的事情,例如后台播放音乐或者下载网络数据。

4.后台进程(Background process

   拥有一个用户看不见的Activity(onStop方法被调用)。这些进程对用户体验没有直接影响,系统可以随时杀死他们。通常会有很多后台进程运行,它们被保存在一个LRU(最近最少使用)表中。如果一个Activity实现其生命周期方法正确,并且保存了它当前的状态。当系统杀死它们后,用户返回到该Activity后,该Activity会恢复它的保存状态,所以对用户体验影响不大。

5.空进程

   一个不包含任何应用程序组件的进程。保持的唯一原因就是为了缓存,当一个组件需要运行它时,提高启动时间。系统经常会杀死这些进程,以平衡整个系统进程缓存和底层内核缓存之间的资源。

    Android进程排名依据进程拥有的最高级别。例如一个进程如果拥有一个服务和一个可见Activity,那么该进程会被认为是一个可见进程。
    
    此外,如果一个进程被其它进程所依赖,那么他的优先级会增加。例如:如果A进程中content provider正在服务一个B进程或者A进程的service被绑定到B进程的一个组件,那么A至少会被认为和B是一样重要的。
    
   因为拥有一个服务的进程优先级比拥有后台Activities的进程优先级高,一个Activity启动一个长时间运行的操作要在服务中,而不是简单的创建一个线程(如果这个操作时间比Activity长)。例如一个Activity上传一个图片到一个网站上,开启一个服务上传图片可以在Activity到后台时让然可以上传。用service操作保证至少拥有服务进程优先级,不管Activity发生了什么。同样的原因,Receiver也要使用Service来执行长时间的操作,而不是简单的使用线程。

线程(Threads

当应用程序启动时,系统会为应用创建一个执行线程(主线程)。这个线程非常重要,它负责控件事件的分发和屏幕绘制,也负责应用程序和控件之间的相互作用。因此,主线程也被叫做UI线程。

系统不会为每一个组件的实例创建一个线程。所有运行在相同进程的组件都在UI线程实例化,系统对每一个组件的调用都从UI线程发出。因此,系统的回调函数(例如onKeydown和生命周期函数)总是在UI线程执行。

例如:当用户点击一个Button时,UI线程将touch事件分发给button,button会设置自己的点击状态并发送一个invalidate request到事件队列(event queue),UI线程拿到请求,并通知button重绘自己。

当你的应用程序执行繁多的操作以相应用户的交互,这种单线程模型会表现不佳,除非你正确的实现你的应用程序。
具体来说,如果你在UI线程执行长时间的操作,例如网络访问,数据库操作,将会使UI线程阻塞。当UI线程被阻塞,事件将不能被分发,屏幕也不能绘制。从用户的角度来看应用挂起了。更糟糕的是,如果UI线程被阻塞超过5秒钟,用户将会被呈现臭名昭著的应用程序无响应("application not responding" (ANR)对话框。

Andorid UI工具包是非线程安全的,因此不能从一个工作线程去直接操作UI,必须在UI线程操作UI。因此对于Android单线程模型有两个规则:
    1.不阻塞UI线程
    2.不从工作线程访问Android UI toolkit

工作线程(Worker threads

基于以上单线程模型的描述,它的响应能力是至关重要的,你不能阻塞UI线程。如果你有操作不是在瞬间完成,确保它们在工作线程完成。下面是一个例子:
public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            Bitmap b = loadImageFromNetwork("http://example.com/image.png");
            mImageView.setImageBitmap(b);
        }
    }).start();
}
但是它违反了单线程模型的第二个规则,为了解决这个问题Android提供了几种方式从其它线程访问UI线程:
    1.Activity.runOnUiThread(Runnable)
    2.View.post(Runnable)
    3.View.postDelayed(Runnable, long)
例如解决如上问题可以使用View.post(Runnable):
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();
}
然而,随着操作的复杂性,这种代码会变得很复杂和难以维护,你可以考虑使用Handler。更好的解决方案可以继承
AsyncTask类,这简化了需要和用户界面交互的工作线程。

使用AsyncTask(Using AsyncTask

AsyncTask允许你在用户接口执行异步工作,他在工作线程执行阻塞操作并将结果发到UI线程,不需要自己实现handler操作。
你必须实现AsyncTask的子类,并在doInBackground()回调方法,它是执行在一个线程池中的线程,你可以在onPostExecute()中操作UI。然后必须在UI线程中调用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);
    }
}
AsyncTask使代码比较简单,容易维护:
     1.doInBackground()在工作线程
   2.onPreExecute(),onPostExecute(),onProgressUpdate()工作在UI线程
   3.doInBackground()返回的值被发送到onPostExecute()
   4.你可以在doInBackground()中随时调用publishProgress()以执行onProgressUpdate()方法。
   5.你可以在任何时候从任何线程取消任务。
警告:你可能在由于 runtime configuration change意外重启Activity的时候,破坏你的工作线程。你可以查看如何在重启时候保持task,以及在Activity被destroyed时候取消task。

线程安全的方法

当在其它进程调用Ibinder的方法时,可能在同一时间从线程池的几个线程中执行方法,因此必须是线程安全的。
从其它进程操作content provider的 query()insert()delete(),update(), and getType()时,也会遇到这种情况。

进程间通信

Andorid提供了进程间通信(IPC)机制,使用远程过程调用(RPCs),通过在应用程序组件中调用但是在远程的进程(另一个进程)中执行,并返回一个结果给调用者。这需要分解一个方法和它的数据到操作系统可以理解的层次,并把它从当前进程和地址空间传到远程的进程和地址空间,然后在那里重组和重现。返回值将按照相反的方向传回。Andorid提供了所有可以执行这些IPC交易的代码,这样就可以专注与定义和实现RPC编程接口。

执行IPC,应用程序必须绑定到一个service,使用bindService。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值