Android App进程中最少有几个线程

当应用程序组件启动,且应用程序没有其他组件运行时,Android系统为这个应用程序启动一个新的Linux进程,并开始运行一个主线程。默认情况下,同一应用程序的所有组件都在同一进程的主线程中运行。如果应用程序组件启动,并且已经存在该应用程序的进程(因为应用程序中的其它组件已经启动),则组件将在该进程中启动,并使用相同的主线程。但是,您可以通过设置让应用程序中的不同组件分别在不同的进程中运行,并且可以为任何进程创建其它线程。本文档讨论了进程和线程是如何在Android应用程序中工作的。
Processes

默认情况下,同一应用程序的所有组件都运行在同一进程中,大多数应用程序不需要改变此设置。但是,如果您需要设置某个组件到特定进程,则可以在manifest文件中来实现。

每种组件元素(activity、service、receiver和 provider)都支持android:process属性,用于指定该组件运行在哪个进程中。您可以设置此属性,使每个组件在自己的进程中运行,或者某些组件共享进程,而其他组件不共享进程。您还可以设置android:process,以便不同应用程序的组件在同一进程中运行,前提是应用程序共享相同的Linuxuser ID,并使用相同的证书进行签名。元素也支持android:process属性,用于设置适用于所有组件的默认值。

当内存不足时,Android系统可能会在某个时刻kill进程,以保证其他更直接为用户服务的进程能正常运行。在被kill的进程中运行的应用程序组件因此被销毁。当组件再次工作时,将再次启动新的进程。

Android系统会权衡进程相对于用户的重要性来决定要杀死哪些进程。例如,与屏幕上可见Activity的进程相比,它更容易关闭在屏幕上不再可见的Activity的进程。因此,是否终止进程取决于运行在该进程中的组件的状态。更多关于进程生命周期与APP状态关系的讲解,请见Processes and Application Lifecycle。
线程

当应用程序启动时,系统为应用程序创建主线程。此线程非常重要,因为它负责将事件分派到的widgets。应用程序与UI组件交换也主要在主线程中进行,所以主线程也叫UI线程。然而,在特殊情况下,应用程序的主线程可能不是它的UI线程;有关更多信息,请参见 Thread annotations。

系统不为组件的每个实例创建单独的线程。运行在同一进程中的所有组件都在该进程的UI线程中实例化,系统从UI线程发出对每个组件的调用。因此,响应系统的回调函数(如onkeydown()或onCreate())始终运行在进程的UI线程。例如,当用户触摸屏幕上的某个按钮时,应用程序的UI线程将触摸事件分发给UI控件,后者依次设置其按下状态,并向事件队列发送无效请求。UI线程取出请求并通知UI控件重新绘制。

如果在UI线程中做所有事情,那么执行诸如网络访问或数据库查询之类的耗时操作将阻塞整个UI。当线程被阻塞时,就不能发送任何事件,包括绘制事件。从用户的角度来看,应用程序就会出现卡顿。更糟糕的是,如果UI线程被阻塞超过几秒钟(现在大约是5秒),系统就会向用户弹出“应用程序没有响应”(ANR)的对话框。然后,用户可能会退出应用程序,甚至卸载它。

另外,安卓UI toolkit不是线程安全的。因此,您不能从工作线程操作您的UI,您必须从UI线程对用户界面进行操作。因此,Android的单线程模型有两条规则:
1、不要阻塞UI线程
2、不要在非UI线程中操作UI控件。
工作线程

由于上面描述的单线程模型,不阻塞UI线程对应用程序的响应性至关重要。如果要执行的操作不是瞬时的,则应该确保在单独的线程(“后台线程”或“工作线程”)中执行它们。但是,请注意,您不能从UI线程或“主线程”以外的任何线程更新UI。为了解决这个问题,Android提供了下面几种从其他线程访问UI线程的方法:

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

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            // a potentially  time consuming task
            final Bitmap bitmap =
                    processBitMap("image.png");
            mImageView.post(new Runnable() {
                public void run() {
                    mImageView.setImageBitmap(bitmap);
                }
            });
        }
    }).start();
}

这个实现是线程安全的:后台操作在一个单独的线程中,而ImageView总是操纵UI线程。

然而,随着操作复杂度的增加,这种代码会变得复杂和难以维护。为了处理UI线程与工作线程更复杂的交互,您可以考虑在工作线程中使用Handler来处理从UI线程传递的消息。或者用AsyncTask,实现后台工作任务,且更新UI。
使用AsyncTask

AsyncTask允许你在用户界面上执行异步操作。它在工作线程中执行耗时操作,然后更新UI,而不需要您自己处理线程或Handler。你必须扩展AsyncTask并且实现doInBackground()回调方法,它启动的线程会运行在后台线程池。如果要更新UI,你应该实现onPostExecute()。它会获取从doInBackground()返回的结果,并在UI线程中更新UI。然后可以在UI线程中调用execute()运行任务。更多关于AsyncTask的资料请参考 AsyncTask。
线程安全的方法

在某些情况下,您实现的方法可能会从多个线程调用,因此必须编写为线程安全的。有些方法是可以远程调用的,如bound service中的方法。当在IBinder运行的进程中调用IBinder实现的方法,那么方法是在调用者的线程中执行。但是,当在其它进程中调用IBinder实现的方法,那么方法运行所在的线程是由系统维护的一个线程池创建,并且与IBinder在同一进程。例如,Service的onBind()方法是从Service的UI线程调用的,onBind()返回的对象中的方法是在是从线程池创建的线程调用的。因为服务可以有多个客户端,多个线程可以同时使用同一个IBinder方法,因此IBinder方法必须被实现为线程安全的。

类似地,content provider可以接收来自其他进程的数据请求。虽然ContentResolver和ContentProvider类隐藏了他们是怎样管理进程间通信的,ContentProvider中的query(), insert(), delete(), update(), 和getType()等方法是从content provider所在进程的线程池调用的。因为这些方法可以同时从任意数量的线程中调用,所以它们也必须实现为线程安全的。
进程间通信

Android使用远程过程调用(RPC)提供了一种进程间通信机制(IPC),Activity或其它应用程序组件调用方法,但是在另一个进程中执行,执行完后返回结果给调用者。这就需要分解方法和数据,使操作系统能理解,并将它从本地进程和地址空间传递到远程进程和地址空间,然后重新组装和调用。返回值然后以相反的方向传输。Android提供了实现IPC的所有代码,因此您可以将重点放在定义和实现RPC编程接口上。要执行IPC,你的应用必须使用bindService()绑定到一个服务,使用bindservice(),有关更多信息,请参见 Services开发指南。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值