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协议数据单元),必须要对短信的格式很了解才可以解析短信.

安卓源码搜索网站

www.grepcode.com,

搜索对应信息,一般查看版本最低的源码,因为这里面的代码最简单,高版本中有更多的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_广播机制二

ANDROID_MARS学习笔记_S01原始版_012_广播机制一

[Android]_[初级]_[发送广播时序列化报错]

[Android]_[初级]_[发送广播时序列化报错]

[Android]_[初级]_[发送广播时序列化报错]

Android系统_SystemUI_android10_添加控制底部导航栏广播