长期广播服务

注意:
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);
	
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值