Android的进程与线程

Android的进程与线程:html

一.Android中的进程

默认状况下, 同一个application中的全部component运行在同一个linux进程下. java

启动一个component A时, 若是已存在处于运行状态中的component B, 且A和B属于同一个application, 那么component A将在component B所在的进程下运行,不然将为A建立一个新的linux进程. linux

开发者也能够为application中的component指定不一样的运行进程. android

manifest.xml文件中的<activity>, <service>, <receiver>, <provider>标签都支持android:process属性, 经过这个属性, 能够为component指定运行的进程. 程序员

<application>标签也支持设定android:process属性, 用于为application下的全部component指定默认的运行进程.缓存


进程优先级

内存不足时:

当系统的内存不足时, android系统将根据进程优先级选择杀死一些不过重要的进程. 安全

进程优先级从高到低分别为:

1. 前台进程. 如下的进程为前台进程:多线程

a. 进程中包含处于前台的正与用户交互的activity;app

b. 进程中包含与前台activity绑定的service;ide

c. 进程中包含调用了startForeground()方法的service;

d. 进程中包含正在执行onCreate(), onStart(), 或onDestroy()方法的service;

e. 进程中包含正在执行onReceive()方法的BroadcastReceiver.

系统中前台进程的数量不多, 前台进程几乎不会被杀死. 只有当内存低到没法保证全部的前台进程同时运行时才会选择杀死某个前台进程.

2. 可视进程. 如下进程为可视进程:

a. 进程中包含未处于前台但仍然可见的activity(调用了activity的onPause()方法, 但没有调用onStop()方法). 

  典型的状况是运行activity时弹出对话框, 此时的activity虽然不是前台activity, 但其仍然可见.

b. 进程中包含与可见activity绑定的service.

可视进程不会被系统杀死, 除非为了保证前台进程的运行而不得已为之.

3. 服务进程. 进程中包含已启动的service.

4. 后台进程. 进程中包含不可见的activity(onStop()方法调用后的activity). 

   后台进程不会直接影响用户体验, 为了保证前台进程/可视进程/服务进程的运行, 系统随时都有可能杀死一个后台进程.

   一个正确的实现了生命周期方法的activity处于后台时被系统杀死, 能够在用户从新启动它时恢复以前的运行状态.

5. 空进程. 不包含任何处于活动状态的进程是一个空进程. 

   系统常常杀死空进程, 这不会形成任何影响. 

   空进程存在的惟一理由是为了缓存一些启动数据, 以便下次能够更快的启动. 

进程优先级的额外说明

1. 系统会赋予进程尽量高的优先级. 

   例如一个进程既包含已启动的service, 也包含前台activity, 则这个进程会被视为前台进程. 

2. 因为组件之间的依赖性, 进程的优先级有可能被提升. 

   假如进程A服务于进程B, 则进程A的优先级不能低于进程B. 

   好比, 进程A的ContentProvider组件正在服务于进程B的某个组件, 或者进程A的service组件和进程B的某个组件绑定等, 

   这些状况下, 进程A的优先级都不会低于进程B(若是按照优先级规则, 进程A的优先级确实低于进程B, 则系统会选择提升进程A的优先级到和进程B相同).

3. 因为服务进程的优先级高于后台进程, 所以若是activity须要执行耗时操做, 最好仍是启动一个service来完成. 

   固然, 在activity中启动子线程完成耗时操做也能够, 

   可是这样作的缺点在于, 一旦activity再也不可见, activity所在的进程成为后台进程, 

   而内存不足时后台进程随时都有可能被系统杀死(可是启动service完成耗时操做会带来数据交互的问题, 

   好比耗时操做须要实时更新UI控件的状态的话, service就不是一个好的选择). 

   基于一样的考虑, 在BroadcastReceiver中也不该该执行耗时操做, 

   而应该启动service来完成(固然, BroadcastReceiver的生命周期过于短暂, 也决定了不能在其中执行耗时操做).

 

二.Android中的线程

系统不会为进程中的每个组件启动一个新的线程, 进程中的全部组件都在UI线程中实例化. 

关于android中的多线程机制, 请参博文http://coolxing.iteye.com/blog/1208371 


永远要记得:

1. 不要阻塞UI线程. 若是在UI线程中执行阻塞或者耗时操做会致使UI线程没法响应用户请求.

2. 不能在非UI线程(也称为工做线程)中更新UI, 这是由于android的UI控件都是线程不安全的.


由上所述, 开发者常常会启动工做线程完成耗时操做或阻塞操做, 若是须要在工做线程的执行期间更新UI状态, 则应该通知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();
}

上面的代码是错误的, mImageView.setImageBitmap(b)违反了第二条准则--不能在工做线程中更新UI. 


四.线程间通讯能够解决工做线程如何通知UI线程更新控件的问题. 

android提供了3种线程间通讯的方案:

1. 调用如下方法:

Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)

若是在工做线程中调用了这3个方法, 那么方法中Runnable参数封装的操做会在UI线程中执行.

使用这种方式能够修正例子中的错误之处:

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() {
                // run方法会在UI线程中执行
                public void run() {
                    mImageView.setImageBitmap(bitmap);
                }
            });
        }
    }).start();
}

2. Handler机制. 

   Handler机制容许开发者在工做线程中调用与UI线程绑定的handler对象的sendMessage()方法向UI线程的消息队列发送一条消息, 

   UI线程会在适当的时候从消息队列中取出消息并完成处理.


3. 使用AsyncTask类. 

   建立一个AsyncTask类的子类, 并根据须要选择覆写onPreExecute(), doInBackground(), onProgressUpdate(), onPostExecute()方法.

   AsyncTask类的具体使用方法请参看文档, 如下是一些大概的说明:

   a. AsyncTask类是一个泛型类, 存在3个泛型参数. 

      第一个参数指定execute方法的参数类型, 

      第二个参数指定onProgressUpdate()方法的参数类型, 

      第三个参数指定 doInBackground()方法的返回值类型以及onPostExecute()方法的参数类型.

   b. 执行流程: 

      在UI线程中调用AsyncTask类的execute方法(只有该步骤是由程序员控制的)-->系统调用onPreExecute(), 

 这个方法在UI线程中执行-->系统调用doInBackground()方法, 这个方法在工做线程中执行-->在doInBackground()方法中每调用一次publishProgress()方法, 

 就会在UI线程中执行一次onProgressUpdate()方法-->doInBackground()方法执行完成后, 系统将调用 onPostExecute()方法, 

 并将doInBackground()方法的返回值传递给 onPostExecute()方法的形参.  

 onPostExecute()方法在UI线程中执行.

采用这种方式也能够修正例子中的错误之处:

public void onClick(View v) {
    new DownloadImageTask().execute("http://example.com/image.png");
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
    protected Bitmap doInBackground(String... urls) {
        return loadImageFromNetwork(urls[0]);
    }
    
    protected void onPostExecute(Bitmap result) {
        mImageView.setImageBitmap(result);
    }
}


五.Handler的sendEmptyMessage(int what)和sendMessage(Message msg)的区别

其实二者没区别,请看下面Handler的源代码:

public final boolean sendEmptyMessage(int what){
   return sendEmptyMessageDelayed(what, 0);
}

就是调用了sendEmptyMessageDelayed()而已,下面看下这个方法:

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}

而sendMessage(Message msg)的实现和上面同样,请看:

public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}

原来在sendEmptyMessageDelayed中就是构建了一个Message,而后把这个Message的what设置成sendEmptyMessage方法中的What参数便可。

在主线程中,Looper类的loop()经过调用: msg.target.dispatchMessage(msg),调用Hanler类的dispatchMessage(Message msg)方法,从而在主线程中处理了这个Message.

若是对Handler机制要深刻了解的,请参考博客文章:http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html。

相关文章
相关标签/搜索