`

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

阅读更多

 在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文件中定义计数器的服务接口:

 

  1. package shy.luo.broadcast;  
  2.   
  3. public interface ICounterService {  
  4.         public void startCounter(int initVal);  
  5.         public void stopCounter();  
  6. }  
package shy.luo.broadcast;

public interface ICounterService {
        public void startCounter(int initVal);
        public void stopCounter();
}

       这个接口很简单,它只有两个成员函数,分别用来启动和停止计数器;启动计数时,还可以指定计数器的初始值。

 

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

 

  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. }  
package shy.luo.broadcast;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity implements OnClickListener {
	private final static String LOG_TAG = "shy.luo.broadcast.MainActivity";
	   
	private Button startButton = null;
	private Button stopButton = null;
	private TextView counterText = null;
	
	private ICounterService counterService = null;
	
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
        
            startButton = (Button)findViewById(R.id.button_start);
            stopButton = (Button)findViewById(R.id.button_stop);
            counterText = (TextView)findViewById(R.id.textview_counter);
        
            startButton.setOnClickListener(this);
            stopButton.setOnClickListener(this);
        
            startButton.setEnabled(true);
            stopButton.setEnabled(false);
        
            Intent bindIntent = new Intent(MainActivity.this, CounterService.class);
            bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
        
            Log.i(LOG_TAG, "Main Activity Created.");
        }
    
        @Override 
        public void onResume() {
    	    super.onResume();
    	
    	    IntentFilter counterActionFilter = new IntentFilter(CounterService.BROADCAST_COUNTER_ACTION);
	    registerReceiver(counterActionReceiver, counterActionFilter);
        }
    
        @Override
        public void onPause() {
    	    super.onPause();
    	    unregisterReceiver(counterActionReceiver);
        }
    
        @Override
        public void onDestroy() {
    	    super.onDestroy();
    	    unbindService(serviceConnection);
        }
    
        @Override
        public void onClick(View v) {
    	    if(v.equals(startButton)) {
    		if(counterService != null) {
    			counterService.startCounter(0);
    			
	    		startButton.setEnabled(false);
	                stopButton.setEnabled(true);
    		}
    	    } else if(v.equals(stopButton)) {
    		if(counterService != null) {
    			counterService.stopCounter();
    			
    			startButton.setEnabled(true);
    			stopButton.setEnabled(false);
    		}
    	    }
        }
    
        private BroadcastReceiver counterActionReceiver = new BroadcastReceiver(){
		public void onReceive(Context context, Intent intent) {
			int counter = intent.getIntExtra(CounterService.COUNTER_VALUE, 0);
			String text = String.valueOf(counter);
			counterText.setText(text);
			
			Log.i(LOG_TAG, "Receive counter event");
		}
	};
	
	private ServiceConnection serviceConnection = new ServiceConnection() {
    	    public void onServiceConnected(ComponentName className, IBinder service) {
    		counterService = ((CounterService.CounterBinder)service).getService();
    		
    		Log.i(LOG_TAG, "Counter Service Connected");
    	    }
     	    public void onServiceDisconnected(ComponentName className) {
    		counterService = null;
    		Log.i(LOG_TAG, "Counter Service Disconnected");
    	    }
        };
}

        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文件中:

 

  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. }  
package shy.luo.broadcast;

import android.app.Service;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class CounterService extends Service implements ICounterService {
	private final static String LOG_TAG = "shy.luo.broadcast.CounterService";
	
	public final static String BROADCAST_COUNTER_ACTION = "shy.luo.broadcast.COUNTER_ACTION";
	public final static String COUNTER_VALUE = "shy.luo.broadcast.counter.value";
	
	private boolean stop = false;
	
	private final IBinder binder = new CounterBinder();
	
	public class CounterBinder extends Binder {
		public CounterService getService() {
			return CounterService.this;
		}
	}
	
	@Override
        public IBinder onBind(Intent intent) {
                return binder;
        }
	
	@Override
	public void onCreate() {
		super.onCreate();
		
		Log.i(LOG_TAG, "Counter Service Created.");
	}
	
	@Override
        public void onDestroy() {
    	        Log.i(LOG_TAG, "Counter Service Destroyed.");
        }
	
	public void startCounter(int initVal) {
    	        AsyncTask<Integer, Integer, Integer> task = new AsyncTask<Integer, Integer, Integer>() {	
			@Override
			protected Integer doInBackground(Integer... vals) {
				Integer initCounter = vals[0];
				
				stop = false;
				while(!stop) {
					publishProgress(initCounter);
					
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					
					initCounter++;
				}
				
				return initCounter;
			}
			
			@Override 
			protected void onProgressUpdate(Integer... values) {
				super.onProgressUpdate(values);
				
				int counter = values[0];
				
				Intent intent = new Intent(BROADCAST_COUNTER_ACTION);
				intent.putExtra(COUNTER_VALUE, counter);
				
				sendBroadcast(intent);
			}
			
			@Override
			protected void onPostExecute(Integer val) {
				int counter = val;
				
				Intent intent = new Intent(BROADCAST_COUNTER_ACTION);
				intent.putExtra(COUNTER_VALUE, counter);
				
				sendBroadcast(intent);
			}
		
    	        };
			
    	        task.execute(0);	
        }

	public void stopCounter() {
		stop = true;
	}
}

        这个计数器服务实现了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:

 

  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>   
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="shy.luo.broadcast"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MainActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
	<service android:name=".CounterService"
		 android:enabled="true">
	</service>
    </application>
</manifest> 

        这个配置文件很简单,只是告诉系统,它有一个Activity和一个Service。

 

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

 

  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>  
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" 
    android:gravity="center">
    <LinearLayout
    	android:layout_width="fill_parent"
    	android:layout_height="wrap_content"
    	android:layout_marginBottom="10px"
    	android:orientation="horizontal" 
    	android:gravity="center">
	    <TextView  
	   	android:layout_width="wrap_content" 
	    	android:layout_height="wrap_content" 
	    	android:layout_marginRight="4px"
	    	android:gravity="center"
	    	android:text="@string/counter">
	    </TextView>
	    <TextView  
	    	android:id="@+id/textview_counter"
	   	android:layout_width="wrap_content" 
	    	android:layout_height="wrap_content" 
	    	android:gravity="center"
	    	android:text="0">
	    </TextView>
    </LinearLayout>
    <LinearLayout
    	android:layout_width="fill_parent"
    	android:layout_height="wrap_content"
    	android:orientation="horizontal" 
    	android:gravity="center">
	    <Button 
	    	android:id="@+id/button_start"
	    	android:layout_width="wrap_content"
	    	android:layout_height="wrap_content"
	    	android:gravity="center"
	    	android:text="@string/start">
	    </Button>
	    <Button 
	    	android:id="@+id/button_stop"
	    	android:layout_width="wrap_content"
	    	android:layout_height="wrap_content"
	    	android:gravity="center"
	    	android:text="@string/stop" >
	    </Button>
     </LinearLayout>  
</LinearLayout>

        这个界面配置文件也很简单,等一下我们在模拟器把这个应用程序启动起来后,就可以看到它的截图了。

 

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

  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>  
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Broadcast</string>
    <string name="counter">Counter: </string>
    <string name="start">Start Counter</string>
    <string name="stop">Stop Counter</string>
</resources>

         最后,我们还要在工程目录下放置一个编译脚本文件Android.mk:

 

 

  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)    
LOCAL_PATH:= $(call my-dir)      
include $(CLEAR_VARS)      
      
LOCAL_MODULE_TAGS := optional      
      
LOCAL_SRC_FILES := $(call all-subdir-java-files)      
      
LOCAL_PACKAGE_NAME := Broadcast      
      
include $(BUILD_PACKAGE)  

          接下来就要编译了。有关如何单独编译Android源代码工程的模块,以及如何打包system.img,请参考如何单独编译Android源代码中的模块一文。
          执行以下命令进行编译和打包:

  1. USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/Broadcast          
  2. USER-NAME@MACHINE-NAME:~/Android$ make snod     
USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/Broadcast        
USER-NAME@MACHINE-NAME:~/Android$ make snod   

         这样,打包好的Android系统镜像文件system.img就包含我们前面创建的Broadcast应用程序了。
         再接下来,就是运行模拟器来运行我们的例子了。关于如何在Android源代码工程中运行模拟器,请参考在Ubuntu上下载、编译和安装Android最新源代码一文。
         执行以下命令启动模拟器:

  1. USER-NAME@MACHINE-NAME:~/Android$ emulator  
USER-NAME@MACHINE-NAME:~/Android$ emulator

        模拟器启动起,就可以App Launcher中找到Broadcast应用程序图标,接着把它启动起来,然后点击界面上的Start Counter按钮,就可以把计数器服务启动起来了,计数器服务又通过广播把计数值反馈给MainActivity,于是,我们就会在MainActivity界面看到计数器的值不断地增加了:

 

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

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

 

  1. IntentFilter counterActionFilter = new IntentFilter(CounterService.BROADCAST_COUNTER_ACTION);  
  2. registerReceiver(counterActionReceiver, counterActionFilter);  
IntentFilter counterActionFilter = new IntentFilter(CounterService.BROADCAST_COUNTER_ACTION);
registerReceiver(counterActionReceiver, counterActionFilter);

        这里,指定感兴趣的广播就是CounterService.BROADCAST_COUNTER_ACTION了,而指定的广播接收器就是counterActonReceiver,它是一个BroadcastReceiver类型的实例。

 

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

 

  1. Intent intent = new Intent(BROADCAST_COUNTER_ACTION);  
  2. intent.putExtra(COUNTER_VALUE, counter);  
  3. sendBroadcast(intent)  
Intent intent = new Intent(BROADCAST_COUNTER_ACTION);
intent.putExtra(COUNTER_VALUE, counter);
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函数来最终处理这个广播。

 

原博客链接:http://blog.csdn.net/luoshengyang/article/details/6730748

 

 

本文节选自Android系统源代码情景分析》

电子工业出版社出版

罗升阳

分享到:
评论

相关推荐

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

    Android系统中的广播(Broadcast)机制是一种关键的组件间通信方式,允许应用的不同部分即使在不同进程中也能相互通信。广播通常通过Intent对象来传递信息,Intent不仅可以在同一个应用内部使用,也可以跨应用发送,...

    Android broadcast广播机制的使用

    在Android系统中,广播(Broadcast)是一种非常重要的组件,它使得应用程序之间能够进行无须直接交互的信息传递。本文将深入探讨Android广播机制的使用,包括动态注册与静态注册两种方式。 首先,我们要理解广播的...

    android学习教程--广播机制

    在Android开发中,广播机制(Broadcast Mechanism)是系统中的一种重要组件,它允许应用程序接收并响应系统或其它应用发出的事件。广播接收器(BroadcastReceiver)是这一机制的核心,它能够监听特定的Intent(意图...

    Android广播监听机制的两种实现方法.pdf

    Android广播是操作系统用来通知所有感兴趣的程序某些特定事件发生的一种机制。这些事件可能是系统级别的(如开机、接收到短信),也可能是应用级别的(如应用内部的特定操作)。广播接收器(BroadcastReceiver)是...

    AndroidStudio 4.2.2 控件 Broadcast 广播 使用示例

    Broadcast是Android系统中的一个全局事件通知机制,它允许发送者(BroadcastSender)向系统发布消息,而接收者(BroadcastReceiver)可以在任何时间、任何地方监听并响应这些消息。Broadcast分为两种类型:有序广播...

    Android UDP broadcast Example

    "Android UDP broadcast Example"是关于如何在Android平台上实现UDP广播接收和发送的一个示例项目。这个项目的核心功能包括: 1. **接受UDP信号**: 在Android中,我们可以使用`java.net.DatagramSocket`类来创建...

    android 广播机制

    本教程将深入探讨Android广播机制的核心概念、工作原理以及如何在实践中运用。 **广播的概念** 广播在Android中扮演着一个通知的角色,当系统或某个应用产生了一个广播事件(例如设备开机、网络状态改变等),这个...

    Android简单的广播消息

    在Android系统中,广播消息(Broadcast)是一种全局的通知机制,允许应用程序之间进行异步通信。开发者可以发送广播来传递信息,而任何注册了相应意图过滤器(Intent Filter)的接收者(BroadcastReceiver)都可以...

    android BroadCast

    "android Broadcast"这个标题指的是Android中的广播机制,它是Android系统事件传播的核心机制。 广播分为系统广播和自定义广播。系统广播是由系统触发的,如开机、网络状态改变等;而自定义广播则是由开发者自己...

    android 跨应用程序广播发送接受

    在Android系统中,跨应用程序广播(Cross-App Broadcasting)是一种通信机制,允许应用程序之间传递消息,无需知道接收方确切的身份。这种机制对于组件间的解耦合以及系统事件的全局通知非常有用。本篇文章将深入...

    android 广播简单的使用

    ### Android广播机制详解 在Android开发中,广播(Broadcast)是一种常见的组件间通信方式,用于在应用程序的不同部分或不同应用程序之间传递消息。本篇将基于提供的文件内容详细讲解Android广播的简单使用方法。 ...

    Android说明Broadcast的代码例子

    在Android系统中,Broadcast(广播)是一种非常重要的组件,它允许应用程序之间进行异步通信,无需两者之间有明确的依赖关系。BroadcastReceiver(广播接收器)是广播机制的核心,它可以监听系统或应用发送的广播...

    Android设备连接Honeywell蓝牙扫描枪实现广播方式输出.rar

    在Android系统中,广播接收器是一种非常重要的组件,它能够监听系统或应用程序发送的各种广播事件,并在事件发生时执行相应的操作。在这种情况下,我们需要创建一个专门用于接收扫描数据的BroadcastReceiver。 1. *...

    2011.09.07(3)——— android 跨进程通信之Broadcast

    在Android中,Broadcast分为系统广播和自定义广播两种。系统广播是由系统发送的,例如当设备启动、网络状态变化时,系统会发出相应的广播。自定义广播则是由应用自身或者应用间互相发送的,用于实现特定的通信需求。...

    Broadcast知识点总结

    综上,Broadcast机制是Android系统中实现组件间通信的重要方式,无论是简单的信息传递还是复杂的流程控制,都能找到其应用场景。理解并熟练掌握BroadcastReceiver的使用,能帮助开发者构建更加灵活和响应迅速的应用...

    android广播简单使用

    在Android系统中,广播(Broadcast)是一种非常重要的组件,它使得应用程序之间能够进行无须直接交互的...通过学习并实践“boardcase”这个简单的广播使用示例,开发者可以进一步提升对Android广播的理解和运用能力。

    android 广播,界面传值示例

    在Android开发中,广播(Broadcast)和界面传值是两个重要的概念。广播是Android系统中的一种全局通知机制,用于在应用程序之间传递消息。界面传值则涉及到Activity、Fragment或者Service之间的数据交换。在这个...

    Broadcast demo

    在Android系统中,Broadcast(广播)是一种全局的消息传递机制,它允许应用程序之间进行异步通信。Broadcast demo是一个演示了如何在Android中简单使用Broadcast的示例项目。通过这个项目,我们可以学习到关于...

    Android学习-broadcast实现音乐播放器

    在这个“Android学习-broadcast实现音乐播放器”的项目中,我们将探讨如何利用BroadcastReceiver来构建一个简单的音乐播放器。BroadcastReceiver是Android系统中的一个关键组件,它监听并响应系统或应用发布的广播...

    简单使用安卓的广播机制

    广播机制是Android系统中事件驱动编程的核心部分,允许开发者发送和接收特定事件的通知。下面将详细介绍安卓的广播机制,以及如何简单地使用它。 一、广播的基本概念 1. 广播Intent:广播是通过Intent对象进行传递...

Global site tag (gtag.js) - Google Analytics