1、Android进程模型
在安装Android应用程序的时候,Android会为每个程序分配一个Linux用户ID,并设置相应的权限,这样其它应用程序就不能访问此应用程序所拥有的数据和资源了。
在 Linux 中,一个用户ID 识别一个给定用户;在 Android 上,一个用户ID 识别一个应用程序。
应用程序在安装时被分配用户 ID,应用程序在设备上的存续期间内,用户ID 保持不变。
默认情况下,每个apk运行在它自己的Linux进程中。当需要执行应用程序中的代码时,Android会启动一个jvm,即一个新的进程来执行,因此不同的apk运行在相互隔离的环境中。
下图显示了:两个 Android 应用程序,各自在其自己的基本沙箱或进程上,他们是不同的Linux user ID。
开发者也可以给两个应用程序分配相同的linux用户id,这样他们就能访问对方所拥有的资源。
为了保留系统资源,拥有相同用户id的应用程序可以运行在同一个进程中,共享同一个jvm。
不同的应用程序可以运行在相同的进程中。要实现这个功能,首先必须使用相同的私钥签署这些应用程序,然后必须使用 manifest 文件给它们分配相同的 Linux 用户 ID,这通过用相同的值/名定义 manifest 属性 android:sharedUserId 来做到。
如下图,显示了两个 Android 应用程序,运行在同一进程上。
二、Android的单线程模型
当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。
在开发Android应用时必须遵守单线程模型的原则: Android UI操作,并不是线程安全的,并且这些操作必须在UI线程中执行。
如果在非UI线程中直接操作UI线程,会抛出异常,这与普通的java程序不同。
由于UI线程负责事件的监听和绘图,因此,必须保证UI线程能够随时响应用户的需求,UI线程里的操作应该像中断事件那样短小。
费时的操作(如网络连接)需要另开线程,否则,如果UI线程超过5s没有响应用户请求,会弹出对话框提醒用户终止应用程序。
如果在新开的线程中需要对UI进行设定,就可能违反单线程模型,因此android采用一种复杂的Message Queue机制保证线程间通信
Message Queue:
1/Message—消息,理解为线程间交流的信息。
2/Handler—处理者,是Message的主要处理者,负责Message的发送和处理。
主线程通过子类化Handler实现handlerMessage(msg)来处理数据,而子线程通过传入的handler对象的sendMessage()发送数据。
Handler有两种发送方式:sendMessage和post。sendMessage发送一个messgae对象交由handMessage()处理,而post()则发送一个runnable代码块插到主线程运行。
3/Message Queue—消息队列,用来存放通过Handler发布的消息,按照先进先出执行。
Android在第一次启动程序时会默认会为主线程UI Thread创建一个关联的消息队列MessageQueue(子线程不创建),用来管理程序的一些上层组件如activities,broadcastReceivers等等。
所以Looper.getMainLooper得到住线程Looper不为null,当前线程调用Looper.myLooper可能为null;
4/Looper—每个线程里的MessageQueue的管家。
Looper扮演着一个Handler和消息队列之间通讯桥梁的角色。
程序组件首先通过Handler把消息传递给Looper,Looper把消息放入队列。Looper也把消息队列里的消息广播给所有的Handler,Handler接受到消息后调用handleMessage进行处理。
总结:
1、主线程UI Thread 默认创建消息队列 Message Queue,队列拥有管家Looper;
2、在主线程中子类化Handler,并把对象传入子线程中;
3、子线程得到主线程的处理者Handler,通过Handler把消息传递给管家Looper;
4,管家Looper放到队列Message Queue中,让主线程处理数据(更新界面等),Over;
实例如下:
package com.jseven.messagehandler; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class MessageAndHandlerActivity extends Activity { EditText input; Button bn; TextView show; MyHandler myHandler; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); input = (EditText) findViewById(R.id.input); bn = (Button) findViewById(R.id.bn); show = (TextView) findViewById(R.id.show); // 1、主线程UI Thread 默认创建消息队列 Message Queue,队列拥有管家Looper; Looper looper = Looper.myLooper(); // 2.1 、在主线程中子类化Handler,并把对象传入子线程中; myHandler = new MyHandler(Looper.myLooper()); // 此处甚至可以不需要设置Looper,因为 Handler默认就使用当前线程的Looper // myHandler = new MyHandler(); // 2.2 、 在主线程中子类化Handler,并把对象传入子线程中; bn.setOnClickListener(new OnClickListener() { public void onClick(View v) { // 这里子线程是内部类,可以直接访问handler。但有时候可能是外部类,所以还是以参数传递 new Thread(new MySubThread(myHandler)).start(); } }); } // 3、子线程得到主线程的处理者Handler,通过Handler把消息传递给管家Looper; class MySubThread implements Runnable { Handler h; public MySubThread(Handler handler) { super(); this.h = handler; } public void run() { String str = "啥都处理只是露个面------" + input.getText().toString(); Message msg = new Message(); msg.what = 0x123; // 0x123表示符,让handlerMessage知道而已 msg.obj = str; h.sendMessage(msg); } } // 4,管家Looper放到队列Message Queue中,让主线程处理数据(更新界面等),Over; class MyHandler extends Handler { public MyHandler() { super(); } public MyHandler(Looper looper) { super(looper); } // 发送消息给主线程 public void handleMessage(Message msg) { if(msg.what == 0x123) { show.setText(msg.obj.toString()); } } } }
对于这个实例,当这个Activity执行完onCreate,onStart,onResume后,就监听UI的各种事件(如按钮点击事件)和消息(如Handler Message)。
当点击一个按钮后启动线程,线程执行结束后通过handler发送一个消息,由于这个handler属于UI线程,因此这个消息也发送给UI线程,然后UI线程又把这个消息给handler处理,而这个handler是UI线程创造的,它可以访问UI组件,因此就更新了页面。
由于通过handler需要自己管理线程类,如果业务稍微复杂,代码看起来就比较混乱,因此android提供了AsyncTask类来解决此问题。
三、AsyncTask类
线程更新是可用,但是这种匿名线程的方式是存在缺陷的:
第一,线程的开销较大,如果每个任务都要创建一个线程,那么应用程序的效率要低很多;
第二,线程无法管理,匿名线程创建并启动后就不受程序的控制了,如果有很多个请求发送,那么就会启动非常多的线程,系统将不堪重负。
另外,前面已经看到,在新线程中更新UI还必须要引入handler,这让代码看上去非常臃肿。
AsyncTask很好地解决这一问题。AsyncTask的特点是任务在主线程之外运行,而回调方法是在主线程中执行,这就有效地避免了使用Handler带来的麻烦。
阅读AsyncTask的源码可知,AsyncTask是使用java.util.concurrent 框架来管理线程以及任务的执行的,concurrent框架是一个非常成熟,高效的框架,经过了严格的测试。这说明AsyncTask的设计很好的解决了匿名线程存在的问题。
首先继承一下此类,实现以下若干方法:
1) onPreExecute(),该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。
2) doInBackground(Params...),将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。
可以调用publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
3) onProgressUpdate(Progress...),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。
4) onPostExecute(Result),在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.
使用时需要遵循以下规则:
1、Task的实例必须在UI thread中创建
2、execute方法必须在UI thread中调用
3、不要手动的调用这些方法,只调用execute即可
4、该task只能被执行一次,否则多次调用时将会出现异常
package com.jseven.messagehandler; import android.app.Activity; import android.os.AsyncTask; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class AsyncTaskTest extends Activity { EditText input; Button bn; TextView show; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); input = (EditText) findViewById(R.id.input); bn = (Button) findViewById(R.id.bn); show = (TextView) findViewById(R.id.show); bn.setOnClickListener(new OnClickListener() { public void onClick(View v) { // 1、Task的实例必须在UI thread中创建 // 2、execute方法必须在UI thread中调用 // 3、不要手动的调用这些方法,只调用execute即可 // 4、该task只能被执行一次,否则多次调用时将会出现异常 new MyTask().execute(input.getText().toString()); } }); } class MyTask extends AsyncTask<String, Integer, String> { // 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。 // 这里将主要负责执行那些很耗时的后台计算工作。 protected String doInBackground(String... params) { return params[0] + "----在后面露个面"; // 处理 } // 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用, // 后台的计算结果将通过该方法传递到UI thread. protected void onPostExecute(String result) { show.setText(result.toString()); // 更新 } } }
参考文章:(感谢一下博主的分享)
http://blog.csdn.net/sunboy_2050/article/details/8027442
http://blog.csdn.net/super005/article/details/5827298
相关收藏:
Android消息处理机制http://www.cnblogs.com/qingblog/archive/2012/06/27/2566021.html
Android的异步(Thread,Handler,AsyncTask)http://blog.csdn.net/jave_/article/details/6879978