Android08_广播接受者_服务
Posted 抓根宝
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android08_广播接受者_服务相关的知识,希望对你有一定的参考价值。
1 广播接受者 广播的发送是通过intent开启的
需要被接收的广播:电量不足,sd卡被移除,电话移除,短信到来等等
1.1 监视sd卡的状态
需求:监视sd卡的状态,并提示用户受到影响的功能.
步骤:①买个收音机
创建一个类,继承BroadcastReceiver类(可以看做当一个类继承了该类,如果sd卡发生状态改变会接收到一条信息?)
②装上电池
在清单文件中配置
<receiver>标签(接受者)
配置属性name=”全包名”一定要配置到对应的类,不然会报错
③调到对应频道
在receiver节点中配置意图过滤器intent-filter
//这条属性是sd卡什么被取消挂载
配置频道<action android:name=”android.intent.action.MEDIA_UNMOUNTED”>
配置sd卡的文件系统<data android:scheme=”file”></data>
④在继承类中,重写onReceive方法(接收到广播的时候调用)
1.2 开机启动的广播
①创建一个类,继承BroadCastReceiver
②配置<receiver>标签 配置属性name=”全包名”
③配置意图过滤器intent-filter
配置频道 action android:name=”android.intent.action.BOOT_COMPLETED”
开机启动没有任何数据,不需要配置DATA节点
④重写onReceive方法,开机启动的时候调用
//配置一个开机启动的应用
显示意图配置
intent.setClass(上下文,被开启类的字节码文件)
//如果要开启应用记得设置flag
intent.setFlags(Intent.FLAG_ACTIVITY_NEWFLAG)
context.startActivity(intent)//方法中带有的上下文
提示报错:Unable to start receiver XXXX,context requires the FLAG_ACTIVITY_newflag
原因:广播接受者提供的context缺少一个flag,在广播接受者里面开启activity需要添加一个flag,因为它缺少任务栈,不存在上下文的概念,无法运行.所以添加之后就是告诉 activity运行在自己的任务栈中
应用:一些勒索软件会在开机的时候启动应用,并且在activtiy中重写onBackActivity()方法(返回键的方法,重写之后就相当于把系统默认的返回键给覆盖了,不能返回退出)(home键,软件无法卸载,在后面会提到,这里只做了解)
1.3 开机启动细节问题:
高版本的系统,开机启动需要添加一个权限android.permission.RECEIVE_ROOT_COMPLETED(低版本不需要,4.1以后就需要了)
1.4 电话外播的广播接收者
实例:ip电话,比一般打电话要便宜一些
原因:把跨省电话变成本地电话(拨打电话先到通地区的ip电话提供者基站,再由他们进行网络传输到另一个地方的基站,相当于是网络通讯一样,17951,17909比较出名的网络ip电话运营商)
案例ip拨号器,用户可以在这个拨号器里设置自己的ip拨号,因为每个地方的ip电话运营商号码不一样
①定义页面,提示,号码输入框,提交按钮
②当用户输入ip号码之后,点击提交,就保存ip号码SharedPreferences
③如果内容为空,就取消ip拨号
④实现功能,外播电话的时候,如果号码是以0开头,就在号码前面加拨设置的号码.
⑤创建一个类,继承BroadcastReceiver,重写onreceiver类
⑥清单文件配置,receiver节点,name属性
定义intent-filter action动作,android.intent.action.NEW_OUTGOING_CALL//新电话拨打
⑦声明权限
Android.permission.PROCESS_OUTGOING_CALLS
⑧获取拨打的号码在onReceiver()方法中getResultData();获取外播的电话号码
setResultData()方法,设置外播的号码
额外:添加判断,如果开头是以0开头,就添加,不是就不添加
2.应用程序的安装和卸载
一般大公司的应用程序都会对手机应用的安装和卸载进行监视收据(大数据)
2.1 案例数据收集
①创建一个类,继承BroadCastReceiver
②清单文件配置,receiver节点,name属性
定义intent-filter
action动作,android.intent.action.PACKAGE.ADDED
android.intent.action.PACKAGE.REMOVED
<data android:scheme=”package”>标签
当有多个action的时候该如何去区分
③重写onReceiver
intent.getAction()//获取动作名称
判断是卸载还是安装动作
intent.getData().toString();//获取对应的信息(在别的广播中可能为空)
//这一个监听不需要配置权限?
2.2 短信的广播的接受者(4.4之后这一方法被屏蔽了,后续内容观察者可以监听短信数据库达到这一效果)
用处:解析短信广播,对短信进行解析,获取其中的验证码或其它信息之类
①创建一个类,继承BroadCastReceiver
②配置清单文件receiver+name属性
Intent-filter意图监听器 action:name=android.provider.Telephony.SMS_RECEIVED
(这个动作被谷歌从列表中移除,但是它依然可以存在,因为这是恶意的)
③配置权限RECEIVED_SMS权限
⑤重写onReceiver()方法
⑥短信的格式pdu(Protocol Data Unit协议数据单元),必须要对短信的格式很了解才可以解析短信.
安卓源码搜索网站
搜索对应信息,一般查看版本最低的源码,因为这里面的代码最简单,高版本中有更多的ui信息之类的会造成干扰,搜素android.provider.Telephony.SMS_RECEIVED即可
public static final SmsMessage[] getMessagesFromIntent(
Intent intent) {
//拿到一组短信
Object[] messages = (Object[]) intent.getSerializableExtra("pdus");
//强转成byte数据
byte[][] pduObjs = new byte[messages.length][];
for (int i = 0; i < messages.length; i++) {
pduObjs[i] = (byte[]) messages[i];
}
byte[][] pdus = new byte[pduObjs.length][];
int pduCount = pdus.length;
SmsMessage[] msgs = new SmsMessage[pduCount];
for (int i = 0; i < pduCount; i++) {
pdus[i] = pduObjs[i];
msgs[i] = SmsMessage.createFromPdu(pdus[i]);
}
return msgs;
}
//以上是谷歌写的,下面是简化版:
Object[] objs = (Object[])intent.getExtras().get(“pdus”);
//遍历数组,每一个obj都是一个byte[]数组的短信数据
SmsMessage msg= SmsMessage.createFormPdu((byte[])obj);
msg.getMessageBody();//获取短信体
msg.getOriginatingAddress();//获取发件人地址
⑦调用该方法getMessagesFromIntent();
SmsMessage[]message = getMessagesFromIntent(intent);
遍历该数组
msg.getMessageBody();//获得短信体
msg.getOriginatingAddress();//获取发件人地址
3,广播接收者的细节问题:
3.1即使应用程序的进程不存在,当广播事件触发的时候,广播接收者的进程就会被触发.
(控制台指令,start http://www.baidu.com可以打开默认浏览器,打开网页)
3.2 ||||4.0版本的广播(不装安全软件的情况下都是可以实现短信监听)
不同之处
低版本停止了应用程序,当广播事件触发的时候,广播接收者的应用就会被打开.
4.0之后,多了一个强行停止,相当于是把软件冻结,所有代码不起效,直到用处再次开启了该应用为止.
4 自定义广播消息和广播接收者
4.1自定义广播消息
①为了方便演示,在主界面中定义一个button用来发送广播
②在点击方法中,定义意图对象
定义动作setAction(任意字符串,一般以包名+用处实现);
//放置数据
intent.putExtra(xx,xx);
sendBroadcast(intent)发送广播
4.2 自定义广播接受者
①创建一个应用,继承BroadCastReceiver
②配置清单文件,意图过滤器,要跟发送的Action保持一致
③重写onReceiver()方法,可以通过getStringExtra(xx)获取信息
5,广播事件的类型
5.1有序广播:广播消息是按一定顺序传达的,高优先级的先得到广播,低优先级后得到,高优先级的可以拦截广播消息或者修改广播消息,效率比较低.
5.2无需广播:没有顺序,同时接受广播消息,效率较高
sendBoradCast()发送的是无序广播
5.3 有序广播的实现:
①文本显示框,提示这是一个什么app,按钮,发送广播
②找到控件,并且设置点击事件
③创建意图intent
设置动作setAction(全包名+预期)
sendBroadCast()//发送的是无序广播
sendOrderBroadCase(intent,receiverPermission(广播接收者需要的权限,目前设置为null),resultReciver(最终的广播接收者,目前定义为null),scheduler(实际上是一个handler,在主线程定义为null即可),initialcode(初始码,区分消息的类型),initialData(初始数据,),initialExtras(bundle对象,可以看做是一个集合,是一个intent额外扩展的对象.可以储存Extras数据,可以为null))//发送有序广播
参数详解
resultReceiver结果接收者的使用
①创建一个类继承BradeCastReceiver,不需要配置清单
②onReceiver()方法中,先获取getResultData()数据
③然后在发送有序广播的时候,new一个该类对象,当做resultReceiver的参数
可以获取到广播的最终结果,然后进行处理(如果中间有高优先级的接受者修改了数据,可以通过这个结果接收者的数据进行判断)
5.4 接收有序广播
①创建一个类,继承BroadCastReceiver,重现onReceive()方法
②获取初始数据,getResultData()//获取初始数据
③配置清单文件,意图过滤器中的Action要跟发送者的一致,
意图过滤器中可以配置一条属性,android:priority(-1000>>>1000)指定优先级
④高优先级的修改低优先级接收者.
abrotBroadCast()//终止广播事件,不会对结果接收者起效
setResultData(“修改数据的文本”)//对低于它的优先级有效.
如果发送了无序广播,中间对其进行修改会报:BroadeCastReceiver trying to return result during a not-orderd boradCast错误
5.5 系统的广播事件
设置了getResultData,setResultData的就是有序广播
有序的:外播电话的广播设置了最终的接受者,就是拨号界面,所以中间虽然将其终止也是无效的.短信也是有序的,可以被拦截.
(一些木马软件就是利用的高优先级拦截短信)
6,只能使用代码注册的广播事件
6.1 手机锁屏事件的监听
①创建一个类,继承BroadCastReceiver
②配置清单文件,receiver标签,name属性
定义intent-filter action:name=android.intent.action.SCREEN_OF/ON //锁屏//解锁
③继承类中重写onReceive()方法
//但是上面这种配置是无法生效的,因为屏幕的解锁锁屏属于一个高频的广播事件,因而会反复开启一个应用,消耗内存.所以直接在清单里配置是无效的.
//同样的事件还有电量变化的广播事件
//这类广播事件就必须要用代码注册(相当于是应用程序开启时有效?)
6.2 代码注册广播接收者
①在onCreate()初始化界面的时候,注册广播接收者
在onDestory()中销毁广播接收者
这样广播就只在应用程序开启的时候有效
②onCreate()方法中,继承Activity类中//注册广播接收者
ScreenStatusReceiver receiver = new ScreenStatusReceiver ();// 自己写的广播接收类.
//创建意图过滤器
IntentFilter filer = new ();
//添加动作,注册接收者
filter.addAction("android.intent.action.SCREEN_OFF");
filter.addAction("android.intent.action.SCREEN_ON");//注册广播接收者
registerReceiver(receiver,filter);
③如果不销毁广播接收者,在点击返回的时候会报错(但是系统会自动禁用掉)
//销毁广播接收者
unregisterReceiver(receiver);
//同时把对象赋值为null receiver = null;
这一广播的发送时间是在用户点了解锁之后发生,而不是在解锁之后看到屏幕.
应用场景:视频播放器,锁屏就应该暂停播放
地图应用:需要不停请求gps位置,锁屏就应该暂时停止获取
另一个应用
四大组件,Activity,BroadCast Receiver广播接收者,Service服务,内容提供者
7,服务
7.1介绍
Windows:长期后台运行,没有界面的进程就叫服务(主要是为了帮助处理业务逻辑)一般Windows的服务是独立的进程
Android:长期后台运行,并且没有界面的组件,但是仅运行在当前应用程序界面
7.2服务的简单入门
①创建一个类继承Service
Service父类和Activity的爷爷是同一个类,ContextWrapper
简单的理解:服务就是一个特殊的没有用户界面的,可以长期执行的Activity
②在清单文件里配置
节点:service name属性
③onCreate()方法,被系统调用,当服务第一次被创建,调用的代码
④onDestory()方法,当服务被取消,不再使用的时候调用的方法
额外:SystemClock.sleep(2000),程序休眠两秒,不会抛出异常,不需要try,catch
⑤单纯的写服务是无效果的,必须打开它才有用,这里就通过按钮去开启关闭服务.
定义一个意图对象intent(this,服务类字节码对象).
startService(intent);//开启服务
stopService(intent);//停止服务
问题:1.但是这样会出现服务无响应,ANR 应用程序无响应
原因服务这一操作是在主线程里执行
所以要在服务里进行不断的轮寻,带有延迟操作就一定要放在子线程里使用
2.销毁服务无效,onDestory()虽然把服务重置为空,但是子线程没有关闭,所以在子线程中写有无限循环的时候,最好用一个布尔变量去控制它
3.即使应用程序退出了,服务也会一致存在,除非应用程序被销毁了
7.3服务的应用场景:长期在后台运行,没有界面的组件
①监视一个硬件是否被插入,如sd卡
②股票应用中,每时每刻在更新K线图,不停的连接服务器获取最新的数据
③定时的轮询
注意小细节:服务是运行在主线程里面,不可以直接在服务里编辑一个耗时的逻辑(或访问网络)
7.4 服务的优先级
默认情况下:四大组件都是运行在主线程中.
进程的生命周期:安卓系统会尽可能的保证应用程序的进程存活时间,因为进程创建时需要花费一段时间的,为了保证系统的流畅性,就尽可能的延长生命周期,但是当系统内存不足的时候,就会移除一些进程来给更重要的进程使用.所以谷歌定义了一个进程优先级来判断哪些进程被优先移除,达到释放进程的目的.
Foreground process:前台进程,用户正在操作的应用程序所在的进程
Visible process:可视进程:用户已经不再操作这个应用程序,但是界面用户依然看得到.
(当一个应用程序被暂停,被一个透明的界面覆盖的时候就暂停了,但它属于可视进程)
Service process:服务进程,应用程序的服务代码正在运行
Background process:后台进程应用程序有界面,但是界面被最小化了,点击了home键(安卓上小房子的应用就是把应用程序最小化)
Empty process:空进程,应用程序没有任务运行的activity,service.
服务的优先级:前台进程>可视进程>服务进程>后台进程>空进程
一般而言前台进程是不会被回收的,除非内存少到一定地步了.
7.5,服务和子线程的区别
子线程在进程被清除以后会停止,优先级低
服务在被清楚以后,如果内存充足就会被复活(一定时间检查一次),优先级高
通过代码,或者系统中的应用管理可以停止服务
8.1 服务的生命周期(这是通过startService开启的服务生命周期)
①创建一个类,继承service,
②配置清单文件
③生命周期相关的方法
onCreate(),onDestory(),onStartCommand();
服务是不会自己创建自己开启的(广播会在接收到的时候创建),所以需要手动创建
onStartCommand()(接收到了开启的指令的时候调用)
服务只会被开启一次,再冲开启不会调用onCreate()方法,而是调用onStartCommand()方法和onStart()方法(这个方法被废弃,因为它没有界面)
8.2 练习:音乐播放器
音乐播放器需要在后台长期运行,在后台播放音乐,用服务,不容易被回收,后台也能播放
①界面布局:ListView遍历音乐列表,三个按钮,播放,下一首,停止
②找到这些控件,并且设置点击方法
③定义一个服务类继承service,在清单文件中进行配置
④新的API MediaPlayer mediaplayer;可以直接new出来
在onmediaplayer.setDataSource(路径)
数据的传递,Activiy中找到输入的文本,通过服务类 名.成员变量传递到服务类中(静态的,被所有对象共享)
mediaplayer.setLopping(false)//音乐只会被播放一次
//要想知道什么时候播放完毕
mediaplayer.setOnCompletionListener(//播放完毕后调用
提示信息,
Mediaplayer.seekTo(0) //设置进度条为0
Mediaplayer.start()
);Mediaplayer.prepare();//准备开始播放
⑤服务类中,
在停止的方法中,mediaplayer.stop();//停止播放器
Mediaplayer.release();//释放播放器
定义一个播放的方法,判断当前播放器是否开启了,如果没有开启,就启动它
判断路径是否存在(如果是遍历文件条目显示,就不用)
mediaplayer.isPlayering()//是否播放中,mp.start()//开始播放
定义一个暂停的方法,判断当前播放器是否存在,如果不为null,并且在播放中,就暂停
mediaplayer.pause();//暂停播放器
定义一个停止的方法,如果播放器不为null,就关闭它,同时重新准备播放
Mediaplayer.stop();
Mediaplayer.prepare();
⑥在Activity继承类中,通过intent给播放器传递信息
在服务类中,通过onStartCommand()方法,判断是哪一个指令
额外:重写onBackPressed()返回键方法让用户选择后台是否播放音乐
通过对话框
AlertDialog.Builder builder = new Builder(this);
//设置标题
Builder.setTitle(XXXX);
//设置具体内容
builder.setMessage(提醒文本);
builder.setPositiveButton(“第一个按钮继续播放”,点击事件)
点击事件:关闭页面
builder.setNegativeButton(“通知播放”点击事件)
点击事件:停止服务stopService(intent),关闭页面
提示框.show()
以上是关于Android08_广播接受者_服务的主要内容,如果未能解决你的问题,请参考以下文章
ANDROID_MARS学习笔记_S01原始版_013_广播机制二