注意:
1. 承载广播的进程与广播一起启动和终止,但如果包含了活动或服务,那么也会把他们的生命周期考虑在内,除了组件,其他都会被突然终止执行
2. 部分唤醒锁支持设备不打开屏幕等组件即可运行代码,并且必须在接收程序的主线代码中获取,否则来不及唤醒
3. IntentService的存在就是简化在Service中处理工作线程的模式,而WakefulIntentService保留了IntentService的语义并还获取了唤醒锁,而ALongRunningNonStickBroadcastService则是更进一步,拥有更加强大的处理能力。貌似后面两个类不是系统的API,是大牛根据IntentService改造的
4. 启动和关闭服务:启动服务后,在没有挂起消息的情况下,应该也要有相同数目的服务被关闭,否则会造成内存泄露
5. 获取和释放唤醒锁:获取和释放的唤醒锁数量不匹配的话,最坏的结果将是得设备实际运行的时间远远长于需要运行的时间,另外在不需要服务时并垃圾回收时,如果发现唤醒锁计数不匹配会报异常。
6. 不要在服务的onStart方法中执行工作,否则将再次持有主线程
7. 关于LightedGreenRoom中(简化与唤醒锁的交互的类),clientCount计算每种服务客户端,而count计算当前服务进入和退出的次数并作相关逻辑处理
8. 使用唤醒锁需要权限:<uses-permission android:name="android.permission.WAKE_LOCK" />
完整源码:
清单文件:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.alongrunningbroadcastreceiverservice"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="16" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<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>
<!-- 注册广播 -->
<receiver android:name=".broadcastreceiver.MyBroadcastReceiver">
<intent-filter android:priority="999">
<action android:name="along_running_brs"/>
</intent-filter>
</receiver>
<!-- 注册服务 -->
<service android:name=".service.MyService"/>
</application>
<!-- 唤醒锁需要的权限 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
</manifest>
触发广播的主界面:
/**
* 说明:多次重复点击,会按照顺序依次执行服务,执行完一个再执行一个
* 作者:
* 日期:2016-2-16
*/
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void doClick(View v){
//特性类型的广播intent
Intent intent = new Intent("along_running_brs");
intent.putExtra("message", "伟才测试30s的长期广播服务");
sendBroadcast(intent);
}
}
长期广播子类:
/**
* 说明:长期广播子类,这里指向哪个服务
* 作者:
* 日期:2016-2-16
*/
public class MyBroadcastReceiver extends ALongRunningReceiver {
//对于集成关系,可以用抽象方法模拟类似接口回调方法,接口更加解耦
@Override
public Class getLRSClass() {
Utils.logThreadSignature("TAG");
return MyService.class;//指向MyService服务
}
}
长期广播基类:
public abstract class ALongRunningReceiver extends BroadcastReceiver {
private static final String tag = "ALongRunningReceiver";
@Override
public void onReceive(Context context, Intent intent) {
LightedGreenRoom.setup(context);//需要提前启动唤醒锁
startService(context, intent);
}
private void startService(Context context, Intent intent) {
Intent serviceIntent = new Intent(context, getLRSClass());
serviceIntent.putExtra("original_intent", intent);
context.startService(serviceIntent);
}
/*
* Override this methode to return the "class" object belonging to the
* nonsticky service class.
*/
public abstract Class getLRSClass();
}
长期服务子类:
/**
* 说明:长期服务子类,handleBroadcastIntent方法处理工作线程的内容
* 作者:黄伟财
* 日期:2016-2-16
*/
public class MyService extends ALongRunningNonStickyBroadcastService {
public static String tag = "TAG";
// Required by IntentService
public MyService() {
super("com.ai.android.service.Test60SecBCRService");
}
/*
* Perform long running operations in this method. This is executed in a
* separate thread.
*/
@Override
protected void handleBroadcastIntent(Intent broadcastIntent) {
Utils.logThreadSignature(tag);
Utils.sleepForInSecs(30);//线程睡眠30s,模拟长期后台运行的耗时操作
String message = broadcastIntent.getStringExtra("message");
Log.d(tag, "Job completed");
Log.d(tag, message);
}
}
长期服务基类:
/**
* 说明:继承自IntentService服务,功能更加强大
* 作者:
* 日期:2016-2-16
*/
public abstract class ALongRunningNonStickyBroadcastService extends
IntentService {
public static String tag = "TAG";
protected abstract void handleBroadcastIntent(Intent broadcastIntent);
/*构造方法*/
public ALongRunningNonStickyBroadcastService(String name) {
super(name);
}
/*
* This method can be invoked under two circumstances 1. When a broadcast
* receiver issues a "startService" 2. when android restarts it due to
* pending "startService" intents.
*
* In case 1, the broadcast receiver has already setup the
* "lightedgreenroom".
*
* In case 2, we need to do the same.
*/
@Override
public void onCreate() {
super.onCreate();
// Set up the green room
// The setup is capable of getting called multiple times.
LightedGreenRoom.setup(this.getApplicationContext());//这里需要重复启动
// It is possible that there are more than one service
// of this type is running.
// Knowing the number will allow us to clean up
// the locks in ondestroy.
LightedGreenRoom.s_registerClient();
}
@Override
public int onStartCommand(Intent intent, int flag, int startId) {
// Call the IntentService "onstart"
super.onStart(intent, startId);//调用IntentService服务的onStart方法
// Tell the green room there is a visitor
LightedGreenRoom.s_enter();
// mark this as non sticky
// Means: Don't restart the service if there are no
// pending intents.
return Service.START_NOT_STICKY;
}
/*
* Note that this method call runs in a secondary thread setup by the
* IntentService.
*
* Override this method from IntentService. Retrieve the original broadcast
* intent. Call the derived class to handle the broadcast intent. finally
* tell the ligthed room that you are leaving. if this is the last visitor
* then the lock will be released.
* -----耗时操作放在如下方法-----
*/
@Override
final protected void onHandleIntent(Intent intent) {
try {
//把广播传过来的intent传递给handleBroadcastIntent,以便在子类中处理逻辑
Intent broadcastIntent = intent.getParcelableExtra("original_intent");
handleBroadcastIntent(broadcastIntent);
} finally {
LightedGreenRoom.s_leave();
}
}
/*
* If Android reclaims this process, this method will release the lock
* irrespective of how many visitors there are.
*/
@Override
public void onDestroy() {
super.onDestroy();
LightedGreenRoom.s_unRegisterClient();
}
}
工具类:
简化与唤醒锁的交互的类:
public class LightedGreenRoom {//简化与唤醒锁的交互的类
// debug tag
private static String tag = "LightedGreenRoom";
// *************************************************
// * A Public static interface
// * static members: Purely helper methods
// * Delegates to the underlying singleton object
// *************************************************
public static void setup(Context inCtx) {
if (s_self == null) {
Log.d(LightedGreenRoom.tag, "Creating green room and lighting it");
s_self = new LightedGreenRoom(inCtx);
s_self.turnOnLights();
}
}
public static boolean isSetup() {
return (s_self != null) ? true : false;
}
public static int s_enter() {
assertSetup();
return s_self.enter();
}
public static int s_leave() {
assertSetup();
return s_self.leave();
}
// Dont directly call this method
// probably will be deprecated.
// Call register and unregister client methods instead
public static void ds_emptyTheRoom() {
assertSetup();
s_self.emptyTheRoom();
return;
}
public static void s_registerClient() {
assertSetup();
s_self.registerClient();
return;
}
public static void s_unRegisterClient() {
assertSetup();
s_self.unRegisterClient();
return;
}
/**维护设置,检查是否设置了s_self对象*/
private static void assertSetup() {
if (LightedGreenRoom.s_self == null) {
Log.w(LightedGreenRoom.tag, "You need to call setup first");
throw new RuntimeException("You need to setup GreenRoom first");
}
}
// *************************************************
// * A pure private implementation
// *************************************************
// Keep count of visitors to know the last visitor.
// On destory set the count to zero to clear the room.
private int count;
// Needed to create the wake lock
private Context ctx = null;
// Our switch
PowerManager.WakeLock wl = null;//唤醒锁对象
// Multi-client support
//访问者计数
private int clientCount = 0;
/*
* This is expected to be a singleton. One could potentially make the
* constructor private I suppose.
*/
private LightedGreenRoom(Context inCtx) {
ctx = inCtx;
wl = this.createWakeLock(inCtx);
}
/*
* Setting up the green room using a static method. This has to be called
* before calling any other methods. what it does: 1. Instantiate the object
* 2. acquire the lock to turn on lights Assumption: It is not required to
* be synchronized because it will be called from the main thread. (Could be
* wrong. need to validate this!!)
*/
private static LightedGreenRoom s_self = null;
/*
* The methods "enter" and "leave" are expected to be called in tandem.
*
* On "enter" increment the count.
*
* Do not turn the lights or off as they are already turned on.
*
* Just increment the count to know when the last visitor leaves.
*
* This is a synchronized method as multiple threads will be entering and
* leaving.
*/
synchronized private int enter() {
count++;
Log.d(tag, "A new visitor: count:" + count);
return count;
}
/*
* The methods "enter" and "leave" are expected to be called in tandem.
*
* On "leave" decrement the count.
*
* If the count reaches zero turn off the lights.
*
* This is a synchronized method as multiple threads will be entering and
* leaving.
*/
synchronized private int leave() {
Log.d(tag, "Leaving room:count at the call:" + count);
// if the count is already zero
// just leave.
if (count == 0) {
Log.w(tag, "Count is zero.");
return count;
}
count--;
if (count == 0) {
// Last visitor
// turn off lights
turnOffLights();
}
return count;
}
/*
* Right now no one is using this method. Just in case.
*/
synchronized private int getCount() {
return count;
}
/*
* acquire the wake lock to turn the lights on it is upto other synchronized
* methods to call this at the appropriate time.
*/
private void turnOnLights() {
Log.d(tag, "Turning on lights. Count:" + count);
this.wl.acquire();//获取唤醒锁(系统做法)
}
/*
* Release the wake lock to turn the lights off. it is upto other
* synchronized methods to call this at the appropriate time.
*/
private void turnOffLights() {
if (this.wl.isHeld()) {
Log.d(tag, "Releasing wake lock. No more visitors");
this.wl.release();//释放唤醒锁
}
}
/*
* Standard code to create a partial wake lock
* 创建唤醒锁对象
*/
private PowerManager.WakeLock createWakeLock(Context inCtx) {
PowerManager pm = (PowerManager) inCtx
.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, tag);
return wl;
}
/**注册服务客户端*/
private int registerClient() {
Utils.logThreadSignature(tag);
this.clientCount++;//clientCount从0开始
Log.d(tag, "registering a new client:count:" + clientCount);
return clientCount;//访问者计数
}
/**取消注册服务客户端*/
private int unRegisterClient() {
Utils.logThreadSignature(tag);
Log.d(tag, "un registering a new client:count:" + clientCount);
if (clientCount == 0) {
Log.w(tag, "There are no clients to unregister.");
return 0;
}
// clientCount is not zero
clientCount--;
if (clientCount == 0) {
emptyTheRoom();
}
return clientCount;
}
synchronized private void emptyTheRoom() {
Log.d(tag, "Call to empty the room");
count = 0;
this.turnOffLights();
}
}
线程处理类:
public class Utils {
public static long getThreadId() {
Thread t = Thread.currentThread();
return t.getId();
}
public static String getThreadSignature() {
Thread t = Thread.currentThread();
long l = t.getId();
String name = t.getName();
long p = t.getPriority();
String gname = t.getThreadGroup().getName();
return (name + ":(id)" + l + ":(priority)" + p + ":(group)" + gname);
}
public static void logThreadSignature(String tag) {
Log.d(tag, getThreadSignature());
}
public static void sleepForInSecs(int secs) {
try {
Thread.sleep(secs * 1000);
} catch (InterruptedException x) {
throw new RuntimeException("interrupted", x);
}
}
}
另外补充,IntentService源码:
/**
* 说明:IntentService源码
* 作者:
* 日期:2016-2-16
*/
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private final class ServiceHandler extends Handler{
public ServiceHandler(Looper looper){
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf((Integer) msg.obj);
}
}
public IntentService(String name){
super();
mName = name;
}
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService["+mName+"]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
@Deprecated
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
@Override
public void onDestroy() {
mServiceLooper.quit();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
protected abstract void onHandleIntent(Intent intent);
}