• Android进程线程详解

    2018-03-22 17:57:51
    当应用程序组件启动,且应用程序没有其他组件运行时,Android系统为这个应用程序启动一个新的Linux进程,并开始运行一个主线程。默认情况下,同一应用程序的所有组件都在同一进程的主线程中运行。如果应用程序组件...

    当应用程序组件启动,且应用程序没有其他组件运行时,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开发指南。

    展开全文
  • Android进程线程详解

    2014-12-04 20:20:13
    当一个应用的组件开始运行,并且这个应用没有其它的组件在运行,系统会为这个应用启动一个新的Linux进程,这个进程只有一个线程.默认情况下,一个应用的所有组件都运行在一个进程线程(主线程)中.如果一个应用...

    进程

    当一个应用的组件开始运行,并且这个应用没有其它的组件在运行,系统会为这个应用启动一个新的Linux进程,这个进程只有一个线程.默认情况下,一个应用的所有组件都运行在一个进程和线程(主线程)中.如果一个应用的线程开始运行,并且已经存在这个应用的线程了(因为有这个应用程序的另一个组件已经运行了),于是这个组件就会在这个已有的进程中启动并且运行在同一个线程中.然而,你完全可以安排不同的组件运行于不同的进程,并且你可以为任何程序创建另外的线程.

     

     

    进程

    默认下,同一个程序的所有组件都运行在同一个进程中并且大多数程序不必改变这一状况.然而,如果你非要与众不同,也可以通过修改manifest文件实现.

     

    manifest文件中的所有支持Android:process属性的那些项(<activity>,<service>, <receiver>,<provider>)都可以指定一个进程,于是这些组件就会在这个进程中运行.你可以设置这个属性使每个组件运行于其自己的进程或只是其中一些组件共享一个进程.你也可以设置android:process以使不同应用的组件们可以运行于同一个进程—假如这些应用共享同一个用户ID并且有相同的数字证书.

     

    <application>元素也支持android:process属性,用于为所有的组件指定一个默认值.

     

    Android可能在某些时刻决定关闭一个进程,比如内存很少了并且另一个进程更迫切的需要启动时.进程被关闭时,其中的组件们都被销毁.如果重新需要这些组件工作时,进程又会被创建出来.

     

    当决定关闭哪些线程时,Android系统会衡量进程们与用户的紧密程度.例如,比起一个具有可见的activity的进程,那些所含activity全部不可见的进程更容易被关闭.如何决定一个进程是否被关闭,取决于进程中运行的组件们的状态.决定关闭进程的规则将在下面讨论.

     

    进程的生命期

     

     

    Android系统会尽量维持一个进程的生命,直到最终需要为新的更重要的进程腾出内存空间。为了决定哪个该杀哪个该留,系统会跟据运行于进程内的组件的和组件的状态把进程置于不同的重要性等级。当需要系统资源时,重要性等级越低的先被淘汰。

     

    重要性等级被分为5个档。下面列出了不同类型的进程的重要性等级(第一个进程类型是最重要的,也是最后才会被杀的):

     

    1前台进程

     

    用户当前正在做的事情需要这个进程。如果满足下面的条件,一个进程就被认为是前台进程:

     

    这个进程拥有一个正在与用户交互的Activity(这个ActivityonResume()方法被调用)

     

    这个进程拥有一个绑定到正在与用户交互的activity上的Service

     

    这个进程拥有一个前台运行的Service — service调用了方法 startForeground().

     

    这个进程拥有一个正在执行其任何一个生命周期回调方法(onCreate(),onStart(),onDestroy())的Service

     

    这个进程拥有正在执行其onReceive()方法的BroadcastReceiver

     

     

    通常,在任何时间点,只有很少的前台进程存在。它们只有在达到无法调合的矛盾时才会被杀--如果内存太小而不能继续运行时。通常,到了这时,设备就达到了一个内存分页调度状态,所以需要杀一些前台进程来保证用户界面的反应

     

    2可见进程

     

     

    一个进程不拥有运行于前台的组件,但是依然能影响用户所见。满足下列条件时,进程即为可见:

     

     

    这个进程拥有一个不在前台但仍可见的Activity(它的onPause()方法被调用)。当一个前台activity启动一个对话框时,就出了这种情况。

     

    3服务进程

     

    一个可见进程被认为是极其重要的。并且,除非只有杀掉它才可以保证所有前台进程的运行,否则是不能动它的。

     

    这个进程拥有一个绑定到可见activityService

     

    一个进程不在上述两种之内,但它运行着一个被startService()所启动的service

     

    尽管一个服务进程不直接影响用户所见,但是它们通常做一些用户关心的事情(比如播放音乐或下载数据),所以系统不到前台进程和可见进程活不下去时不会杀它。

    4后台进程

     

     

    一个进程拥有一个当前不可见的activity(activityonStop()方法被调用)

     

    这样的进程们不会直接影响到用户体验,所以系统可以在任意时刻杀了它们从而为前台、可见、以及服务进程们提供存储空间。通常有很多后台进程在运行。它们被保存在一个LRU(最近最少使用)列表中来确保拥有最近刚被看到的activity的进程最后被杀。如果一个activity正确的实现了它的生命周期方法,并保存了它的当前状态,那么杀死它的进程将不会对用户的可视化体验造成影响。因为当用户返回到这个activity时,这个activity会恢复它所有的可见状态。

     

     

    5空进程

     

    一个进程不拥有入何active组件。

     

    保留这类进程的唯一理由是高速缓存,这样可以提高下一次一个组件要运行它时的启动速度。系统经常为了平衡在进程高速缓存和底层的内核高速缓存之间的整体系统资源而杀死它们。

     

     

     

     

    跟据进程中当前活动的组件的重要性,Android会把进程按排在其可能的最高级别。例如,如果一个进程拥有一个service和一个可见的activity,进程会被定为可见进程,而不是服务进程。

     

    另外,如果被其它进程所依赖,一个进程的级别可能会被提高—一个服务于其它进程的进程,其级别不可能比被服务进程低。

     

    因为拥有一个service的进程比拥有一个后台activitie的进程级别高,所以当一个activity启动一个需长时间执行的操作时,最好是启动一个服务,而不是简单的创建一个工作线程。尤其是当这个操作可能比activity的生命还要长时。例如,一个向网站上传图片的activity,应该启动一个service,从而使上传操作可以在用户离开这个activity时继续在后台执行。使用一个service保证了这个操作至少是在"服务进程"级别,而不用管activity是否发生了什么不幸。这同样是广播接收者应该使用service而不是简单地使用一个线程的理由。

     

     

     

    线程

     

    当一个应用被启动,系统创建一个执行线程,叫做"main"。这个线程是十分重要的,因为它主管向用户界面控件派发事件。其中包含绘图事件。它也是你的应用与界面工具包(Android.widget android.view包中的组件)交互的地方。于是main线程也被称为界面线程。

     

    系统不会为每个组件的实例分别创建线程。所有运行于一个进程的组件都在界面线程中被实例化,并且系统对每个组件的调用都在这个线程中派发。结果,响应系统调用的方法(比如报告用户动作的onKeyDown()或一个生命周期回调方法)永远在界面线程中进程。

     

    例如,当用户触摸屏幕上的一个按钮时,你的应用的界面线程把触摸事件派发给控件,然后控件设置它的按下状态再向事件队列发出一个自己界面变得无效的请求,界面线程从队列中取出这个请求并通知这个控件重绘它自己。

     

    当你的应用在响应用户交互时需执行大量运算时,这种单线程的模式会带来低性能,除非你能正确的优化你的程序。特别的,如果所有事情都在界面线程中发生,执行比如网络连接或数据库请求这样的耗时操作,将会阻止整个界面的响应。当线程被阻塞时,就不能派发事件了,包括绘图事件。从用户的角度看,程序反应太慢了。甚至更糟的是,如果界面线程被阻塞几秒钟(大5秒钟吧),用户就户抱怨说程序没反应了,用户可能因而退出并删掉你的应用。

     

    此外,Andoid界面不是线程安全的。所以你绝不能在一个工作线程中操作你的界面—你只能在界面线程中管理的你的界面。所以,对于单线程模式有两个简单的规则:

     

    1 不要阻塞界面线程

     

    2不要在界面线程之外操作界面。

     

    工作线程

     

    由于上述的单线程模式,不要阻塞你的界面线程以使你的应用的界面保持响应是非常重要的,那么如果你有不能很快完成的任务,你应把它们放在另一个线程中执行(后台线程或工作线程)

     

    例如,下面是的代码是响应click事件,在另外一个线程中下载一个图片并在一个ImageView中显示它:

     

     

    1. public void onClick(View v) {    
    2.   
    3.     new Thread(new Runnable() {    
    4.   
    5.         public void run() {    
    6.   
    7.             Bitmap b = loadImageFromNetwork("http://example.com/image.png");    
    8.   
    9.             mImageView.setImageBitmap(b);    
    10.   
    11.         }    
    12.   
    13.     }).start();    
    14.   
    15. }    


    第一眼,这看起来能很好的工作,因为它创建了一个新线程来进行网络操作。然而它违反了第二条规则:不要在界面线程之外操作界面—它简单的在工作线程中修改了ImageView。这会导至未定义的异常出现,并且难以调试追踪。

    为了能改正这个问题,Android提供了很多从其它线程来操作界面的方法。下面是可用的方法们:

     

     Activity.runOnUiThread(Runnable)

     

     View.post(Runnable)

     

     View.postDelayed(Runnable,long)

     

     

    例如,你可以用View.post(Runnable)来修正上面的问题:

     

    1. public void onClick(View v) {    
    2.   
    3.     new Thread(new Runnable() {    
    4.   
    5.         public void run() {    
    6.   
    7.             final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");    
    8.   
    9.             mImageView.post(new Runnable() {    
    10.   
    11.                 public void run() {    
    12.   
    13.                     mImageView.setImageBitmap(bitmap);    
    14.   
    15.                 }    
    16.   
    17.             });    
    18.   
    19.         }    
    20.   
    21.     }).start();    
    22.   
    23. }    


     

    现在这个实现终于是线程安全的了:网络操作在另一个线程中并且ImageView在界面线程中改变。

     

     

     

    AsyncTask使你可以在你的界面上执行异步工作。它在一个工作线程中执行耗时操作然后把结果传给界面线程,不需要你亲自去管理线程和句柄。

    要使用它,你必须从AsyncTask派生一个类并实现回调方法doInBackground(),此方法在一个后台的线程池中运行。要更新你的界面,你应实现onPostExecute(),它把doInBackground()的结果弄过来并在界面线程中运行,于是你可以安全地更新你的界面。你可以在界面线程中调用execute()来执行AsyncTask任务。

    例如,你可以把上一章中的例子用AsyncTask实现:

     

    1. public void onClick(View v) {    
    2.     new DownloadImageTask().execute("http://example.com/image.png");    
    3. }    
    4. private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {    
    5.     /** 系统把给予AsyncTask.execute() 的参数传给这个方法并且在后台线程中调用这个方法完成工作*/    
    6.     protected Bitmap doInBackground(String... urls) {    
    7.         return loadImageFromNetwork(urls[0]);    
    8.     }    
    9.         
    10.     /** 系统把doInBackground()的执行结果传送过来并且在界面线程中调用这个方法执行任务  */    
    11.     protected void onPostExecute(Bitmap result) {    
    12.         mImageView.setImageBitmap(result);    
    13.     }    
    14. }    


     

     

     

    现在界面变得安全并且代码变得简单了。因为把工作分成了在工作线程中完成的部分和在界面线程中完成的部分。

    你应该阅读AsyncTask的参考来完全理解如何使用这个类。这里是一个对它如何工作的概览:

    你可以使用范型指定参数的类型,进度值,和任务的最终值。

    方法doInBackground()自动在工作线程中执行

     

    onPreExecute(),onPostExecute(), onProgressUpdate()都在界面线程中执行

    doInBackground()返回的值被传递到onPostExecute()

     

    你可以在任何时刻在doInBackground()中调用publishProgress()引起在界面线程中执行onProgressUpdate()你可以在任意时刻在任何线程中取消任务

     

    警告:另一个你在使用工作线程时可能遇到的问题是activity的意外重启(比如屏幕的方向变了),这可能销毁你的工作线程。要了解如何才能在此类现象发生时避免你的任务被杀以及如何在activity死亡时正确的取消你的任务,请参阅Shelves例子的源码。

     

     

    线程安全

     

    在某些情况下,你实现的方法可能会在不同的线程中调用,所以它们必须被实现成“线程安全的”。

     

    这首先体现在可以远程调用的方法上—比如在boundservice中的方法。当在一个进程中调用同一个进程中的IBinder上所实现的方法时,这个方法会在调用者的线程中运行(IBinder不理解的看这里:http://www.linuxidc.com/Linux/2012-02/53482.htm)。然而,当这个调用从另一个进程中发出时,方法会在线程池中某个被选出的线程中执行,这个线程在与IBinder相同的进程中(它不是运行于进程的界面线程中)。例如,尽管一个serviceonBind()方法在service所在进程的界面线程中调用,onBian()所返回的对象的方法(例如,一个子类所实现的那些RPC方法们)将在线程池的线程中调用。因为一个service可以拥有多个客户端,某个时刻可能一个或多个池中线程被用于执行一个Ibinder的同一个方法。IBinder的方法们因此必须被实现为线程安全的。

     

    同样的,一个内容提供者也可能收到来自不同线程的数据请求。尽管ContentResolverContentProvider类隐藏了进程间通信的管理细节,ContentProvider的那些响应请求的方法们—query(),insert(), delete(), update(),getType()—会被内容提供者所在的进程中的线程池中的线程调用,而不是被进程的界面线程调用。因为这些方法们可能在同一时刻被任意数量的线程调用,所以它们必须被实现为线程安全的。

     

    进程间通信

     

        Android通过远程调用(RPCs)提供了进程间通信(IPC)机制,于是一个方法可以被另一个应用的Activity调用,但是它却远程执行(在另一个进程中),而又把结果返回给调用者。这使得必须分解一个方法调用和它的数据到操作系统能理解的水平,把它们从本地进程和地址空间传到远程进程和地址空间,然后重组并重演调用过程。要返回的结果以相反的方向传送。Android提供了所有的执行这些IPC传输的代码,于是你可以专注于定义和实现RPC程序接口。

     

    要执行IPC,你的应用必须绑定到一个service,使用方法bindService()

    展开全文
  • 一,Android 进程线程 进程-Process 是程序的一个运行实例,以区别于“程序”这一静态的概念,线程-Thread则是cpu调度的基本单位。 一个进程的创建是通过Process.start()来完成的,其中的参数可以定制进程启动后...

    一,Android 进程和线程

    进程-Process 是程序的一个运行实例,以区别于“程序”这一静态的概念,线程-Thread则是cpu调度的基本单位。

    一个进程的创建是通过Process.start()来完成的,其中的参数可以定制进程启动后最先执行的线程,通常会指定为ActivityThread,所以一个进程起来后先运行的就是这个主线程。Process会通过socket把创建进程的请求发给zygote,最终由zygote来fork一个新的进程。

    Android中的程序和进程具体是个什么概念呢?

    对于Android的应用程序来说,通常接触的都是Activity,Service,Receive,ContentProvider等组件,四大组件,它们不能算是完整的进程实例,最多只能算是进程的组成部分,从AndroidManifest.xml中可以看出,在这些组件的最外围还有一个application 标签,四大组件只是 application 的零件。我们以一个有IDE向导生成的简单的工程来看看一个应用中的进程和线程。

    在自动生成的源码中MainActivity.java中的onCreate()函数入口处加上断点,如图:

    那么,这个Activity启动后,会产生几个Thread 呢,如图:

    除了我们熟悉的main thread外,还有2个Binder Thread,因为应用程序启动过程中需要跟系统进程ActivityManagerService、WindowManagerService等通信,需要Binder线程。那么主线程是怎么产生?可以看到主线程是有ZygoteInit启动,经过一系列的调用最终执行了Activity本身的onCreate()函数,从这里知道,主线程就是ActivityThread。

    frameworks/base/core/java/android/app/ActivityThread.java

    public static void main(String[] args) {

    Looper.prepareMainLooper(); 主线程会调用这个函数,普通线程调用prepare()

    ActivityThread thread = new ActivityThread();

      thread.attach(false); 这个函数将于windowManagerService 建立联系,因为Activity是有界面显示的

    if (sMainThreadHandler == null) {

      sMainThreadHandler = thread.getHandler();主线程对应的handler

    }

     Looper.loop(); 主循环开始

    }

    在ActivityThread内部,还有一个ApplicationThread,这个ApplicationThread是应用进程跟ActivityManagerService进行跨进程通信的桥梁,比如AMS启动一个Activity,实际是先由ApplicationThread的scheduleLaunchActivity,然后到ActivityThread的performLaunchActivity。

    实际上,所有应用程序的主线程都是Activity Thread,这里使用的组件是Activity,如果换成Service也是一样的,并且在一个应用程序中,主线程只有一个,对于同一个AndroidManifest.xml中定义的组件,除非有特别的指明(可以指定一个组件运行在某个进程空间,使用android:process属性,也可以在<application>标签中加入这个属性,指明想要依存的进程环境),否则它们都运行于同一个进程中。

    二,Handler,MessageQueue,Runnable,Looper

    1,Handler 代码路径:framework/base/core/java/android/os/Handler.java

    public class Handler{

    final MessageQueue mQueue;

    final Looper mLooper;

    IMessenger mMessenger;

    final Callback mCallback;。。。

    }

    这里提到的几个主要元素的关系,简单说就是:Looper 不断从MessageQueue中取出消息message,然后交给Handler来处理。

    每个Thread只对应一个Looper

    每个Looper只对应一个MessageQueue

    每个MessageQueue中有N个Message

    每个Message最多指定一个Handler来处理事件

    每个Thread可以对应多个Handler

    Handler是经常会使用到的一个类,主要有两个方面的作用:一是处理Message,二是将某个message压入messageQueue中。Looper从MessageQueue中取出一个Message后,首先会调用Handler.dispatchMessage进行消息派发,默认的派发流程:

       /**

     

         *Handle system messages here.

         */

        publicvoid dispatchMessage(Message msg) {

            if(msg.callback != null) {

               handleCallback(msg);

            }else {

               if (mCallback != null) {

                   if (mCallback.handleMessage(msg)) {

                        return;

                   }

               }

               handleMessage(msg);

            }

        }

    由这个函数看出,Handler的扩展子类可以通过重载dispatchMessage或handleMessage来改变它的默认行为。

    处理优先级是消息本身的callback,最有优先权,其次是handler的callback,

    其中msg的callback是在其生成Message对象时设置的,比如post(Runnable r)时,会先把runnable转成message,同时把runnable设置为msg的callback;

    handler的callback是在实例化handler对象时。作为Handler的构造函数的参数,传入的一个实现了Handler.Callback的对象。

     

    把一个消息压入消息队列的相应功能函数:

    public final boolean post(Runnable r)

     public final boolean postAtTime(Runnable r, long uptimeMillis)

     public final boolean sendMessage(Message msg)

    public final boolean sendMessageDelayed(Message msg, long delayMillis)。。。

    以第一个函数为例:

        public final boolean post(Runnable r)
        {
           return  sendMessageDelayed(getPostMessage(r), 0);
        }首先要把Runnable 对象,封装成一个Message,接着通过对应的send函数推送到messageQueue中,

        private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            m.callback = r;
            return m;
        }这里将Runnable对象设置为Message的回调。

    整个过程中,message通过handler压入messagequeue中,然后由looper从messagequeue中取出消息,还是交给了handler来处理,为什么不直接操作,而是大费周折的转了这么一圈?这里体现了程序设计的一个良好习惯“有序性”。

    2,MessageQueue,源码路径:frameworks/base/core/java/android/os/MessageQueue.java

    就是一个消息队列,具有“队列”的一些常规操作:

    新建队列:由本地方法nativeInit来完成。

    元素入队: boolean enqueueMessage(Message msg, long when)

    元素出队:Message next()

    删除元素:void removeMessages(Handler h, int what, Object object)

    其中有一个接口IdleHandler,

        public static interface IdleHandler {
            boolean queueIdle();
        }

    这是一个回调接口,当线程队列中没有消息时,线程会进入block状态,这时会调用其queueIdle()方法,如果返回值为true,表示这个idlehandler将保持active状态,否则会被删除,如果你希望在线程处于空闲时,要做什么事情,可以实现这个接口,然后添加到消息队列中。比如说GC的执行,可能就需要在线程空闲时执行。

     

    3,Looper,源码路径:frameworks/base/core/java/android/os/

    从Looper的源码看出,Looper中包含了一个MessageQueue队列。

    使用Looper的线程,有三个步骤:

    1)Looper的准备工作(prepare

    2)创建处理消息的Handler

    3)Looper开始运作(loop)

    首先:Looper.prepare() Looper中有一个很重要的成员变量,   

     // sThreadLocal.get() will return null unless you've called prepare().

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();ThreadLocal对象是一个特殊的全局变量,因为它的“全局”性只限于自己所在的线程,而外界的线程(即使在同一进程)一概无法访问到他,这从侧面说明,每个线程的Looper都是独立的。它实际就是针对每个Thread的特定数据存储空间。

        private static void prepare(boolean quitAllowed) {
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            sThreadLocal.set(new Looper(quitAllowed));
        }

    这个函数里的判断保证了一个Thread只会有一个Looper实例存在,sThreadLocal创建了一个只针对当前线程的Looper对象。

    在使用Looper的线程里,肯定会创建Handler对象,所以mHandler是Thead实现类的成员变量,那么,Handler如何与Looper关联起来的呢?从Handler的构造函数分析:

    public Handler()

    public Handler(Callback callback)

    public Handler(Looper looper, Callback callback) 

    public Handler(Looper looper, Callback callback, boolean async)

    之所以有这么多构造函数,是因为Handler有如下内部变量需要初始化:

     final MessageQueue mQueue;

     final Looper mLooper;

     final Callback mCallback;

    以其中一个构造函数为例:

        public Handler(Callback callback, boolean async) {
            if (FIND_POTENTIAL_LEAKS) {
                final Class<? extends Handler> klass = getClass();
                if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                        (klass.getModifiers() & Modifier.STATIC) == 0) {
                    Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                        klass.getCanonicalName());
                }
            }
            mLooper = Looper.myLooper();  //
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }

      mLooper = Looper.myLooper();  还是通过sThreadLocal.get来获取当前线程中的Looper实例。

    mQueue = mLooper.mQueue; mQueue是Looper跟handler之间沟通的桥梁。这样Handler和Looper,MessageQueue就联系起来了,后续Handler执行post/send系列函数时,会将消息投递到mQueue也即是mLooper.mQueue中。

    3,UI主线程 Activitythread

    源码路径:frameworks/base/core/java/android/app/ActivityThread.java

       public static void main(String[] args) {
      ...
            Looper.prepareMainLooper();


            ActivityThread thread = new ActivityThread();
            thread.attach(false);


            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }

            Looper.loop();

            throw new RuntimeException("Main thread loop unexpectedly exited");
        }

    注意区别,普通线程调用prepare(),这里调用prepareMainLooper(),主线程的Handler是从当前线程中获得的thread.getHandler();。

        public static void prepareMainLooper() { //这个是Looper.java中的方法
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }prepareMainLooper也需要调用prepare,参数false表示该线程不允许退出。经过prepare后,myLooper就得到一个本地线程<ThreadLocal>的Looper对象,然后赋值给sMainLooper,从这里看,主线程和其他线程的Looper对象没有本质的区别。

    作为主线程,它这么做的目的,就是其他线程如果要获取主线程的Looper,只需要调用getMainLooper()即可。

    作为普通线程,它生成的Looper对象,只能在线程内通过myLooper()访问。

    在来看下Looper.loop的是实现,也是Looper.java中的方法

     public static void loop() {
            final Looper me = myLooper(); 函数myLooper()则是调用sThreadLocal.get()来获取与之匹配的Looper实例,其实就是取出在prepare中创建的那个Looper对象。
            final MessageQueue queue = me.mQueue; //Looper中自带了一个MessageQueue

            for (;;) {
                Message msg = queue.next(); // might block从MessageQueue中取出消息,可能会阻塞,如果当前消息队列中没有消息,说明线程要退出了
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }

                msg.target.dispatchMessage(msg); 开始分发消息,这里的target就是一个handler,所以dispatch最终调用的是handler中的处理函数

                msg.recycleUnchecked(); 消息处理完毕,进行回收
            }
        }

    在来看一个函数,Looper的构造函数:

        private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed); //new 了MessageQueue,也就是Looper在创建是,消息队列也同时被创建出来
            mThread = Thread.currentThread(); Looper与当前线程建立了对应关系
        }

    从上面的loop()函数看出,它里面是是一个死循环,那为什么主线程中的这个死循环没有卡死呢?

    原因是:messagequeue的处理是一个消费者、生产者模式,当有消息要处理时,会dispatch消息,当没有消息需要处理(这里也包含带有延迟的消息,也就是消息处理的时间还没到)会wait,然后让出cpu的时间,当有新消息入队,或者阻塞时间到了,再wake主线程,继续分发消息。

    另外一点,如果子线程调用了prepare,loop进入了循环,那么在子线程退出时,记得调用looper的quitSafety方法,让消息循环正常结束。

    4,Thread类

    1)Thread类的内部原理,源码路径  libcore/libart/src/main/java/java/lang/Thread.java

    public class Thread implements Runnable{

    Thread实现了Runnable,也就是说线程是“可执行的代码”。

    libcore/libuni/src/main/java/java/lang/Runnable.java  Runnable是一个抽象的接口,唯一的方法就是run方法

    public interface Runnable {
        /**
         * Starts executing the active part of the class' code. This method is
         * called when a thread is started that has been created with a class which
         * implements {@code Runnable}.
         */
        public void run();
    }

    通常我们是这样使用Thread的:

    方法1,定义一个类继承自Thread,重写它的run方法,然后调用start

    MyThread thr= new Mythread();

    thr.start();

    方法2,直接实现Runnable,

    new Thread(Runnable target).start();

    两种方法都是通过start启动,它会间接调用run方法,

        public synchronized void start() {
            checkNotStarted();
            hasBeenStarted = true;
            nativeCreate(this, stackSize, daemon); 这里是真正创建一个cpu线程的地方,在此之前,一直都是运行在“老线程”中,实际上在新线程中运行的只有Run方法。
        }

    2)Thread的休眠与唤醒,来看一下与此相关的控制方法:

    首先是:wait notify notifyAll,这三个函数是Object类定义的,意味着它们是任何类的共有“属性”,下面的方法是Handler.java中的内部类:BlockingRunnable的,涉及到等待时,会调用它

            public boolean postAndWait(Handler handler, long timeout) {
                if (!handler.post(this)) {
                    return false;
                }
                synchronized (this) {
                    if (timeout > 0) {
                        final long expirationTime = SystemClock.uptimeMillis() + timeout;
                        while (!mDone) {
                            long delay = expirationTime - SystemClock.uptimeMillis();
                            if (delay <= 0) {
                                return false; // timeout
                            }
                            try {
                                wait(delay);
                            } catch (InterruptedException ex) {
                            }
                        }
                    } else {
                        while (!mDone) {
                            try {
                                wait();
                            } catch (InterruptedException ex) {
                            }
                        }
                    }
                }
                return true;
            }这个函数在Handler中的作用就是“投递并等待”,所以函数开头就把一个runnable(this)post到了handler所在的looper中。如果timeout大于0,说明是有条件限制的等待,这样可以避免异常时间下的“死等”;如果timeout等于0,无限期等待,直到有人来唤醒他。线程进入等待调用的是wait(),唤醒他就是notify/notifyAll,那么什么时候执行的唤醒操作呢?

        private static final class BlockingRunnable implements Runnable {
            public BlockingRunnable(Runnable task) {
                mTask = task;
            }

            @Override
            public void run() {
                try {
                    mTask.run();
                } finally {
                    synchronized (this) {
                        mDone = true;
                        notifyAll(); BlockingRunnable 对象在执行run函数时,同时也做了这个特殊的操作,通知所有在等待的人,我运行OK了。
                    }
                }
            }

    然后,interrupt,如果说wait是一种“自愿”的行为,那么interrupt就是被迫的了,调用一个线程的interrupt这个方法,就是中断它的执行过程。

    join方法的几个原型:

     public final void join()

     public final void join(long millis)

    public final void join(long millis, int nanos)

    比如:Thread t1;Thread t2;

    t1.start();

    t1.join();

    t2.start(); 它希望的目的是只有当t1线程执行完成时,才接着执行后面的t2.start(),这样就保证了两个线程的顺序执行。带有参数的join多了一个限制,假如在规定的时间内t1没有执行完成,那么会继续执行后面的语句,防止无限等待。

    最后,sleep方法,它和wait类似,都属于自愿的行为,只是wait是等待某个object,而sleep则是等待时间,一旦设置的时间到了就会被唤醒。

    展开全文
  • Android进程线程

    2016-02-29 21:55:57
    Android进程回收,最重要的是考量内存开销,以及电量等其他资源状况,此外每个进程承载的组件数量、单个应用开辟的进程数量等数量指标,也是作为衡量的一个重要标识。另外,一些运行时的时间开销,也被严格监控,...

    Android的进程回收,最重要的是考量内存开销,以及电量等其他资源状况,此外每个进程承载的组件数量、单个应用开辟的进程数量等数量指标,也是作为衡量的一个重要标识。另外,一些运行时的时间开销,也被严格监控,启动慢的进程会很被强行kill掉。Android会定时检查上述参数,也会在一些很可能发生进程回收的时间点,比如某个组件执行完成后,来做回收的尝试。

        进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。

    线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。

    进程和线程的关系:
    (1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
    (2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。
    (3)处理机分给线程,即真正在处理机上运行的是线程。

    (4)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。


    进程与线程的区别:
    (1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
    (2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
    (3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.

    (4) 系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。但是进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个进程死掉就等于所有的线程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些


    结论:

    (1)线程是进程的一部分

    (2)CPU调度的是线程

    (3)系统为进程分配资源,不对线程分配资源

    对比维度

    多进程

    多线程

    总结

    数据共享、同步

    数据共享复杂,需要用IPC;数据是分开的,同步简单

    因为共享进程数据,数据共享简单,但也是因为这个原因导致同步复杂

    各有优势

    内存、CPU

    占用内存多,切换复杂,CPU利用率低

    占用内存少,切换简单,CPU利用率高

    线程占优

    创建销毁、切换

    创建销毁、切换复杂,速度慢

    创建销毁、切换简单,速度很快

    线程占优

    编程、调试

    编程简单,调试简单

    编程复杂,调试复杂

    进程占优

    可靠性

    进程间不会互相影响

    一个线程挂掉将导致整个进程挂掉

    进程占优

    分布式

    适应于多核、多机分布式;如果一台机器不够,扩展到多台机器比较简单

    适应于多核分布式

    进程占优

      “消息收发”和“消息处理”可以分进程设计,“消息解码”、“业务处理”可以分线程设计。

    进程是程序执行时的一个实例,即它是程序已经执行到课中程度的数据结构的汇集。从内核的观点看,进程的目的就是担当分配系统资源(CPU时间、内存等)的基本单位

    线程是进程的一个执行流,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。一个进程由几个线程组成(拥有很多相对独立的执行流的用户程序共享应用程序的大部分数据结构),线程与同属一个进程的其他的线程共享进程所拥有的全部资源。

    进程有独立的地址空间,线程没有单独的地址空间(同一进程内的线程共享进程的地址空间)

    (下面的内容摘自Linux下的多线程编程

    使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右,当然,在具体的系统上,这个数据可能会有较大的区别。

      使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。

      在Unix上编程采用多线程还是多进程的争执由来已久,这种争执最常见到在B/S通讯中服务端并发技术 的选型上,比如WEB服务器技术中,Apache是采用多进程的(perfork模式,每客户连接对应一个进程,每进程中只存在唯一一个执行线 程),Java的Web容器Tomcat、Websphere等都是多线程的(每客户连接对应一个线程,所有线程都在一个进程中)。

    从Unix发展历史看,伴随着Unix的诞生多进程就出现了,而多线程很晚才被系统支持,例如Linux直到内核2.6,才支持符合Posix规范的NPTL线程库。进程和线程的特点,也就是各自的优缺点如下:

    进程优点:编程、调试简单,可靠性较高。
    进程缺点:创建、销毁、切换速度慢,内存、资源占用大。
    线程优点:创建、销毁、切换速度快,内存、资源占用小。
    线程缺点:编程、调试复杂,可靠性较差。

    进程间通讯(IPC)方法主要有以下几种:   

        管道/FIFO/共享内存/消息队列/信号   

    1.管道中还有命名管道和非命名管道(即匿名管道)之分,非命名管道(即匿名管道)只能用于父子进程通讯,命名管道可用于非父子进程,命名管道就是FIFO,管道是先进先出的通讯方式    

    2.消息队列是用于两个进程之间的通讯,首先在一个进程中创建一个消息队列,然后再往消息队列中写数据,而另一个进程则从那个消息队列中取数据。需要注意的是,消息队列是用创建文件的方式建立的,如果一个进程向某个消息队列中写入了数据之后,另一个进程并没有取出数据,即使向消息队列中写数据的进程已经结束,保存在消息队列中的数据并没有消失,也就是说下次再从这个消息队列读数据的时候,就是上次的数据!!!!    

    3.信号量,它与WINDOWS下的信号量是一样的,所以就不用多说了    

    4.共享内存,类似于WINDOWS下的DLL中的共享变量,但LINUX下的共享内存区不需要像DLL这样的东西,只要首先创建一个共享内存区,其它进程按照一定的步骤就能访问到这个共享内存区中的数据,当然可读可写      

    以上几种方式的比较:    

    1.管道:速度慢,容量有限,只有父子进程能通讯    

    2.FIFO:任何进程间都能通讯,但速度慢    

    3.消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题    

    4.信号量:不能传递复杂消息,只能用来同步    

    5.共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存

    》 使用多进程的优点是:

    A.父进程Crash之后,子进程还存在,可以继续干想干的事情

    B.由于Android单个进程所能够使用的内存是有限制的,故多个进程可以使用更多的内存,不用担心单个进程的内存吃紧

      多进程之间的通信:

    A.AIDL

    B.Socket

    一.Linux系统进程间通信有哪些方式?

    1.socket;

    2.name pipe命名管道;

    3.message queue消息队列;

    4.singal信号量;

    5.share memory共享内存;

    二.Java系统的通信方式是什么?

    1.socket;

    2.name pipe;

    三.Android系统通信方式是什么?

    Binder 通信;

    四.Binder通信的优势是什么?

    高效率

    五.Binder通信的特点是什么?

    是同步,而不是异步;

    六.Binder通信是如何实现的?

    1.Binder通信是通过linux的binder driver来实现的,

    2.Binder通信操作类似线程迁移(threadmigration),两个进程间IPC看起来就象是一个进程进入另一个进程执行代码然后带着执行的结果返回;

    3.Binder的用户空间为每一个进程维护着一个可用的线程池,线程池用于处理到来的IPC以及执行进程本地消息,Binder通信是同步而不是异步。

    七. Android中的 Binder通信实现要点:

    1. Android中的Binder通信是基于Service与Client的工作模型的;

    2. 所有需要IBinder通信的进程都必须创建一个IBinder接口;

    3. 系统中有一个进程管理所有的system service:

    4. Android不允许用户添加非授权的Systemservice;

    5. 现在源码开放了,我们可以修改一些代码来实现添加底层system Service的目的;

    6. 对用户程序来说,我们也要创建server,或者Service用于进程间通信;

    7. ActivityManagerService管理JAVA应用层所有的service创建与连接(connect),disconnect;

    8. 所有的Activity也是通过这个service来启动,加载的;

    9. ActivityManagerService也是加载在Systems Servcie中的;

    八.Android的 Service工作流程

    1.Android虚拟机启动之前系统会先启动service Manager进程;2.service Manager打开binder驱动,并通知binder kernel驱动程序这个进程将作为System Service Manager;

    3.然后该进程将进入一个循环,等待处理来自其他进程的数据。4.用户创建一个System service后,通过defaultServiceManager得到一个远程ServiceManager的接口,通过这个接口我们可以调用addService函数将System service添加到Service Manager进程中;

    5.然后client可以通过getService获取到需要连接的目的Service的IBinder对象,这个IBinder是Service的BBinder在binder kernel的一个参考,

    6.所以serviceIBinder 在binder kernel中不会存在相同的两个IBinder对象,每一个Client进程同样需要打开Binder驱动程序。对用户程序而言,我们获得这个对象就可以通过binderkernel访问service对象中的方法。

    7.Client与Service在不同的进程中,通过这种方式实现了类似线程间的迁移的通信方式,对用户程序而言当调用Service返回的IBinder接口后,访问Service中的方法就如同调用自己的函数。






    展开全文
  • 本文将结合百度,博客等,对Android进程线程进行一次全面的总结。进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。进程是系统进行资源分配和调度的一个独立单位。可以申请和拥有系统资源,是一个...

    说起进程与线程大家心里想必还是有点概念的,但是却说不清楚。
    什么一条进程里面多条线程,学过Java的都知道异步线程这个十分重要的技术。但是真要实实在在说清楚他们的概念,区别,关联等等,还是很难的。
    本文将结合百度,博客等,对Android的进程与线程进行一次全面的总结。

    进程

    是一个具有独立功能的程序关于某个数据集合的一次运行活动。进程是系统进行资源分配和调度的一个独立单位。可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体,是一个“执行中的程序”。不只是程序的代码,还包括当前的活动。
    在默认情况下同一app的所有组件都是运行在同一进程中的,而且大多数app并不需要去更改这个设定。但如果我们真的需要指定进程来运行特定组件,那么可以在manifest文件中设置。我们在manifest文件中定义了各个组件,例如activity,service,receiver,provider等等,我们可以设置process属性来指定一个新线程来运行该组件。通过设置process属性,我们可以设置每个组件都运行在不同的进程中,也可以指定几个组件运行在同一个进程中。我们甚至可以设置不同来自app的组件运行在同一个进程中(通过指定相同的process属性并共享相同的user ID)。

    进程的生命周期

    1. 前台进程foreground process:当前正在与用户交互的进程。
    2. 可见进程Visable process:没有任何前台组件在此进程中运行,但是仍然可以影响到用户所看到的屏幕。
    3. 服务进程service process:例如音乐播放,文件下载等等。
    4. 后台进程background process:无法直接影响到用户体验,系统可能随时杀死此类进程回收内存供以上三种进程使用。
    5. 空进程empty process:该进程中没有运行任何组件,保持此类进程存在的唯一原因就是等待任务。一旦有组件需要运行,则可以缩短进程启动时间。

    进程通信

    1. Bundle
      四大组件中三大组件Activity、Service、Receiver都支持在Intent中传递Bundle数据。
      由于Bundle实现了Parcelable接口,所以它可以很方便的在不同的进程间传输数据。当然我们传输的数据必须能够被序列化,比如基本类型、实现了Parcelable接口的对象、实现了Serializable接口的对象以及一些Android支持的特殊对象。

    2. 使用文件共享/数据库
      两个进程通过读写同一个文件来交换数据,比如A进程把数据写入文件,B进程通过读取这个文件来获取数据。
      Android系统基于Linux,使得并发读写文件可以没有限制的进行,甚至两个线程同时对文件读写操作都是允许的,尽管可能出问题,因此文件共享方式适合在对数据同步要求不高的进程间进行通信。
      SharedPreferences也属于文件的一种,但是由于系统对它的读写有一定的缓存策略,即在内存中会有一份SharedPreferences文件的缓存;因此在多进程模式下,系统对它的读写就变得不可靠,会有很大几率丢失数据,不建议在进程间通信中使用SharedPreferences。

    3. Messenger
      Messenger可以理解为信使,通过它可以再不同进程中传递Message对象,在Message中放入我们需要传递的数据,就可以实现数据的进程间传递了。
      Messenger是一种轻量级的IPC方案,它的底层实现是AIDL。由于它一次处理一个请求,因此在服务端不需要考虑线程同步的问题,因为服务端不存在并发执行的情形。

    4. 使用AIDL
      AIDL是 Android Interface definition language的缩写,它是一种android内部进程通信接口的描述语言。AIDL可以处理发送到服务器端大量的并发请求(不同与Messenger的串行处理方式),也可以实现跨进程的方法调用。
      在Android中使用方法:创建一个Service和一个AIDL接口,接着创建一个类继承自AIDL接口中的Stub类并实现Stub中的抽象方法,在Service的onBind方法中返回这个类的对象,然后客户端绑定服务端Service,建立连接后就可以访问远程服务器了。

    5. 使用ContentProvider
      ContentProvider是Android中提供的专门用于不同应用间进行数据共享的方式,天生适合进程间通信。
      ContentProvider的底层实现也是Binder,但是它的使用过程比AIDL简单的多,因为系统做了封装,使得无需关心细节即可轻松实现IPC。ContentProvider主要以表格的形式组织数据,和数据库很类似,但ContentProvider对底层的数据存储方式没有任何要求,既可以使用Sqlite数据库,也可以使用文件方式,甚至可以使用内存中的一个对象来存储。

    6. Socket
      Socket套接字,是网络通信中的概念,分为流式套接字和用户数据奥套接字两种,对应于网络的传输控制层中的TCP和UDP协议。
      两个进程可以通过Socket来实现信息的传输,Socket本身可以支持传输任意字节流。

    7. BroadcastReceiver

    线程

    当应用程序启动后,系统将会创建一个主线程来运行应用程序。主线程非常重要,它负责为适当的用户控件分发任务和事件,包括绘制任务等等。同时,主线程也负责UI组件和应用程序的交互,所以我们也称主线程为UI线程。
    除了主线程之外,耗时操作都应该规范到子线程中,线程之间会有相应的通信方式,但相互独立。
    系统并不会为每个组件单独开启一个线程来运行,所有的组件都会在主线程中初始化并运行运行在同一个进程中,系统通过主线程来调用每个组件。所以,系统回调方法(例如onKeyDown,生命周期回调方法等)通常运行于主线程。

    线程间通信

    1. 全局变量
    2. Handler机制
    3. 使用AsyncTask
    4. View.postDelay(Runnable , long)
    5. runOnUiThread(Runnable)
    runOnUiThread(new Runnable(){//更新UI
                        @Override
                        public void run() {
                        //更新UI
                        }    
                    });

    进程与线程的关系与区别

    1. 子进程和父进程有不同的代码和数据空间,而多个线程则共享数据空间,每个线程有自己的执行堆栈和程序计数器为其执行上下文。
    2. 进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
    3. 进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
    4. 线程上下文切换比进程上下文切换要快得多。
    5. 在操作系统中进程是OS分配资源的最小单位,而线程是执行任务的最小单位。
    6. 一个进程可以拥有多个线程执行任务,这些线程可以共享该进程分配到的资源。
    7. 当我们的app启动运行后,在该app没有其他组件正在运行的前提下,Android系统会启动一个新Linux进程来运行app,这个进程只包含了一个线程在运行。在默认情况下,app的组件都运行在该进程中,最初就包含的这个线程也被称为主线程或者UI线程。
    8. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程比进程更小,基本上不拥有系统资源,故对它的调度所用资源小,能更高效的提高系统内多个程序间并发执行的。

    本文参考

    http://www.cnblogs.com/pear-lemon/p/6547734.html
    http://www.cnblogs.com/cqumonk/p/4828616.html

    展开全文
  •  如果某个应用程序组件是第一次被启动,且这时应用程序也没有其他组件在运行,则Android系统会为应用程序创建一个包含单个线程的linux进程。默认情况下,同一个应用程序的所有组件都运行在同一个进程线程里(这个...
  • Android中的进程线程

    2016-06-01 15:02:49
    一个Android应用就是一个Linux进程,每个应用在各自的进程中运行,互不干扰,比较安全。一个应用对应一个主线程,就是通常所说的UI线程android遵守的就是单线程模型,所以说Ui操作不是线程安全的并且这些操作必须...
  • Android进程线程区别

    2015-07-05 12:04:52
    进程,常被定义为程序的执行,可以把一个进程看成一个独立的程序,在内存中有其完备的数据空间和代码空间。一个进程所拥有的数据和变量只属于他自己。  线程,某一进程中一路单独运行的程序。也就是线程存在于...
  • 什么是进程什么是线程 进程是指在系统中正在运行的一个应用程序;程序一旦运行就是进程 进程可以认为是程序执行时的一个实例。进程是系统进行资源分配的独立实体, 且每个进程拥有独立的地址空间。一个进程无法直接...
  • Android系统中的应用通常对应一个或多个进程进程内部会创建并执行多个线程,那我们如何监控其中单个线程的状态和详细情况呢?(如,CPU/内存使用率)。 Android系统本身是基于Linux系统的,所以Linux系统的查看...
  • Android进程线程与优先级一、本文的结论1、Android进程的优先级与垃圾回收机制相关,优先级越低被垃圾回收的机会越大。当内存不足的时候,总是低优先级的进程被最先回收;2、Android线程的优先级与调用顺序...
  • 前言:安卓的线程进程,很多互联网公司在面试的时候的(如果你投的是安卓实习生)必问的,今天系统的整理一下关于安卓进程线程的知识,希望能帮到有需要的人。 一、安卓的进程: 在开始具体啰嗦前,我们先开看...
  • 一、Android中的进程 当一个程序第一次启动的时候,Android会启动一个LINUX进程和一个主线程。默认的情况下,所有该程序的组件都将在该进程线程中运行。 同时,Android会为每个应用程序分配一个单独的LINUX用户...
  • 常用语句:  android.os.Process  //获取当前进程的方法  android.os.Process....android.os.Process.myPid():获取该进程的ID。 android.os.Process.myTid():获取调用进程的ID。 android.os.Process.my
  • 进程是系统进行资源分配的独立单元 设计思想:为了允许多个程序同时在内存中运行特点 进程是程序的一次执行过程。若程序执行两次甚至多次,则需要两个甚至多个进程进程是是正在运行程序的抽象。它代表运行的CPU,...
  • Android面试题(四大组件篇)...window、进程线程Android面试题(数据存储、view篇)   Window Q:Activity、View、Window三者之间的关系? https://blog.csdn.net/qq_21399461/article/details/79836806 ...
  • 当某个应用组件启动且该应用没有运行其他任何组件时,Android 系统会使用单个执行线程为应用启动新的 Linux 进程。默认情况下,同一应用的所有组件在相同的进程线程(称为“主”线程)中运行。 如果某个应用组件...
  • 当某个应用组件启动且该应用没有运行其他任何组件时,Android 系统会使用单个执行线程为应用启动新的 Linux 进程。默认情况下,同一应用的所有组件在相同的进程线程(称为“主”线程)中运行。 如果某个应用组件...
  • android中 什么是进程?什么是服务?什么是线程?  对于以上三个问题,我相信大家都会遇到.那么首先我们探讨下这三个问题吧.  进程:  进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源...
  • 恩恩,今天参加阿里的面试,被问到Android进程线程的区别,着实把我伤脑了,表示平时开发的时候只知道线程,并没有去仔细关注过进程,所以下来特地去查了以下资料,先说说线程: (1)在Android APP中,只允许...
1 2 3 4 5 ... 20
收藏数 84,171
精华内容 33,668