Android进程模型

在安装Android应用程序的时候,Android会为每个程序分配一个Linux用户ID,并设置相应的权限,这样其它应用程序就不能访问此应用程序所拥有的数据和资源了。

在 Linux 中,一个用户ID 识别一个给定用户;在 Android 上,一个用户ID 识别一个应用程序。应用程序在安装时被分配用户 ID,应用程序在设备上的存续期间内,用户ID 保持不变。

默认情况下,每个apk运行在它自己的Linux进程中。当需要执行应用程序中的代码时,Android会启动一个jvm,即一个新的进程来执行,因此不同的apk运行在相互隔离的环境中。

下图显示了:两个 Android 应用程序,各自在其自己的基本沙箱或进程上,他们是不同的Linux user ID。

Android中的线程处理_第1张图片


开发者也可以给两个应用程序分配相同的linux用户id,这样他们就能访问对方所拥有的资源。

为了保留系统资源,拥有相同用户id的应用程序可以运行在同一个进程中,共享同一个jvm。

如下图,显示了两个 Android 应用程序,运行在同一进程上。

Android中的线程处理_第2张图片

不同的应用程序可以运行在相同的进程中。要实现这个功能,首先必须使用相同的私钥签署这些应用程序,然后必须使用 manifest 文件给它们分配相同的 Linux 用户 ID,这通过用相同的值/名定义 manifest 属性android:sharedUserId来做到。


Android进程知识补充

下图是标准的Android 架构图

Android中的线程处理_第3张图片

其中我们可以看到在“Android本地库 & Java运行环境层”中,Android 运行时中,

Dalvik是Android中的java虚拟机,可支持同时运行多个虚拟机实例;每个Android应用程序都在自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例;

所有java类经过java编译器编译,然后通过SDK中的dx工具转成.dex格式交由虚拟机执行。


Android系统进程

init进程(1号进程),父进程为0号进程,执行根目录底下的init可执行程序,是用户空间进程

——-> /system/bin/sh

——-> /system/bin/mediaserver

——-> zygote

—————–> system_server

—————–>com.android.phone

—————–>android.process.acore(Home)

… …

kthreadd进程(2号进程),父进程为0号进程,是内核进程,其他内核进程都是直接或者间接以它为父进程


Android的单线程模型

当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。

在开发Android 应用时必须遵守单线程模型的原则:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行

如果在非UI线程中直接操作UI线程,会抛出android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views,这与普通的java程序不同。

由于UI线程负责事件的监听和绘图,因此,必须保证UI线程能够随时响应用户的需求,UI线程里的操作应该向中断事件那样短小,费时的操作(如网络连接)需要另开线程,否则,如果UI线程超过5s没有响应用户请求,会弹出对话框提醒用户终止应用程序。

如果在新开的线程中需要对UI进行设定,就可能违反单线程模型,因此android采用一种复杂的Message Queue机制保证线程间通信


Message Queue:

Message Queue是一个消息队列,用来存放通过Handler发布的消息。Android在第一次启动程序时会默认会为UI thread创建一个关联的消息队列,可以通过Looper.myQueue()得到当前线程的消息队列,用来管理程序的一些上层组件,activities,broadcast receivers 等等。你可以在自己的子线程中创建Handler与UI thread通讯。

通过Handler你可以发布或者处理一个消息或者是一个Runnable的实例。每个Handler都会与唯一的一个线程以及该线程的消息队列管理。

Looper扮演着一个Handler和消息队列之间通讯桥梁的角色。程序组件首先通过Handler把消息传递给Looper,Looper把消息放入队列。Looper也把消息队列里的消息广播给所有的Handler,Handler接受到消息后调用handleMessage进行处理。

实例如下:

[java] view plain copy
  1. publicclassMainActivityextendsActivityimplementsOnClickListener{
  2. privateEditTextetTXT;
  3. privateButtonbtnTXT;
  4. privateTextViewtvTXT;
  5. privateMessageHandlermessageHandler;
  6. @Override
  7. publicvoidonCreate(BundlesavedInstanceState){
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.activity_main);
  10. etTXT=(EditText)findViewById(R.id.etTXT);
  11. btnTXT=(Button)findViewById(R.id.btnTXT);
  12. tvTXT=(TextView)findViewById(R.id.tvTXT);
  13. btnTXT.setOnClickListener(this);
  14. Looperlooper=Looper.myLooper();//得到当前线程的Looper实例
  15. Looperlooper2=Looper.getMainLooper();//由于当前线程是UI线程,所以也可以通过Looper.getMainLooper()得到
  16. messageHandler=newMessageHandler(looper);//此处甚至可以不需要设置Looper,因为Handler默认就使用当前线程的Looper
  17. }
  18. @Override
  19. publicvoidonClick(Viewv){
  20. newThread(){
  21. @Override
  22. publicvoidrun(){
  23. Messagemsg=Message.obtain();
  24. msg.obj=etTXT.getText().toString();
  25. messageHandler.sendMessage(msg);
  26. }
  27. }.start();
  28. }
  29. //构造Hanlder
  30. classMessageHandlerextendsHandler{
  31. publicMessageHandler(Looperlooper){
  32. super(looper);
  33. }
  34. @Override
  35. publicvoidhandleMessage(Messagemsg){
  36. tvTXT.setText((String)msg.obj);
  37. }
  38. }
  39. }

对于这个实例,当这个activity执行完onCreate,onStart,onResume后,就监听UI的各种事件(如Click点击事件)和消息(如Handler Message)。

当点击一个按钮后启动线程,线程执行结束后通过handler发送一个消息,由于这个handler属于UI线程,因此这个消息也发送给UI线程,然后UI线程又把这个消息给handler处理,而这个handler是UI线程创造的,它可以访问UI组件,因此就更新了页面。

由于通过handler需要自己管理线程类,如果业务稍微复杂,代码看起来就比较混乱,因此android提供了AsyncTask类来解决此问题


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只能被执行一次,否则多次调用时将会出现异常

示例如下:

[java] view plain copy
  1. publicclassMainActivityextendsActivityimplementsOnClickListener{
  2. privateEditTextetTXT;
  3. privateButtonbtnTXT;
  4. privateTextViewtvTXT;
  5. @Override
  6. publicvoidonCreate(BundlesavedInstanceState){
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_main);
  9. etTXT=(EditText)findViewById(R.id.etTXT);
  10. btnTXT=(Button)findViewById(R.id.btnTXT);
  11. tvTXT=(TextView)findViewById(R.id.tvTXT);
  12. btnTXT.setOnClickListener(this);
  13. }
  14. @Override
  15. publicvoidonClick(Viewv){
  16. Stringtxt=etTXT.getText().toString();
  17. newMyTask().execute(txt);
  18. }
  19. //构造AsyncTask
  20. classMyTaskextendsAsyncTask<String,Integer,String>{
  21. @Override
  22. protectedStringdoInBackground(String...params){//在后台执行后返回结果result
  23. returnparams[0].toString();
  24. }
  25. @Override
  26. protectedvoidonPostExecute(Stringresult){//捕获后台返回的结果result
  27. tvTXT.setText(result.toString());//显示结果result
  28. }
  29. }
  30. }

运行结果:

Android中的线程处理_第4张图片

更多相关文章

  1. Android开发指南 ──应用程序基础
  2. android 跨应用程序广播发送接受
  3. Android应用程序线程消息循环模型分析(1)
  4. 【Android】进程间通信IPC——AIDL

随机推荐

  1. 使用AIDL实现Android的跨进程通信
  2. android checkbox,radiobox style自定义
  3. Phone拨号调起InCallUi流程(Phone 9.0 )(P
  4. 基于Android(安卓)XML解析与保存的实现
  5. Android Drawable Importer的使用
  6. Android中判断字符串中必须包含字母或者
  7. android browser 的几个小feature (四) k
  8. Android SDK R17
  9. Android Selector和Shape的使用方法
  10. android ListView中添加ImageButton按钮