老罗:Android系统中的广播(Broadcast)机制简要介绍和学习计划

本文简要介绍了Android系统中的广播机制,作为进程间通信和组件间松耦合的重要方式。通过一个计数器服务的例子,展示了如何使用BroadcastReceiver在不同线程间传递数据。文章还探讨了广播机制在系统中的必要性和与其它通信方式的区别,并提供了一个简单的应用实例,解释了如何创建和使用广播。接下来的文章将深入分析广播的注册和发送过程。
摘要由CSDN通过智能技术生成

博客源址Android系统中的广播(Broadcast)机制简要介绍和学习计划

博客时间2011-08-31 01:12

   

 在Android系统中,广播(Broadcast)是在组件之间传播数据(Intent)的一种机制;这些组件甚至是可以位于不同的进程中,这样它就像Binder机制一样,起到进程间通信的作用;本文通过一个简单的例子来学习Android系统的广播机制,为后续分析广播机制的源代码作准备。

        在Android系统中,为什么需要广播机制呢?广播机制,本质上它就是一种组件间的通信方式,如果是两个组件位于不同的进程当中,那么可以用Binder机制来实现,如果两个组件是在同一个进程中,那么它们之间可以用来通信的方式就更多了,这样看来,广播机制似乎是多余的。然而,广播机制却是不可替代的,它和Binder机制不一样的地方在于,广播的发送者和接收者事先是不需要知道对方的存在的,这样带来的好处便是,系统的各个组件可以松耦合地组织在一起,这样系统就具有高度的可扩展性,容易与其它系统进行集成。

        在软件工程中,是非常强调模块之间的高内聚低耦合性的,不然的话,随着系统越来越庞大,就会面临着越来越难维护的风险,最后导致整个项目的失败。Android应用程序的组织方式,可以说是把这种高内聚低耦合性的思想贯彻得非常透彻,在任何一个Activity中,都可以使用一个简单的Intent,通过startActivity或者startService,就可以把另外一个Activity或者Service启动起来为它服务,而且它根本上不依赖这个Activity或者Service的实现,只需要知道它的字符串形式的名字即可,而广播机制更绝,它连接收者的名字都不需要知道。

        不过话又说回来,广播机制在Android系统中,也不算是什么创新的东西。如果读者了解J2EE或者COM,就会知道,在J2EE中,提供了消息驱动Bean(Message-Driven Bean),用来实现应用程序各个组件之间的消息传递;而在COM中,提供了连接点(Connection Point)的概念,也是用来在应用程序各个组间间进行消息传递。无论是J2EE中的消息驱动Bean,还是COM中的连接点,或者Android系统的广播机制,它们的实现机理都是消息发布/订阅模式的事件驱动模型,消息的生产者发布事件,而使用者订阅感兴趣的事件。

        废话说了一大堆,现在开始进入主题了,和前面的文章一样,我们通过具体的例子来介绍Android系统的广播机制。在这个例子中,有一个Service,它在另外一个线程中实现了一个计数器服务,每隔一秒钟就自动加1,然后将结果不断地反馈给应用程序中的界面线程,而界面线程中的Activity在得到这个反馈后,就会把结果显示在界面上。为什么要把计数器服务放在另外一个线程中进行呢?我们可以把这个计数器服务想象成是一个耗时的计算型逻辑,如果放在界面线程中去实现,那么势必就会导致应用程序不能响应界面事件,最后导致应用程序产生ANR(Application Not Responding)问题。计数器线程为了把加1后的数字源源不断地反馈给界面线程,这时候就可以考虑使用广播机制了。

        首先在Android源代码工程中创建一个Android应用程序工程,名字就称为Broadcast吧。关于如何获得Android源代码工程,请参考在Ubuntu上下载、编译和安装Android最新源代码一文;关于如何在Android源代码工程中创建应用程序工程,请参考在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务一文。这个应用程序工程定义了一个名为shy.luo.broadcast的package,这个例子的源代码主要就是实现在这里了。下面,将会逐一介绍这个package里面的文件。 

        首先,我们在src/shy/luo/broadcast/ICounterService.java文件中定义计数器的服务接口:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package shy.luo.broadcast;  
  2.   
  3. public interface ICounterService {  
  4.         public void startCounter(int initVal);  
  5.         public void stopCounter();  
  6. }  
       这个接口很简单,它只有两个成员函数,分别用来启动和停止计数器;启动计数时,还可以指定计数器的初始值。

       接着,我们来看一个应用程序的默认Activity的实现,在src/shy/luo/broadcast/MainActivity.java文件中:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package shy.luo.broadcast;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.BroadcastReceiver;  
  5. import android.content.ComponentName;  
  6. import android.content.Context;  
  7. import android.content.Intent;  
  8. import android.content.IntentFilter;  
  9. import android.content.ServiceConnection;  
  10. import android.os.Bundle;  
  11. import android.os.IBinder;  
  12. import android.util.Log;  
  13. import android.view.View;  
  14. import android.view.View.OnClickListener;  
  15. import android.widget.Button;  
  16. import android.widget.TextView;  
  17.   
  18. public class MainActivity extends Activity implements OnClickListener {  
  19.     private final static String LOG_TAG = "shy.luo.broadcast.MainActivity";  
  20.          
  21.     private Button startButton = null;  
  22.     private Button stopButton = null;  
  23.     private TextView counterText = null;  
  24.       
  25.     private ICounterService counterService = null;  
  26.       
  27.         @Override  
  28.         public void onCreate(Bundle savedInstanceState) {  
  29.             super.onCreate(savedInstanceState);  
  30.             setContentView(R.layout.main);  
  31.           
  32.             startButton = (Button)findViewById(R.id.button_start);  
  33.             stopButton = (Button)findViewById(R.id.button_stop);  
  34.             counterText = (TextView)findViewById(R.id.textview_counter);  
  35.           
  36.             startButton.setOnClickListener(this);  
  37.             stopButton.setOnClickListener(this);  
  38.           
  39.             startButton.setEnabled(true);  
  40.             stopButton.setEnabled(false);  
  41.           
  42.             Intent bindIntent = new Intent(MainActivity.this, CounterService.class);  
  43.             bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);  
  44.           
  45.             Log.i(LOG_TAG, "Main Activity Created.");  
  46.         }  
  47.       
  48.         @Override   
  49.         public void onResume() {  
  50.             super.onResume();  
  51.           
  52.             IntentFilter counterActionFilter = new IntentFilter(CounterService.BROADCAST_COUNTER_ACTION);  
  53.         registerReceiver(counterActionReceiver, counterActionFilter);  
  54.         }  
  55.       
  56.         @Override  
  57.         public void onPause() {  
  58.             super.onPause();  
  59.             unregisterReceiver(counterActionReceiver);  
  60.         }  
  61.       
  62.         @Override  
  63.         public void onDestroy() {  
  64.             super.onDestroy();  
  65.             unbindService(serviceConnection);  
  66.         }  
  67.       
  68.         @Override  
  69.         public void onClick(View v) {  
  70.             if(v.equals(startButton)) {  
  71.             if(counterService != null) {  
  72.                 counterService.startCounter(0);  
  73.                   
  74.                 startButton.setEnabled(false);  
  75.                     stopButton.setEnabled(true);  
  76.             }  
  77.             } else if(v.equals(stopButton)) {  
  78.             if(counterService != null) {  
  79.                 counterService.stopCounter();  
  80.                   
  81.                 startButton.setEnabled(true);  
  82.                 stopButton.setEnabled(false);  
  83.             }  
  84.             }  
  85.         }  
  86.       
  87.         private BroadcastReceiver counterActionReceiver = new BroadcastReceiver(){  
  88.         public void onReceive(Context context, Intent intent) {  
  89.             int counter = intent.getIntExtra(CounterService.COUNTER_VALUE, 0);  
  90.             String text = String.valueOf(counter);  
  91.             counterText.setText(text);  
  92.               
  93.             Log.i(LOG_TAG, "Receive counter event");  
  94.         }  
  95.     };  
  96.       
  97.     private ServiceConnection serviceConnection = new ServiceConnection() {  
  98.             public void onServiceConnected(ComponentName className, IBinder service) {  
  99.             counterService = ((CounterService.CounterBinder)service).getService();  
  100.               
  101.             Log.i(LOG_TAG, "Counter Service Connected");  
  102.             }  
  103.             public void onServiceDisconnected(ComponentName className) {  
  104.             counterService = null;  
  105.             Log.i(LOG_TAG, "Counter Service Disconnected");  
  106.             }  
  107.         };  
  108. }  

        MainActivity的实现也很简单,它在创建(onCreate)的时候,会调用bindService函数来把计数器服务(CounterService)启动起来,它的第二个参数serviceConnection是一个ServiceConnection实例。计数器服务启动起来后,系统会调用这个实例的onServiceConnected函数将一个Binder对象传回来,通过调用这个Binder对象的getService函数,就可以获得计数器服务接口。这里,把这个计数器服务接口保存在MainActivity的counterService成员变量中。同样,当我们调用unbindService停止计数器服务时,系统会调用这个实例的onServiceDisconnected函数告诉MainActivity,它与计数器服务的连接断开了。

        注意,这里通过调用bindService函数来启动Service时,这个Service与启动它的Activity是位于同一个进程中,它不像我们在前面一篇文章Android系统在新进程中启动自定义服务过程(startService)的原理分析中所描述那样在新的进程中启动服务,后面我们再写一篇文章来分析bindService启动服务的过程。

        在MainActivity的onResume函数中,通过调用registerReceiver函数注册了一个广播接收器counterActionReceiver,它是一个BroadcastReceiver实例,并且指定了这个广播接收器只对CounterService.BROADCAST_COUNTER_ACTION类型的广播感兴趣。当CounterService发出一个CounterService.BROADCAST_COUNTER_ACTION类型的广播时,系统就会把这个广播发送到counterActionReceiver实例的onReceiver函数中去。在onReceive函数中,从参数intent中取出计数器当前的值,显示在界面上。

       MainActivity界面上有两个按钮,分别是Start Counter和Stop Counter按钮,点击前者开始计数,而点击后者则停止计数。

       计数器服务CounterService实现在src/shy/luo/broadcast/CounterService.java文件中:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package shy.luo.broadcast;  
  2.   
  3. import android.app.Service;  
  4. import android.content.Intent;  
  5. import android.os.AsyncTask;  
  6. import android.os.Binder;  
  7. import android.os.IBinder;  
  8. import android.util.Log;  
  9.   
  10. public class CounterService extends Service implements ICounterService {  
  11.     private final static String LOG_TAG = "shy.luo.broadcast.CounterService";  
  12.       
  13.     public final static String BROADCAST_COUNTER_ACTION = "shy.luo.broadcast.COUNTER_ACTION";  
  14.     public final static String COUNTER_VALUE = "shy.luo.broadcast.counter.value";  
  15.       
  16.     private boolean stop = false;  
  17.       
  18.     private final IBinder binder = new CounterBinder();  
  19.       
  20.     public class CounterBinder extends Binder {  
  21.         public CounterService getService() {  
  22.             return CounterService.this;  
  23.         }  
  24.     }  
  25.       
  26.     @Override  
  27.         public IBinder onBind(Intent intent) {  
  28.                 return binder;  
  29.         }  
  30.       
  31.     @Override  
  32.     public void onCreate() {  
  33.         super.onCreate();  
  34.           
  35.         Log.i(LOG_TAG, "Counter Service Created.");  
  36.     }  
  37.       
  38.     @Override  
  39.         public void onDestroy() {  
  40.                 Log.i(LOG_TAG, "Counter Service Destroyed.");  
  41.         }  
  42.       
  43.     public void startCounter(int initVal) {  
  44.                 AsyncTask<Integer, Integer, Integer> task = new AsyncTask<Integer, Integer, Integer>() {      
  45.             @Override  
  46.             protected Integer doInBackground(Integer... vals) {  
  47.                 Integer initCounter = vals[0];  
  48.                   
  49.                 stop = false;  
  50.                 while(!stop) {  
  51.                     publishProgress(initCounter);  
  52.                       
  53.                     try {  
  54.                         Thread.sleep(1000);  
  55.                     } catch (InterruptedException e) {  
  56.                         e.printStackTrace();  
  57.                     }  
  58.                       
  59.                     initCounter++;  
  60.                 }  
  61.                   
  62.                 return initCounter;  
  63.             }  
  64.               
  65.             @Override   
  66.             protected void onProgressUpdate(Integer... values) {  
  67.                 super.onProgressUpdate(values);  
  68.                   
  69.                 int counter = values[0];  
  70.                   
  71.                 Intent intent = new Intent(BROADCAST_COUNTER_ACTION);  
  72.                 intent.putExtra(COUNTER_VALUE, counter);  
  73.                   
  74.                 sendBroadcast(intent);  
  75.             }  
  76.               
  77.             @Override  
  78.             protected void onPostExecute(Integer val) {  
  79.                 int counter = val;  
  80.                   
  81.                 Intent intent = new Intent(BROADCAST_COUNTER_ACTION);  
  82.                 intent.putExtra(COUNTER_VALUE, counter);  
  83.                   
  84.                 sendBroadcast(intent);  
  85.             }  
  86.           
  87.                 };  
  88.               
  89.                 task.execute(0);      
  90.         }  
  91.   
  92.     public void stopCounter() {  
  93.         stop = true;  
  94.     }  
  95. }  

        这个计数器服务实现了ICounterService接口。当这个服务被binderService函数启动时,系统会调用它的onBind函数,这个函数返回一个Binder对象给系统。上面我们说到,当MainActivity调用bindService函数来启动计数器服务器时,系统会调用MainActivity的ServiceConnection实例serviceConnection的onServiceConnected函数通知MainActivity,这个服务已经连接上了,并且会通过这个函数传进来一个Binder远程对象,这个Binder远程对象就是来源于这里的onBind的返回值了。

        函数onBind返回的Binder对象是一个自定义的CounterBinder实例,它实现了一个getService成员函数。当系统通知MainActivity,计数器服务已经启动起来并且连接成功后,并且将这个Binder对象传给MainActivity时,MainActivity就会把这个Binder对象强制转换为CounterBinder实例,然后调用它的getService函数获得服务接口。这样,MainActivity就通过这个Binder对象和CounterService关联起来了。

        当MainActivity调用计数器服务接口的startCounter函数时,计数器服务并不是直接进入计数状态,而是通过使用异步任务(AsyncTask)在后台线程中进行计数。这里为什么要使用异步任务来在后台线程中进行计数呢?前面我们说过,这个计数过程是一个耗时的计算型逻辑,不能把它放在界面线程中进行,因为这里的CounterService启动时,并没有在新的进程中启动,它与MainActivity一样,运行在应用程序的界面线程中,因此,这里需要使用异步任务在在后台线程中进行计数。

        异步任务AsyncTask的具体用法可以参考官方文档http://developer.android.com/reference/android/os/AsyncTask.html。它的大概用法是,当我们调用异步任务实例的execute(task.execute)方法时,当前调用线程就返回了,系统启动一个后台线程来执行这个异步任务实例的doInBackground函数,这个函数就是我们用来执行耗时计算的地方了,它会进入到一个循环中,每隔1秒钟就把计数器加1,然后进入休眠(Thread.sleep),醒过来,再重新这个计算过程。在计算的过程中,可以通过调用publishProgress函数来通知调用者当前计算的进度,好让调用者来更新界面,调用publishProgress函数的效果最终就是直入到这个异步任务实例的onProgressUpdate函数中,这里就可以把这个进度值以广播的形式(sendBroadcast)发送出去了,这里的进度值就定义为当前计数服务的计数值。

        当MainActivity调用计数器服务接口的stopCounter函数时,会告诉函数doInBackground停止执行计数(stop = true),于是,函数doInBackground就退出计数循环,然后将最终计数结果返回了,返回的结果最后进入到onPostExecute函数中,这个函数同样通过广播的形式(sendBroadcast)把这个计数结果广播出去。

        计算器服务就介绍到这里了,下面我们看看应用程序的配置文件AndroidManifest.xml:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.       package="shy.luo.broadcast"  
  4.       android:versionCode="1"  
  5.       android:versionName="1.0">  
  6.     <application android:icon="@drawable/icon" android:label="@string/app_name">  
  7.         <activity android:name=".MainActivity"  
  8.                   android:label="@string/app_name">  
  9.             <intent-filter>  
  10.                 <action android:name="android.intent.action.MAIN" />  
  11.                 <category android:name="android.intent.category.LAUNCHER" />  
  12.             </intent-filter>  
  13.         </activity>  
  14.     <service android:name=".CounterService"  
  15.          android:enabled="true">  
  16.     </service>  
  17.     </application>  
  18. </manifest>   
        这个配置文件很简单,只是告诉系统,它有一个Activity和一个Service。

        再来看MainActivity的界面文件,它定义在res/layout/main.xml文件中:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent"   
  6.     android:gravity="center">  
  7.     <LinearLayout  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:layout_marginBottom="10px"  
  11.         android:orientation="horizontal"   
  12.         android:gravity="center">  
  13.         <TextView    
  14.         android:layout_width="wrap_content"   
  15.             android:layout_height="wrap_content"   
  16.             android:layout_marginRight="4px"  
  17.             android:gravity="center"  
  18.             android:text="@string/counter">  
  19.         </TextView>  
  20.         <TextView    
  21.             android:id="@+id/textview_counter"  
  22.         android:layout_width="wrap_content"   
  23.             android:layout_height="wrap_content"   
  24.             android:gravity="center"  
  25.             android:text="0">  
  26.         </TextView>  
  27.     </LinearLayout>  
  28.     <LinearLayout  
  29.         android:layout_width="fill_parent"  
  30.         android:layout_height="wrap_content"  
  31.         android:orientation="horizontal"   
  32.         android:gravity="center">  
  33.         <Button   
  34.             android:id="@+id/button_start"  
  35.             android:layout_width="wrap_content"  
  36.             android:layout_height="wrap_content"  
  37.             android:gravity="center"  
  38.             android:text="@string/start">  
  39.         </Button>  
  40.         <Button   
  41.             android:id="@+id/button_stop"  
  42.             android:layout_width="wrap_content"  
  43.             android:layout_height="wrap_content"  
  44.             android:gravity="center"  
  45.             android:text="@string/stop" >  
  46.         </Button>  
  47.      </LinearLayout>    
  48. </LinearLayout>  
        这个界面配置文件也很简单,等一下我们在模拟器把这个应用程序启动起来后,就可以看到它的截图了。

        应用程序用到的字符串资源文件位于res/values/strings.xml文件中:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <string name="app_name">Broadcast</string>  
  4.     <string name="counter">Counter: </string>  
  5.     <string name="start">Start Counter</string>  
  6.     <string name="stop">Stop Counter</string>  
  7. </resources>  
         最后,我们还要在工程目录下放置一个编译脚本文件Android.mk:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. LOCAL_PATH:= $(call my-dir)        
  2. include $(CLEAR_VARS)        
  3.         
  4. LOCAL_MODULE_TAGS :optional        
  5.         
  6. LOCAL_SRC_FILES := $(call all-subdir-java-files)        
  7.         
  8. LOCAL_PACKAGE_NAME :Broadcast        
  9.         
  10. include $(BUILD_PACKAGE)    
          接下来就要编译了。有关如何单独编译Android源代码工程的模块,以及如何打包system.img,请参考 如何单独编译Android源代码中的模块一文。
          执行以下命令进行编译和打包:
[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/Broadcast          
  2. USER-NAME@MACHINE-NAME:~/Android$ make snod     
         这样,打包好的Android系统镜像文件system.img就包含我们前面创建的Broadcast应用程序了。
         再接下来,就是运行模拟器来运行我们的例子了。关于如何在Android源代码工程中运行模拟器,请参考 在Ubuntu上下载、编译和安装Android最新源代码一文。
         执行以下命令启动模拟器:
[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. USER-NAME@MACHINE-NAME:~/Android$ emulator  
        模拟器启动起,就可以App Launcher中找到Broadcast应用程序图标,接着把它启动起来,然后点击界面上的Start Counter按钮,就可以把计数器服务启动起来了,计数器服务又通过广播把计数值反馈给MainActivity,于是,我们就会在MainActivity界面看到计数器的值不断地增加了:

        这样,使用广播的例子就介绍完了。回顾一下,使用广播的两个步骤:

        1. 广播的接收者需要通过调用registerReceiver函数告诉系统,它对什么样的广播有兴趣,即指定IntentFilter,并且向系统注册广播接收器,即指定BroadcastReceiver:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. IntentFilter counterActionFilter = new IntentFilter(CounterService.BROADCAST_COUNTER_ACTION);  
  2. registerReceiver(counterActionReceiver, counterActionFilter);  
        这里,指定感兴趣的广播就是CounterService.BROADCAST_COUNTER_ACTION了,而指定的广播接收器就是counterActonReceiver,它是一个BroadcastReceiver类型的实例。

        2. 广播的发送者通过调用sendBroadcast函数来发送一个指定的广播,并且可以指定广播的相关参数:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Intent intent = new Intent(BROADCAST_COUNTER_ACTION);  
  2. intent.putExtra(COUNTER_VALUE, counter);  
  3. sendBroadcast(intent)  
        这里,指定的广播为CounterService.BROADCAST_COUNTER_ACTION,并且附带的带参数当前的计数器值counter。调用了sendBroadcast函数之后,所有注册了CounterService.BROADCAST_COUNTER_ACTION广播的接收者便可以收到这个广播了。

        在第1步中,广播的接收者把广播接收器注册到ActivityManagerService中;在第2步中,广播的发送者同样是把广播发送到ActivityManagerService中,由ActivityManagerService去查找注册了这个广播的接收者,然后把广播分发给它们。

        在第2步的分发的过程,其实就是把这个广播转换成一个消息,然后放入到接收器所在的线程消息队列中去,最后就可以在消息循环中调用接收器的onReceive函数了。这里有一个要非常注意的地方是,由于ActivityManagerService把这个广播放进接收器所在的线程消息队列后,就返回了,它不关心这个消息什么时候会被处理,因此,对广播的处理是异步的,即调用sendBroadcast时,这个函数不会等待这个广播被处理完后才返回。

        下面,我们以一个序列图来总结一下,广播的注册和发送的过程:


        虚线上面Step 1到Step 4步是注册广播接收器的过程,其中Step 2通过LoadedApk.getReceiverDispatcher在LoadedApk内部创建了一个IIntentReceiver接口,并且传递给ActivityManagerService;虚线下面的Step 5到Step 11是发送广播的过程,在Step 8中,ActivityManagerService利用上面得到的IIntentReceiver远程接口,调用LoadedApk.performReceiver接口,LoadedApk.performReceiver接口通过ActivityThread.H接口的post函数将这个广播消息放入到ActivityThread的消息队列中去,最后这个消息在LoadedApk的Args.run函数中处理,LoadedApk.Args.run函数接着调用MainActivity.BroadcastReceiver的onReceive函数来最终处理这个广播。

        文章开始的时候,我们提到,举这个例子的最终目的,是为了进一步学习Android系统的广播机制,因此,在接下来的两篇文章中,我们将详细描述上述注册广播接收器和发送广播的过程:

        1. Android应用程序注册广播接收器(registerReceiver)的过程分析;

        2. Android应用程序发送广播(sendBroadcast)的过程分析。

        相信学习完这两篇文章后,能够加深对Android系统广播机制的了解,敬请关注。

老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!

版权声明:本文为博主原创文章,未经博主允许不得转载。

33
15
主题推荐
android broadcast
猜你在找
Android底层技术:Java层系统服务(Android Service)
Android开源项目实践之UI篇
Android移植基础
Android平台技术:JNI开发初步
【技术公开课】Android内存泄漏案例分析
Android Service 详解四开始停止service
Android HandlerThread的用法
android消息广播Intent传递数据
Android ApiDemos示例解析19App-Alarm-Alarm Controller
Android 录音MediaRecorder与播放MediaPlayer
查看评论
23楼  baidu_28545997 2015-05-28 10:12发表 [回复]
您好,罗老师,我想咨询个问题~~

我们都知道用adb的方法可以使用am来发送广播,am的本质是一个脚本调用
我想知道如果我想用C语言写一个守护进程也去接收广播,需要怎么写?
22楼  mangohhl 2015-01-01 16:28发表 [回复]
unbindService()方法成功后,系统并不会调用onServiceDisconnected()
Re:  GOUP2015 2015-05-04 14:37发表 [回复]
回复honghailiang888:为啥? 那是什么流程?
21楼  zhongyuanceshi 2014-12-25 10:52发表 [回复]
异步任务AsyncTask会在系统启动一个后台线程来执行这个异步任务,请问大师:这个后台线程还是运行在activity所在的进程吗?如果CounterService运行在另一个新进程中,计算功能是否就可以不用AsyncTask?广播机制就实现了进程间的通信了?
20楼  vv2012 2014-04-16 20:53发表 [回复]
博主好,
关于" Step 2通过LoadedApk.getReceiverDispatcher在LoadedApk内部创建了一个IIntentReceiver接口,并且传递给ActivityManagerService "

本人资历浅, 有些基本问题想请教一下(Step2). 为什么我在http://developer.android.com/reference/android/app/ActionBar.html 中找不到 "android.app.LoadedApk" 也找不到 "LoadedApk", 请问这些源码我要去哪里找呢? 

感谢.
Best Regards!
Re:  vv2012 2014-04-16 23:25发表 [回复]
回复vv2012:谷歌了几下, 装了个android SDK search的插件, 算是搞定了.
继续向博主学习!
19楼  辰月之征 2014-01-22 11:37发表 [回复]
写的真好啊
18楼  dzzhang1981 2013-11-12 00:24发表 [回复]
老罗您好,请教您一个问题。我在powermanagerservice里面接收到一个intent消息后去做sendbroadcast的操作广播一个消息(该消息没有注册reciever)。当intent本频繁接收触发广播发送时会导致ActivityManagerService被block住以至于被watchdog杀掉。但是我使用异步操作来发广播就没有问题。请问会是什么原因。
17楼  天使之翼 2013-10-13 15:33发表 [回复]
老罗你研究了这么多,对apk不安装就运行你觉得能实现吗
Re:  罗升阳 2013-10-14 09:25发表 [回复]
回复z1074971432:apk不安装运行无非就是使用动态加载技术。这是扯蛋的,跟安装再运行的apk不等价。
16楼  开发者_android 2013-10-10 16:35发表 [回复]
" 回复tcytree:非常感谢,但有个疑问,难道android每次开机启动都要重新安装一遍所有的apk程序吗?统计所有的apk的manifest.xml注册的所有广播?

Re: 罗升阳 2011-11-11 22:10发表 [回复] [引用] [举报]
回复tcytree:是的"
---------------------------------------------------------------------------
罗老师,您好.对于以上的问答,我有一点不明白,还望赐教.如下:
"难道android每次开机启动都要重新安装一遍所有的apk程序吗?统计所有的apk的manifest.xml注册的所有广播?"

每次开机,都会重新安装apk?那我都不明白了,已经安装的应用,如果全部重新安装,它安装的依据是什么?哪里提供的apk?
Re:  罗升阳 2013-10-11 00:40发表 [回复]
回复dalor:你先看看这篇文章:http://blog.csdn.net/luoshengyang/article/details/6766010
Re:  开发者_android 2013-10-11 09:14发表 [回复]
回复Luoshengyang:罗老师,我看了那篇文章.可是还有一点不太明白.
SystemServer启动PackageManagerService服务后,PackageManagerService就会执行应用程序安装的过程,扫描系统中特定的目录,找到里面的以Apk为后缀的文件进行安装.但,如果下载安装完apk,然后,我删除了下载的apk.那启动时,它不就找不到apk文件了吗?还是说,在安装apk时,android系统会备份一份apk到指定的目录呢?
15楼  djzwlltzyzt 2013-08-21 11:03发表 [回复]
看不懂,没写注释。请问楼主能在一年后还能看懂此代码吗?
14楼  XNZ0616 2013-05-13 16:53发表 [回复]
楼主太强大了,学习中。。。
13楼  qeshine 2013-04-08 17:20发表 [回复]
老罗本硕时看了多少本书啊
12楼  qeshine 2013-04-08 14:52发表 [回复]
能不能讲解一下退出所有activity的方法呢?
11楼  ngaicen 2012-11-21 17:03发表 [回复]
我觉得请参考什么什么的虽然可以让正本书更紧凑,但还是要简单介绍一下为好。最好在不参考的情况下能了解个大概。然后把参考什么什么去掉,就说之前有提到过就好。不然看起来好像在告诉读者要看完整本书才能真正了解某一个知识点,有点作秀的感觉。
10楼  huangzhenyu1983 2012-06-02 14:50发表 [回复]
请教罗工“这里通过调用bindService函数来启动Service时,这个Service与启动它的Activity是位于同一个进程中”,bindservice是如何实现让service和启动他的activity处于同一进程中的?位于同一进程中是否就意味着一开始activity对service的调用是本地调用?
Re:  huangzhenyu1983 2012-06-02 14:53发表 [回复]
回复huangzhenyu1983:通过bindservice来注册的服务是否都是和actvity在同一进程中的?除了和service在同一进程中的actvity外,其他的进程是不能使用这个service的?
Re:  罗升阳 2012-06-02 14:59发表 [回复]
回复huangzhenyu1983:也可以不在同一个进程的。一个Service组件被绑定的时候,会通过它的成员函数onBind来返回一个Binder对象给请求绑定它的一个Activity组件。如果不在同一个进程,那么Activity组件所获得的就是这个Binder对象的一个代理接口,通过这个代理接口就可以访问运行在另外一个进程中的Service组件所提拱的服务。如果在同一个进程,Activity组件所获得的就是这个Binder对象的一个本地接口,同样可以通过它来访问运行在同一个进程中的Service组件所提供的服务。
9楼  a06063175 2012-05-15 22:23发表 [回复]
受教了。
8楼  jpliua2 2012-02-02 17:05发表 [回复]
罗老师,这篇写的太好了,BroadcastReceiver和Service都学到了。还有AsyncTask。
能不能再讲一下这个这个AsyncTask。
敬礼!
Re:  罗升阳 2012-02-20 10:32发表 [回复]
回复jpliua2:AsyncTask可以参考这篇文章《Android应用程序线程消息循环模型分析》
7楼  plsafan 2011-11-17 09:44发表 [回复]
我的账号(tcytree)系统不让发了,借用同事的账号继续问
感谢楼主,楼主您太辛苦了!
马上按你的指导去分析。
”还是以动态注册的为例,它是在应用程序主线程中被调用的“,楼主,你所说的应用程序主线程是谁?我就是关注它,也被它迷糊了,我整个项目就一个Receiver类,没有别的任何类,程序里定义的闹钟跟它有关系吗?我的程序是通过静态注册的。
Re:  罗升阳 2011-11-17 22:01发表 [回复]
回复plsafan:不管你的工程有多少个类,系统都认为这是一个应用程序
6楼  bobo1808 2011-11-15 23:24发表 [回复]
LZ写的很好啊 谢谢分享啊 我也在学android ....
5楼  tcytree 2011-11-08 18:53发表 [回复]
非常详细。我想咨询下,在AndroidManifest.xml中注册的广播机制是什么样那,android系统怎么知道这个广播?
Re:  tcytree 2011-11-10 23:54发表 [回复]
回复tcytree:继续打扰楼主,关于广播的优先级,若两个apk的优先级相同,android系统会怎么处理?
Re:  罗升阳 2011-11-11 22:20发表 [回复]
回复tcytree:你说的优先级应该是说Broadcast Receiver的优先级吧,一般是这样的,我们调用sendOrderedBroadcast来发送一个intent时,系统会先找到注册接收这个intent的所有receiver,这些receiver分为两部分,一部分是在AndroidManifest.xml文件静态配置的,一部分是在程序里面动态注册。这两部分receiver分别按照priority值从大到小排列,这时候具有相同priority的receiver谁先谁后是不确定的,取决于排序算法是否是稳定的。现在用的排序算法是由java.util.Collections.sort函数来类来实现的,你可以去研究一下,如果这个排序算法是稳定的话,那么先注册的就排在前面。这两部分的receiver排好序之后,再合并到一起,合并的时候,也是按照从大小的顺序排序,但是当动态注册的receiver与静态配置的receiver具有相同的priority时,动态注册的receiver会排在前面。
Re:  tcytree 2011-11-16 22:38发表 [回复]
回复Luoshengyang:感谢楼主!!!
receiver的生命周期。
我以一个Receiver为我的程序入口,收到android.intent.action.BOOT_COMPLETED后,启动一个闹钟,每隔1分钟打印一下这个receiver中定义的一个成员变量i=0,并i++,通过日志发现,这个i永远是0。
但我若把这个成员变量i定义成static,它就能持续加1了。
我还定义了该receiver类的构造函数,里面打印Receiver Called,发现每次有闹钟广播来到是都会被调用。
并且我通过ddms查看进程,这个receiver进程一直都在。
请楼主指点:
1、系统收到广播,创建一个receiver实例,并把广播传递个它,onReceive方法执行完毕,receiver实例就退出了。为何进程里还能看到这个进程?
2、闹钟应该是注册在系统上,有点不明白,好像有个什么东西在在receiver外面,维护着这个进程,难道是闹钟?
我不太能清楚表达我的问题,就是我认为receiver好比一个main()入口,运行完成,这个应用就退出了,但为何不是,请楼主指点!
楼主,能要下你的mail吗?
Re:  罗升阳 2011-11-17 00:25发表 [回复]
回复tcytree:建议你看一下后面两篇文章《Android应用程序注册广播接收器(registerReceiver)的过程分析》和《Android应用程序发送广播(sendBroadcast)的过程分析》。你的Broadcast Receiver是静态注册还是动态注册的?我做了动态注册的实验,每次广播来的时候调用的都是同一个实例,从刚才我提到的两篇文章也可以看出来,静态注册的我没有去分析过,可能和动态注册的不一样。还是以动态注册的为例,它是在应用程序主线程中被调用的,也就是说,系统不会为了调用它的onReceive函数就创建一个线程或者进程。静态注册也是同样的道理,它一般是常驻在某个进程中的。
Re:  tcytree 2011-11-10 23:35发表 [回复]
回复tcytree:非常感谢,但有个疑问,难道android每次开机启动都要重新安装一遍所有的apk程序吗?统计所有的apk的manifest.xml注册的所有广播?
Re:  罗升阳 2011-11-11 22:10发表 [回复]
回复tcytree:是的
Re:  罗升阳 2011-11-08 21:27发表 [回复]
回复tcytree:你可以看一下后面这两篇文章《Android应用程序注册广播接收器(registerReceiver)的过程分析》和《Android应用程序发送广播(sendBroadcast)的过程分析》,有时间还可以再看一下这篇文章《Android应用程序安装过程源代码分析》
4楼  gaoran_oracle 2011-09-13 22:51发表 [回复]
写的好,你qq多少呀,和你学习!
Re:  罗升阳 2011-09-14 21:30发表 [回复]
回复gaoran_oracle:很少在上面交流
3楼  hxs2020 2011-08-31 23:28发表 [回复]
写的很好,受直教了
2楼  CarsonNiu 2011-08-31 18:43发表 [回复]
不错文章,期待!
1楼  mylzc 2011-08-31 08:53发表 [回复]
“那么势必就会导致应用程序不能响应界面事件,最后导致应用程序产生Force Close问题。”
呵呵。这个应该是会导致ANR问题吧。
Re:  罗升阳 2011-08-31 10:30发表 [回复]
回复mylzc:谢谢指正,这个现象正式的叫法是应该叫ANR,已经修改过来。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值