在公司做一个项目时,要添加背景音乐。项目当中有多个Activity。将背景音乐放在一个Service当中。但问题是:当在不是应用程序的首个Activity按下HOME键退出后,音乐依然播放,Service没有调用到StopService()方法。其实平常许多网络上的APK也同样有这样的问题。
所以查了相关资料有个方法比较有用:可以设置个全局变量
static boolean isBehind = false;
然后在每个Activity中做如下的处理:
在onResume方法中
将这个变量设置成false;
在onPause方法中
将这个变量设置成true;
在onStop方法中进行判断,
if(isBehind != false)
stopService;
当你跳转到另一个activity的时候,
具体的activity生命周期如图:
先执行第一个activity的onPause
此时isBehind = true;
然后执行第二个activity的onResume
此时此时isBehind = false;
当第二个activity is running的时候
第一个activity才会onStop
此时不会停止服务
当按home键的时候,就是直接onPause onStop,然后停止服务。
另一个问题:当我再次进入应用程序中时,发现背景音乐不再播放了。如果你希望此次背景音乐能继续播放,可以如下处理:
在每个Activitiy中 的onResume方法中 添加
if(!AudioService.isPlay){
startService(new Intent(this, AudioService.class));
}
isPlay为服务类Service中我自己定义的一个公共静态变量,在Service中的onStart方法中 为true 在onDestroy中为false
intent 英文意思是意图,pending 表示即将发生或来临的事情。
PendingIntent 这个类用于处理即将发生的事情,比如在通知Notification中用于跳转页面,但不是马上跳转。
Intent 是及时启动,intent 随所在的activity 消失而消失。
PendingIntent 可以看作是对intent的包装,通常通过getActivity,getBroadcast,getService来得到pendingintent的实例,当前activity并不能马上启动它所包含的intent,而是在外部执行 pendingintent 时,调用intent的。正由于pendingintent中保存有当前App的Context,使它赋予外部App一种能力,使得外部App可以如同当前App一样的执行pendingintent里的 Intent, 就算在执行时当前App已经不存在了,也能通过存在pendingintent里的Context照样执行Intent。另外还可以处理intent执行后的操作。PendingIntent常和alermanger 和notificationmanager一起使用。
Intent一般是用作Activity、Sercvice、BroadcastReceiver之间传递数据;而Pendingintent一般用在 Notification上,可以理解为延迟执行的intent,PendingIntent是对Intent一个包装。
PendingIntent pIntent = PendingIntent.getActivity(this, 0, new Intent(this, NotifyActivity.class), 0);
详见代码:
private void showNotify(){ PendingIntent pIntent = PendingIntent.getActivity(this, 0, new Intent(this, NotifyActivity.class), 0); // pendingIntent Notification notify = new Notification(); notify.icon = R.drawable.ic_launcher; notify.tickerText = "您有一条新消息"; notify.defaults = Notification.DEFAULT_SOUND; notify.when = System.currentTimeMillis(); notify.vibrate = new long[]{0, 50, 100, 150}; notify.contentIntent = pIntent; notify.setLatestEventInfo(this, "通知", "http://blog.csdn.net/sunboy_2050", pIntent); // context, title, msg, pendingIntent NotificationManager manager = (NotificationManager)getSystemService(this.NOTIFICATION_SERVICE); manager.notify(0, notify); }
通知结果:
GSM网络中android发送短信示例
private void showNotify222(){ String phone = "13212341234"; String msg = "http://blog.csdn.net/sunboy_2050"; PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, new Intent("com.homer.pendingintent.pendingbroadcast"), 0); // pendingIntent SmsManager manager = SmsManager.getDefault(); if(msg.length() > 70) { // split msg length List<String> msgList = manager.divideMessage(msg); for (String msg2 : msgList) { manager.sendTextMessage(phone, null, msg2, pIntent, null); // destinationAddress, scAddress, text, sentIntent, deliveryIntent } } else { manager.sendTextMessage(phone, null, msg, pIntent, null); } }
<receiver android:name=".PendingReceiver"> <intent-filter> <action android:name="com.homer.pendingintent.pendingbroadcast" /> </intent-filter> </receiver>
PendingReceiver 类 主要处理PendingIntent广播的事情,即发送短信成功后的提醒,实现如下:
public class PendingReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if(intent.getAction().equals("com.homer.pendingintent.pendingbroadcast")) { Toast.makeText(context, "Test PendingIntent Success", Toast.LENGTH_LONG).show(); } } }
AndroidManifest.xml 根节点下,添加短信发送权限:
<uses-permission android:name="android.permission.SEND_SMS" />
运行结果:
代码下载
代码解释:
PendingIntent 是一个Intent的描述(封装),可以把这个描述交给别的程序,别的程序根据这个描述在后面的时间做自己安排做的事情 (By giving a PendingIntent to another application, you are granting it the right to perform the operation you have specified as if the other application was yourself),就相当于PendingIntent代表了Intent。
本例中别的程序就是发送短信的程序,短信发送成功后要把intent广播出去
SmsManager.sendTextMessage(String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent)
1)PendingIntent sentIntent:当短信发出时,成功的话sendIntent会把其内部的描述的intent广播出去,否则产生错误代码并通过android.app.PendingIntent.OnFinished进行回调,这个参数最好不为空,否则会存在资源浪费的潜在问题;
2)PendingIntent deliveryIntent:是当消息已经传递给收信人后所进行的PendingIntent广播。
如果BroadcastReveiver注册接收相应的消息,你就会收到相应的Intent,这时候就可以根据Intent的Action,执行相应的动作,这就是上面说的in the future的含义;
可以获得PendingIntent实例的静态方法:
- public static PendingIntent getActivity(Context context, int requestCode, Intent intent, int flags)
- public static PendingIntent getActivities(Context context, int requestCode, Intent[] intents, int flags)
- public static PendingIntent getService(Context context, int requestCode, Intent intent, int flags)
- public static PendingIntent getBroadcast(Context context, int requestCode, Intent intent, int flags)
flags参数有四个:
1) FLAG_ONE_SHOT :this PendingIntent can only be used once. If set, after send() is called on it, it will be automatically canceled for you and any future attempt to send through it will fail.
2) FLAG_UPDATE_CURRENT :if the described PendingIntent already exists, then keep it but its replace its extra data with what is in this new Intent. This can be used if you are creating intents where only the extras change, and don't care that any entities that received your previous PendingIntent will be able to launch it with your new extras even if they are not explicitly given to it.
3) FLAG_NO_CREATE :if the described PendingIntent does not already exist, then simply return null instead of creating it.
4) FLAG_CANCEL_CURRENT :if the described PendingIntent already exists, the current one is canceled before generating a new one. You can use this to retrieve a new PendingIntent when you are only changing the extra data in the Intent; by canceling the previous pending intent, this ensures that only entities given the new data will be able to launch it. If this assurance is not an issue, consider FLAG_UPDATE_CURRENT.
FLAG_UPDATE_CURRENT 这个简单解释一下,就是当存在时,先把原来的取消,然后创建一个新的,在AlarmManager服务时修改一个闹铃,用的比较笨的的方法,先取消然后重新注册,其实加上这个参数就行了。要注意的是,这个只更新extra data,不会修改其他内容,不能new一个Intent,还有就是如果你的Intent中需要传递Id或者其他数据,一定要用这个flags或者FLAG_CANCEL_CURRENT,曾经一直接收不到Id,查了半天原来是这个原因
上面4个flag中最经常使用的是FLAG_UPDATE_CURRENT,因为Intent有更新的时候需要用到这个flag去更新你的描述(确切的说是更新extra data),否则组件在下次事件发生或时间到达的时候extras永远是第一次Intent的extras,使用FLAG_CANCEL_CURRENT也能做到更新extras,只不过是先把前面的extras清除,另外FLAG_CANCEL_CURRENT和FLAG_UPDATE_CURRENT的区别在于能否新new一个Intent,FLAG_CANCEL_CURRENT能够新new一个Intent,而FLAG_UPDATE_CURRENT则不能,只能使用第一次的Intent。
还有一个问题就是怎么区分PendingIntent,主要是取消的时候要用到requestCode区分,但系统还是根据Intent的Action去区分的,如果Intent设置了Class,classData,取消的时候Intent一定要设置要相同的,不然取消不掉就可能出现取消后Alarm还会响的问题,PendingIntent用的地方蛮多的,像 Notifications, AlarmManager等都会用到。。。
查看PendingIntent 类可以看到许多的Send函数,就是PendingIntent在进行被赋予的相关的操作。
参考推荐:
PendingIntent(Android Developer)
PendingIntent与Intent区别
android发送短信
转自 http://www.dreamingwish.com/dream-2012/of-of-of-of-gcd-introduced-1-basic-concepts-in-and-the-dispatch-queue.html
什么是GCD?
Grand Central Dispatch或者GCD,是一套低层API,提供了一种新的方法来进行并发程序编写。从基本功能上讲,GCD有点像NSOperationQueue,他们都允许程序将任务切分为多个单一任务然后提交至工作队列来并发地或者串行地执行。GCD比之NSOpertionQueue更底层更高效,并且它不是Cocoa框架的一部分。
除了代码的平行执行能力,GCD还提供高度集成的事件控制系统。可以设置句柄来响应文件描述符、mach ports(Mach port 用于 OS X上的进程间通讯)、进程、计时器、信号、用户生成事件。这些句柄通过GCD来并发执行。
GCD的API很大程度上基于block,当然,GCD也可以脱离block来使用,比如使用传统c机制提供函数指针和上下文指针。实践证明,当配合block使用时,GCD非常简单易用且能发挥其最大能力。
你可以在Mac上敲命令“man dispatch”来获取GCD的文档。
为何使用?
GCD提供很多超越传统多线程编程的优势:
Dispatch Objects
尽管GCD是纯c语言的,但它被组建成面向对象的风格。GCD对象被称为dispatch object。Dispatch object像Cocoa对象一样是引用计数的。使用dispatch_release和dispatch_retain函数来操作dispatch object的引用计数来进行内存管理。但主意不像Cocoa对象,dispatch object并不参与垃圾回收系统,所以即使开启了GC,你也必须手动管理GCD对象的内存。
Dispatch queues 和 dispatch sources(后面会介绍到)可以被挂起和恢复,可以有一个相关联的任意上下文指针,可以有一个相关联的任务完成触发函数。可以查阅“man dispatch_object”来获取这些功能的更多信息。
Dispatch Queues
GCD的基本概念就是dispatch queue。dispatch queue是一个对象,它可以接受任务,并将任务以先到先执行的顺序来执行。dispatch queue可以是并发的或串行的。并发任务会像NSOperationQueue那样基于系统负载来合适地并发进行,串行队列同一时间只执行单一任务。
GCD中有三种队列类型:
创建队列
要使用用户队列,我们首先得创建一个。调用函数dispatch_queue_create就行了。函数的第一个参数是一个标签,这纯是为了debug。Apple建议我们使用倒置域名来命名队列,比如“com.dreamingwish.subsystem.task”。这些名字会在崩溃日志中被显示出来,也可以被调试器调用,这在调试中会很有用。第二个参数目前还不支持,传入NULL就行了。
提交 Job
向一个队列提交Job很简单:调用dispatch_async函数,传入一个队列和一个block。队列会在轮到这个block执行时执行这个block的代码。下面的例子是一个在后台执行一个巨长的任务:
dispatch_async 函数会立即返回, block会在后台异步执行。
当然,通常,任务完成时简单地NSLog个消息不是个事儿。在典型的Cocoa程序中,你很有可能希望在任务完成时更新界面,这就意味着需要在主线程中执行一些代码。你可以简单地完成这个任务——使用嵌套的dispatch,在外层中执行后台任务,在内层中将任务dispatch到main queue:
还有一个函数叫dispatch_sync,它干的事儿和dispatch_async相同,但是它会等待block中的代码执行完成并返回。结合 __block类型修饰符,可以用来从执行中的block获取一个值。例如,你可能有一段代码在后台执行,而它需要从界面控制层获取一个值。那么你可以使用dispatch_sync简单办到:
我们还可以使用更好的方法来完成这件事——使用更“异步”的风格。不同于取界面层的值时要阻塞后台线程,你可以使用嵌套的block来中止后台线程,然后从主线程中获取值,然后再将后期处理提交至后台线程:
dispatch_queue_t bgQueue = myQueue; dispatch_async(dispatch_get_main_queue(), ^{ NSString *stringValue = [[[textField stringValue] copy] autorelease]; dispatch_async(bgQueue, ^{ // use stringValue in the background now }); });
取决于你的需求,myQueue可以是用户队列也可以使全局队列。
不再使用锁(Lock)
用户队列可以用于替代锁来完成同步机制。在传统多线程编程中,你可能有一个对象要被多个线程使用,你需要一个锁来保护这个对象:
NSLock *lock;
访问代码会像这样:
使用GCD,可以使用queue来替代:
dispatch_queue_t queue;
要用于同步机制,queue必须是一个用户队列,而非全局队列,所以使用usingdispatch_queue_create初始化一个。然后可以用dispatch_async 或者 dispatch_sync将共享数据的访问代码封装起来:
值得注意的是dispatch queue是非常轻量级的,所以你可以大用特用,就像你以前使用lock一样。
现在你可能要问:“这样很好,但是有意思吗?我就是换了点代码办到了同一件事儿。”
实际上,使用GCD途径有几个好处:
总结
现在你已经知道了GCD的基本概念、怎样创建dispatch queue、怎样提交Job至dispatch queue以及怎样将队列用作线程同步。接下来我会向你展示如何使用GCD来编写平行执行代码来充分利用多核系统的性能^ ^。我还会讨论GCD更深层的东西,包括事件系统和queue targeting。