因为在跟踪GPS轨迹的时候开启了一个新的service来记录GPS数据,将结果显示在主页面,采用的是在service中使用broadCastReceiver发送广播在activity中收到广播后更新界面,在这学习一下broadCastReceiver。
Android广播
- 标准广播
完全异步执行的广播,广播发出后,所有广播接收器几乎同一时刻接收到这条广播消息,无先后顺序。效率高,无法被截断。 - 有序广播
同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器就无法收到广播消息了。
接收系统广播
Android内置系统级别的广播,可在自己的APP中通过监听这些广播得到各种系统的状态信息。
广播接收器可自由地对自己感兴趣的广播进行注册,当有相应的关闭发出时,节能收到该广播。
注册广播的方式:
动态注册——Java代码中注册,一定要取消注册——销毁时unregisterReceiver;可自由的控制注册和注销,在灵活性方面有很大的优势,即必须在程序启动之后才能接受到广播。
静态注册——在AndroidMainfest.xml中注册。可在APP未启动的状态下就能接收到广播。在<application>标签内新建一个新的标签<receiver>所有的静态注册的广播接收器都在此注册。在<intent-filter>中加入想要的广播。
创建广播接收器:
新建一个类继承BroadCastReceiver,并重写父类的onReceive()方法。
注意:不要在onReceive()中添加过多的逻辑或进行任何耗时操作,因为广播接收器中不允许开启线程
发送自定义广播
广播——可跨进程的通信方式,我们的APP内发出的广播其他的APP也可接收到。
1. 发送标准广播
sendBroadcast()
2. 发送有序广播
系统会根据接收者声明的优先级别按顺序逐个执行接收者,前面的接收者有权终止广播(BroadcastReceiver.abortBroadcast()),如果广播被前面的接收者终止,后面的接收者就再也无法获取到广播。对于有序广播,前面的接收者可以将处理结果存放进广播Intent,然后传给下一个接收者。
sendOrderedBroadcast(),前面的广播可以将广播截断阻止继续传播。
设定广播的先后顺序:
①在注册的时候修改AndroidManifest.xml中的代码,android:priority的值。数越大优先级别越高,取值范围:-1000到1000。
②调用IntentFilter对象的setPriority()进行设置,被接收者依次接收广播。
当BroadcastReceiver在10秒内没有执行完毕,Android会认为该程序无响应。所以在BroadcastReceiver里不能做一些比较耗时的操作,否侧会弹出ANR(Application No Response)的对话框。如果需要完成一项比较耗时的工作,应该通过发送Intent给Service,由Service来完成。而不是使用子线程的方法来解决,因为BroadcastReceiver的生命周期很短(在onReceive()执行后BroadcastReceiver 的实例就会被销毁),子线程可能还没有结束BroadcastReceiver就先结束了。如果BroadcastReceiver结束了,它的宿主进程还在运行,那么子线程还会继续执行。但宿主进程此时很容易在系统需要内存时被优先杀死,因为它属于空进程(没有任何活动组件的进程)。
本地广播机制
安全问题——之前讨论的发送接收的广播全部都是属于西永全局广播,即发出的广播可以被其他任何APP接收到,且可接收来自其他任何APP的广播。
本地广播机制——发出的广播只能够在APP内部进行传递,且广播接收器也只能接收来自自身APP发出的广播。
实现——使用LocalBroadcastManager对广播进行管理。
注:本地广播无法通过静态注册方式来接收。
静态广播——让APP在未启动的时候也能接收广播。
本地广播优势:
- 明确知道正在发送的广播不会离开我们的APP,不必担心数据泄露。
- 其他APP无法将广播发送到该APP内,不必担心有安全漏洞隐患。
- 发送本地广播比发送全局广播更高效。
所以,在跟踪运动轨迹的APP中采用本地广播的方式。
想法:
通过学习关于广播与广播接收器后的想法,看的大牛们请批评指正;
- 关于百度网盘中的备份问题,关于短信备份,
- 百度网盘在permission中申请SMS相关权限
- 申请静态的广播接收器(不打开百度网盘的时候也可将接受到的新的信息直接备份)
- 收到短信后将信息发送至相应用户的服务器上。(是否考虑创建一种APP没有界面,直接将新信息发送到指定的服务器完成短信监听)。
这只是考虑到新的信息的备份,还有删除信息后短信的备份。
百度网盘中短信备份肯定不只这一点,但是可以做一个新的短信管理的APP,功能:收信息+屏蔽黑名单信息+信息分类(个人还是广告);
- 加入相关权限
- 用静态方式注册接受广播
- 不显示黑名单信息:在接收到新信息后在onReceive()方法中,加入短信来源的判断,是否为黑名单用户,如果是黑名单信息,退出;否则继续。
- 显示信息类型:根据新信息的手机号判断是个人信息还是广告类型;
关于百度网盘手机忘带功能分析:
- 记得百度网盘有手机忘带的功能,让用户不错过短信与电话的功能;
- 开相关的权限
- 监听短信与电话,坚挺到后上传至服务器,服务器在通知pc端的用户。
生命周期
广播接收者的生命周期非常短暂,在接收到广播的时候创建,onReceive()方法结束时销毁。
广播接收者中不要做耗时工作,否则弹出Application No Response(ANR)错误对话框。
- 最好也不要在广播接收者中创建子线程做耗时操作,因为广播接收者呗销毁后进城就成为空进程,很容易被系统kill掉。
- 耗时的较长的工作最好放在服务中完成。
自己的广播只让指定APP接收
- 自己的应用
- 动态——本地广播localBroadCastManager管理;
- 在发送广播的时候给自己发送的广播添加自定义权限:
<permission Android:name = "com.ppl.android.permission"
android:protectionLevel="normal"></permission>
<users-permission android:name= "com.ppl.android.permission">
其他应用
- 必须知道要是用的广播的权限,然后再自己的清单文件中配置:
最终广播接收者
最终广播接收者——自己APP发送有序广播时通过ContextWrapper.sendOrderdBroadcast()方法指定当前APP下的广播,该广播可能被执行两次。①作为普通广播按优先级接收广播②作为final receiver必须接收一次。——不懂
广播的优先级对无序广播也生效。
动态注册广播的优先级——谁先注册谁优先级高。
判断当前的BroadCastReceiver接收到的是有序广播还是无序广播?
在onReceiver()方法中,调用Boolean b = isOrderdBroadcast();
该方法是BroadCastReceiver类中提供。
———-
理论分析结束,上代码
一,广播接收类;
方法:创建BroadcastReceiver的子类,由于BroadcastReceiver本质上是一种监听器,所以创建BroadcastReceiver的方法也非常简单,只需要创建一个BroadcastReceiver的子类然后重写onReceive (Context context, Intentintent)方法即可。
public class MyBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Log.e("MyBroadcastReceiver", "MyBroadcastReceiver onReceive!");
}
}
二、注册广播
一旦实现了BroadcastReceiver,接下就应该指定该BroadcastReceiver能匹配的Intent即注册BroadcastReceiver。
静态注册:
这种方法是在配置AndroidManifest.xml配置文件中注册,通过这种方式注册的广播为常驻型广播,也就是说如果应用程序关闭了,有相应事件触发程序还是会被系统自动调用运行。
<!-- 在配置文件中注册BroadcastReceiver能够匹配的Intent -->
<receiver android:name="com.ppl.broadcastreceiver.MyBroadcastReceiver" >
<intent-filter>
<action android:name="android.intent.action.MyBroadcastReceiver" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
动态注册
这种方法是通过代码在.Java文件中进行注册。通过这种方式注册的广播为非常驻型广播,即它会跟随Activity的生命周期,所以在Activity结束前我们需要调用unregisterReceiver(receiver)方法移除它。
//通过代码的方式动态注册MyBroadcastReceiver
private void registerMyReceiver() {
MyBroadcastReceiver recevier = new MyBroadcastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.action.MyBroadcastReceiver");
registerReceiver(recevier, filter);
}
注意,在实际应用中,我们在Activity或Service中注册了一个BroadcastReceiver,当这个Activity或Service被销毁时如果没有解除注册,系统会报一个异常,提示我们是否忘记解除注册了。所以,记得在特定的地方执行解除注册操作:
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
unregisterReceiver(recevier);
}
三、发送广播
当注册完成之后,这个接收者就可以正常工作了。我们可以用以下方式向其发送一条广播。
public void sendBroadcast(View view){
Intent intent = new Intent("android.intent.action.MyBroadcastReceiver");
sendBroadcast(intent);
}
结果:
完成基本的用法,接下来有序广播
静态注册方式:
再新建一个广播接收器MyBroadcastReceiverSecond
public class MyBroadcastReceiverSecond extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Log.e("MyBroadcastReceiverSecond", "MyBroadcastReceiverSecond onReceive!");
}
}
在Manifest清单中注册:
<receiver android:name="com.ppl.broadcastreceiver.MyBroadcastReceiver" >
<intent-filter
android:priority="100">
<action android:name="android.intent.action.MyBroadcastReceiver" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<receiver android:name="com.ppl.broadcastreceiver.MyBroadcastReceiverSecond" >
<intent-filter
android:priority="99">
<action android:name="android.intent.action.MyBroadcastReceiver" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
直接发送:
public void sendBroadcast(View view){
Intent intent = new Intent("android.intent.action.MyBroadcastReceiver");
sendBroadcast(intent);
}
执行后的结果:
动态注册,发送有序广播
直接发送:
MyBroadcastReceiver recevier;
MyBroadcastReceiverSecond receiverSecond;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
registerMyReceiver();
registerMyReceiverSecond();
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
unregisterReceiver(recevier);
unregisterReceiver(receiverSecond);
}
private void registerMyReceiver() {
recevier = new MyBroadcastReceiver();
IntentFilter filter = new IntentFilter();
filter.setPriority(99);
filter.addAction("android.intent.action.MyBroadcastReceiver");
registerReceiver(recevier, filter);
}
private void registerMyReceiverSecond() {
receiverSecond = new MyBroadcastReceiverSecond();
IntentFilter filter = new IntentFilter();
filter.setPriority(101);
filter.addAction("android.intent.action.MyBroadcastReceiver");
registerReceiver(receiverSecond, filter);
}
public void sendBroadcast(View view){
Intent intent = new Intent("android.intent.action.MyBroadcastReceiver");
sendBroadcast(intent);
}
结果:
截断:
当发送方式为无序广播的时候,当用abortBroadcast()的时候会报错:
BroadcastReceiver trying to return result during a non-ordered broadcast
结论
发送普通广播的时候:广播为异步发送所有广播接收者都能收到广播;且优先级也有效。
只有在发送有序广播的时候,截断才能执行!
结合我的计划轨迹跟踪的APP代码分析
先写我的逻辑
在地图页面显示开始按钮,其实开始并不是开始记录轨迹 而是开始搜索GPS,直至GPS稳定。
实现过程:
- 点击开始 开启service 去搜索GPS状态;
- 动态注册一个本地BroadCastReceiver,用于接收GPS状态稳定信息。
- 取消搜索GPS状态时,记得取消BroadCastReceiver的注册和stopService;
- 当GPS状态稳定时,“开始”按钮可以点击,点击开始后,返回主页面,并携带 开始 数据;