本文学习拜读自罗升阳老师的《Android系统源代码情景分析》
涉及Binder
在Android系统中,广播(Broadcast)是一种在组件之间进行消息传递的方式。这些组件可以运行在同一个进程中,也可以运行在不同的进程中。当两个不在同一个进程中的组件通过广播机制来传递消息的时候,广播机制就有点类似Binder进程间通信机制。事实上,广播机制就是在Binder机制的基础上实现的。
既然如此,Android系统为什么还需要广播机制呢?我们知道,在Binder进程间通信机制中,Client组件在好Service组件通信之前,必须要先获得它的一个代理对象,即Client组件实现要知道Service组件的存在。然而,在广播机制中,广播发送者事先并不需要知道广播接收者的存在,这样就可以降低广播发送者和广播接收者之间的耦合度,进而提高系统的可扩展性。因此, Android系统需要提供广播机制。
广播机制是一种基于消息发布和订阅的事件驱动模型,即广播发送者负责发布消息,而广播接受者需要先订阅消息,然后才能接收到消息。Android系统将广播接收者抽象为一种Boradcast Receiver组件,它是Android应用程序的四大组件之一。同时,Android应用程序的另外两种组件,即Activity组件和Service组件被赋予了发送广播的能力。因此,在Android系统中,使用广播机制在组件之间传递消息是很方便的。
广播机制存在一个注册中心,它是由ActivityManagerService来担当的。广播接收者订阅消息的表现形式就是将自己注册到ActivityManagerService中,并且指定要接收的广播类型。当广播发送者向广播接收者发送一个广播时,这个广播首先发送到ActivityManagerService然后ActivityManagerService根据这个广播的类型找到相应的广播接收者,最后将这个广播发送给他们处理。
广播接收者的的注册方式分为静态注册和动态注册两种方式。在静态注册方式中,用来描述广播接收者的Boradcast Receiver组件必须要在配置文件 AndroidManifest.xml中注册他们所感兴趣的广播类型,以便ActivityManagerService可以找到他们。在动态注册方式中。我们需要在代码中手动的调用 Context接口的成员函数registerReceiver将Boradcast Receiver组件注册到 ActivityManagerService中,Activity组件和Service组件都实现了Context接口,因此,我们可以方便的在一个Activity组件或者Service组件中注册一个Boradcast Receiver组件。同等情况下,动态注册的广播接收者要比静态注册的广播接收者优先接收到广播。
广播的发送方式分为有序和无序两种。我们在注册广播接收者时,可以指定它们的优先级。当ActivityManagerService接收有序
广播时,它就会先将这个有序广播发送给符合条件的,优先级较高的广播接收者处理,然后再发送给符合条件的,优先级较低的广播接收者处理,而当 ActivityManagerService 接收无序广播时,他就会忽略广播接收者的优先级,并行的将无序广播发送给所有符合条件的广播接收者处理。
在前面的 Service组件分析实例中,我们开发了一个应用程序 Counter,它由一个Activity组件Counter和一个Service组件
CounterService组成。Counter组件在启动CounterService组件的计数器时,需要指定一个计数器回调接口 ICounterCallback,
以便 CounterService组件可以将计数器的当前计数实时的更新到Counter组件的用户界面上。这种数据更新方式存在一定的缺陷,因为这会使得CounterService组件依赖于Counter组件提供的计数器回调接口ICounterCallback。
本文开发一个名称为 Broadcounter的应用程序,它实现的功能与应用程序Counter是一样的,不过它使用广播机制来代替计数器回调接口ICounterCallback,使得CounterService组件不必依赖于Counter组件
应用程序Broadcounter的目录结构如下:
Android/packages/apps/Broadcounter
------AndroidManifest.xml
------Android.mk
------src
------com/android/broadcounter
------ICounterService.java
------CounterService.java
------Broadcounter.java
------res
------layout
------main.xml
------values
------strings.xml
------drawable
------icon.png
ICounterService
定义一个计数器接口ICounterService,用来启动和停止计数器。在启动计数器的时候,我们只需要指定计数器的初始值即可
而不必指定计数器回调接口。
package com.android.broadcounter;
public interface ICounterService {
public void startCounter(int initVal);
public void stopCounter();
}
CounterService.java
package com.android.broadcounter;
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 = "com.android.broadcounter.CounterService";
public final static String BROADCAST_COUNTER_ACTION = "com.android.broadcounter.COUNTER_ACTION";
public final static String COUNTER_VALUE = "com.android.broadcounter.counter.value";
private boolean stop = false;
private final IBinder binder = new CounterBinder();
public class CounterBinder extends Binder {
public CounterService getService() {
return CounterService.this;
}
}
public IBinder onBind(Intent intent) {
return binder;
}
@Override
public void onCreate() {
super.onCreate();
Log.i(LOG_TAG, "Counter Service Created");
}
public void startCounter(int initVal) {
AsyncTask task = new AsyncTask (){
@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(initVal);
}
public void stopCounter() {
stop = true;
}
}
该部分实现了一个CounterService组件,他也是通过异步任务(AsyncTask)来提供计数功能,并且该CounterService组件是通过
广播的形式将计数器的当前计数实时的更新打到Counter组件的用户界面上的。
在计数器的执行过程中,即在异步任务task的成员函数onProgressUpdate()函数中,CounterService组件会先将计数器的当前
计数值写入到一个类型为 BROADCAST_COUNTER_ACTION 的广播中,接着再调用其父类ContextWrapper的成员函数sendBroadcast()将这个广播发送出去,以便Counter组件可以接收到这个广播,并且获得里面的计数值。
同样,在计数器停止执行时,即在异步任务task的成员函数onPostExecute()中,CounterService组件也会先将计数器的最后计数值写入到一个类型为BROADCAST_COUNTER_ACTION 的广播中,接着再调用其父类ContextWrapper的成员函数sendBroadcast()将这个广播发送出去,以便Counter组件可以接收到这个广播,并且获得里面的计数值。
Broadcounter.java
package com.android.broadcounter;
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 Broadcounter extends Activity implements OnClickListener {
private final static String LOG_TAG = "com.android.broadcounter.Broadcounter";
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(Broadcounter.this, CounterService.class);
bindService(bindIntent,ServiceConnection , Context.BIND_AUTO_CREATE);
Log.i(LOG_TAG, "Broadcounter 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 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 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");
}
};
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);
}
};
}
此处代码实现了一个 Broadcounter组件,它在启动时,回调用bindService()来绑定 CounterService组件,以便可以获得它的
一个访问接口。并且这个Broadcounter组件是通过一个广播接收者来获得 CounterService组件内部的计数器的当前计数值。
Broadcounter组件在内部定义了一个广播接收者 counterActionReceiver,它是用来接收类型为 BROADCAST_COUNTER_ACTION 的广播的,以便可以获得CounterService组件的计数器的当前计数值,并且将它显示在用户界面上。
当 Broadcounter组件被激活时,即在它的成员函数onResume()中,它会调用其父类 ContextWrappe的成员函数registerReceiver()将广播接收者 counterActionReceiver 注册到 ActivityManagerService中,并且指定要接收的广播类型为 BROADCAST_COUNTER_ACTION
当Boradcounter组件被中止时,即在他的成员函数 onPause() 中,他会调用其父类 ContextWrappe的成员函数unregisterReceiver()来注销前面所注册的广播接收者counterActionReceiver,这亚航他就不会再接收到类型为 BROADCAST_COUNTER_ACTION的广播了。
main.xml
strings.xml
Broadcounter
Counter:
0
Start Counter
Stop Counter
AndroidManifest.xml
Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := My_Broadcounter
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
# Use the folloing include to make our test apk.
include $(call all-makefiles-under,$(LOCAL_PATH))
build/target/product/core.mk 中添加 My_Broadcounter
mmm packages/apps/Broadcounter/