转自:http://www.eoeandroid.com/thread-178533-1-1.html
Android 的广播机制在 Android 里面有各种各样的广播,比如电池的使用状态,电话的接收和短信的接收都会产生一个广播,应用程序开发者也可以监听这些广播并做出程序逻辑的处理。下面我画一张粗略的图来帮助大家理解广播的运行机制。
Android 中有各式各样的广播,各种广播在Android 系统中运行,当系统/应用程序运行时便会向 Android 注册各种广播,Android 接收到广播会便会判断哪种广播需要哪种事件,然后向不同需要事件的应用程序注册事件,不同的广播可能处理不同的事件也可能处理相同的广播事件,这时就需要Android 系统为我们做筛选。
案例分析:
一个经典的电话黑名单,首先通过将黑名单号码保存在数据库里面,当来电时,我们接收到来电广播并将黑名单号码与数据库中的某个数据做匹配,如果匹配的话则做出相应的处理,比如挂掉电话、比如静音等等。。。
Demo 分析:
下面通过一个小DEMO 来讲解一下广播在Android 中如何编写,在Demo中我们设置了一个按钮为按钮设置点击监听通过点击发送广播,在后台中接收到广播并打印LOG信息。代码如下:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public class BroadCastActivity extends Activity {
public static final String ACTION_INTENT_TEST = "com.terry.broadcast.test" ;
@Override public void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.main); Button btn = (Button) findViewById(R.id.Button01); btn.setOnClickListener( new OnClickListener() {
@Override public void onClick(View v) {
// TODO Auto-generated method stub Intent intent = new Intent(ACTION_INTENT_TEST);
sendBroadcast(intent); } }); } } 接收器代码如下: public class myBroadCast extends BroadcastReceiver {
public myBroadCast() {
Log.v( "BROADCAST_TAG" , "myBroadCast" );
} @Override public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub Log.v( "BROADCAST_TAG" , "onReceive" );
} } |
Android 广播的生命周期在上面的接收器中,继承了BroadcastReceiver 并重写了它的onReceive 并构造了一个函数,下面通过图片来一步一步认识 Android 广播的生命周期。当我点击一下按钮,它向Android 发送了一个广播,如下图:
下面本人画一张图像,描述了Android 中广播的生命周期,其次它并不像Activity 一样复杂,运行原理很简单如下图:
Android 如何判断并筛选广播?前面说过 Android 的广播有各式各样,那么Android 系统是如何帮我们处理我们需要哪种广播并为我们提供相应的广播服务呢?这里有一点需要大家注意,每实现一个广播接收类必须在我们应用程序中的 manifest 中显式的注明哪一个类需要广播,并为其设置过滤器,如下图:
Tip:action 代表一个要执行的动作,在Andriod 中有很action 比如 ACTION_VIEW,ACTION_EDIT
那么有些人会问了,如果我在一个广播接收器中要处理多个动作呢?那要如何去处理?
在Android 的接收器中onReceive 以经为我们想到的,同样的你必须在Intent-filter 里面注册该动作,可以是系统的广播动作也可以是自己需要的广播,之后你之需要在onReceive 方法中,通过intent.getAction()判断传进来的动作即可做出不同的处理,不同的动作。具体大家可以去尝试测试一下。
小结:
在Android 中如果要发送一个广播必须使用sendBroadCast 向系统发送对其感兴趣的广播接收器中。
使用广播必须要有一个intent 对象必设置其action动作对象
使用广播必须在配置文件中显式的指明该广播对象
每次接收广播都会重新生成一个接收广播的对象
在BroadCast 中尽量不要处理太多逻辑问题,建议复杂的逻辑交给Activity 或者 Service 去处理
Android广播机制(两种注册方法)
android下,要想接受广播信息,那么这个广播接收器就得我们自己来实现了,我们可以继承BroadcastReceiver,就可以有一个广播接受器了。有个接受器还不够,我们还得重写BroadcastReceiver里面的onReceiver方法,当来广播的时候我们要干什么,这就要我们自己来实现,不过我们可以搞一个信息防火墙。具体的代码:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
public class SmsBroadCastReceiver extends BroadcastReceiver
{ @Override
public void onReceive(Context context, Intent intent)
{ Bundle bundle = intent.getExtras(); Object[] object = (Object[])bundle.get( "pdus" );
SmsMessage sms[]= new SmsMessage[object.length];
for ( int i= 0 ;i<object.length;i++)
{ sms[ 0 ] = SmsMessage.createFromPdu(( byte [])object<i>);
</i> Toast.makeText(context, "来自" +sms.getDisplayOriginatingAddress()+ " 的消息是:" +sms.getDisplayMessageBody(), Toast.LENGTH_SHORT).show();
} //终止广播,在这里我们可以稍微处理,根据用户输入的号码可以实现短信防火墙。
abortBroadcast(); } } |
当实现了广播接收器,还要设置广播接收器接收广播信息的类型,这里是信息:android.provider.Telephony.SMS_RECEIVED
我们就可以把广播接收器注册到系统里面,可以让系统知道我们有个广播接收器。这里有两种,一种是代码动态注册:
//生成广播处理
smsBroadCastReceiver = new SmsBroadCastReceiver();
//实例化过滤器并设置要过滤的广播
IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
//注册广播
BroadCastReceiverActivity.this.registerReceiver(smsBroadCastReceiver, intentFilter);
一种是在AndroidManifest.xml中配置广播
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<?xml version= "1.0" encoding= "utf-8" ?>
<manifest xmlns:android= "http://schemas.android.com/apk/res/android"
package = "spl.broadCastReceiver"
android:versionCode= "1"
android:versionName= "1.0" >
<application android:icon= "@drawable/icon" android:label= "@string/app_name" >
<activity android:name= ".BroadCastReceiverActivity"
android:label= "@string/app_name" >
<intent-filter> <action android:name= "android.intent.action.MAIN" />
<category android:name= "android.intent.category.LAUNCHER" />
</intent-filter> </activity> <!--广播注册--> <receiver android:name= ".SmsBroadCastReceiver" >
<intent-filter android:priority= "20" >
<action android:name= "android.provider.Telephony.SMS_RECEIVED" />
</intent-filter> </receiver> </application> <uses-sdk android:minSdkVersion= "7" />
<!-- 权限申请 --> <uses-permission android:name= "android.permission.RECEIVE_SMS" ></uses-permission>
</manifest> |
两种注册类型的区别是:
1)第一种不是常驻型广播,也就是说广播跟随程序的生命周期。
2)第二种是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。
BroadcastReceiver用于监听被广播的事件
必须被注册,有两种方法:
1、在应用程序的代码中注册
注册BroadcastReceiver:
registerReceiver(receiver,filter);
取消注册BroadcastReceiver:
unregisterReceiver(receiver);
当BroadcastReceiver更新UI,通常会使用这样的方法注册。启动Activity时候注册 BroadcastReceiver,Activity不可见时候,取消注册。
2、在androidmanifest.xml当中注册
01
02
03
04
05
|
<receiver> <intent-filter> <action android:name = "android.intent.action.PICK" />
</intent-filter> </receiver> |
使用这样的方法注册弊端:它会始终处于活动状态,毕竟是手机开发,cpu和电源资源比较少,一直处于活动耗费大,不利。
请解释下在单线程模型中Message、Handler、Message Queue、Looper之间的关系。
1. Android进程
在了解Android线程之前得先了解一下Android的进程。当一个程序第一次启动的时候,Android会启动一个LINUX进程和一个主线程。默认的情况下,所有该程序的组件都将在该进程和线程中运行。
同时,Android会为每个应用程序分配一个单独的LINUX用户。Android会尽量保留一个正在运行进程,只在内存资源出现不足时,Android 会尝试停止一些进程从而释放足够的资源给其他新的进程使用,也能保证用户正在访问的当前进程有足够的资源去及时地响应用户的事件。Android会根据进程中运行的组件类别以及组件的状态来判断该进程的重要性,Android会首先停止那些不重要的进程。按照重要性从高到低一共有五个级别:
前台进程
前台进程是用户当前正在使用的进程。只有一些前台进程可以在任何时候都存在。他们是最后一个被结束的,当内存低到根本连他们都不能运行的时候。一般来说,在这种情况下,设备会进行内存调度,中止一些前台进程来保持对用户交互的响应。
可见进程
可见进程不包含前台的组件但是会在屏幕上显示一个可见的进程是的重要程度很高,除非前台进程需要获取它的资源,不然不会被中止。
服务进程
运 行着一个通过startService() 方法启动的service,这个service不属于上面提到的2种更高重要性的。service所在的进程虽然对用户不是直接可见的,但是他们执行了用户非常关注的任务(比如播放mp3,从网络下载数据)。只要前台进程和可见进程有足够的内存,系统不会回收他们。
后台进程
运 行着一个对用户不可见的activity(调用过 onStop() 方法).这些进程对用户体验没有直接的影响,可以在服务进程、可见进程、前台进程需要内存的时候回收。通常,系统中会有很多不可见进程在运行,他们被保存在LRU (least recently used) 列表中,以便内存不足的时候被第一时间回收。如果一个activity正确的执行了它的生命周期,关闭这个进程对于用户体验没有太大的影响。
空进程
未运行任何程序组件。运行这些进程的唯一原因是作为一个缓存,缩短下次程序需要重新使用的启动时间。系统经常中止这些进程,这样可以调节程序缓存和系统缓存的平衡。
Android 对进程的重要性评级的时候,选取它最高的级别。另外,当被另外的一个进程依赖的时候,某个进程的级别可能会增高。一个为其他进程服务的进程永远不会比被服务的进程重要级低。因为服务进程比后台activity进程重要级高,因此一个要进行耗时工作的activity最好启动一个service来做这个工作,而不是开启一个子进程――特别是这个操作需要的时间比activity存在的时间还要长的时候。例如,在后台播放音乐,向网上上传摄像头拍到的图片,使用service可以使进程最少获取到“服务进程”级别的重要级,而不用考虑activity目前是什么状态。broadcast receivers做费时的工作的时候,也应该启用一个服务而不是开一个线程。
2. 单线程模型
当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。
2.1 子线程更新UI
Android的UI是单线程(Single-threaded)的。为了避免拖住GUI,一些较费时的对象应该交给独立的线程去执行。如果幕后的线程来执行UI对象,Android就会发出错误讯息
CalledFromWrongThreadException。以后遇到这样的异常抛出时就要知道怎么回事了!
2.2 Message Queue
在单线程模型下,为了解决类似的问题,Android设计了一个Message Queue(消息队列), 线程间可以通过该Message Queue并结合Handler和Looper组件进行信息交换。下面将对它们进行分别介绍:
1. Message
Message消息,理解为线程间交流的信息,处理数据后台线程需要更新UI,则发送Message内含一些数据给UI线程。
2. Handler
Handler处理者,是Message的主要处理者,负责Message的发送,Message内容的执行处理。后台线程就是通过传进来的 Handler对象引用来sendMessage(Message)。而使用Handler,需要implement 该类的 handleMessage(Message)
方法,它是处理这些Message的操作内容,例如Update UI。通常需要子类化Handler来实现handleMessage方法。
3. Message Queue
Message Queue消息队列,用来存放通过Handler发布的消息,按照先进先出执行。
每个message queue都会有一个对应的Handler。Handler会向message queue通过两种方法发送消息:sendMessage或post。这两种消息都会插在message queue队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过sendMessage发送的是一个message对象,会被 Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。
4. Looper
Looper是每条线程里的Message Queue的管家。Android没有Global的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper() 得到当前线程的Looper就有可能为NULL。
对于子线程使用Looper,API Doc提供了正确的使用方法:
这个Message机制的大概流程:
1. 在Looper.loop()方法运行开始后,循环地按照接收顺序取出Message Queue里面的非NULL的Message。
2. 一开始Message Queue里面的Message都是NULL的。当Handler.sendMessage(Message)到Message Queue,该函数里面设置了那个Message对象的target属性是当前的Handler对象。随后Looper取出了那个Message,则调用该Message的target指向的Hander的dispatchMessage函数对Message进行处理。
在dispatchMessage方法里,如何处理Message则由用户指定,三个判断,优先级从高到低:
1) Message里面的Callback,一个实现了Runnable接口的对象,其中run函数做处理工作;
2) Handler里面的mCallback指向的一个实现了Callback接口的对象,由其handleMessage进行处理;
3) 处理消息Handler对象对应的类继承并实现了其中handleMessage函数,通过这个实现的handleMessage函数处理消息。
由此可见,我们实现的handleMessage方法是优先级最低的!
3. Handler处理完该Message (update UI) 后,Looper则设置该Message为NULL,以便回收!
在网上有很多文章讲述主线程和其他子线程如何交互,传送信息,最终谁来执行处理信息之类的,个人理解是最简单的方法判断Handler对象里面的 Looper对象是属于哪条线程的,则由该线程来执行!
1. 当Handler对象的构造函数的参数为空,则为当前所在线程的Looper;
2. Looper.getMainLooper()得到的是主线程的Looper对象,Looper.myLooper()得到的是当前线程的Looper对象。
现在来看一个例子,模拟从网络获取数据,加载到ListView的过程:
这个例子,我自己写完后觉得还是有点乱,要稍微整理才能看明白线程间交互的过程以及数据的前后变化。随后了解到AsyncTask类,相应修改后就很容易明白了!
AIDL(AndRoid接口描述语言)是一种借口描述语言; 编译器可以通过aidl文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程的目的. 如果需要在一个Activity中, 访问另一个Service中的某个对象, 需要先将对象转化成AIDL可识别的参数(可能是多个参数), 然后使用AIDL来传递这些参数, 在消息的接收端, 使用这些参数组装成自己需要的对象.
AIDL的IPC的机制和COM或CORBA类似, 是基于接口的,但它是轻量级的。它使用代理类在客户端和实现层间传递值. 如果要使用AIDL, 需要完成2件事情: 1. 引入AIDL的相关类.; 2. 调用aidl产生的class.
AIDL的创建方法:AIDL语法很简单,可以用来声明一个带一个或多个方法的接口,也可以传递参数和返回值。由于远程调用的需要, 这些参数和返回值并不是任何类型.下面是些AIDL支持的数据类型:
1. 不需要import声明的简单Java编程语言类型(int,boolean等)
2. String, CharSequence不需要特殊声明
3. List, Map和Parcelables类型, 这些类型内所包含的数据成员也只能是简单数据类型, String等其他比支持的类型.
相关推荐
### 2024年Java面试题之Spring Cloud面试题详解 #### 一、什么是微服务架构? 微服务架构是一种设计方法论,旨在将大型单体应用分解为一系列小型、可独立部署的服务单元(即微服务)。每个微服务专注于执行单一...
### Java现场面试题详解:亚信SSH\Oracle面试题 #### Struts1.0的运行机制与架构原理 Struts框架作为MVC(Model-View-Controller)设计模式的经典实现之一,极大地方便了Java Web应用的开发。在传统的JSP-Servlet-...
### Hibernate面试题详解 #### 一、主键生成方式中的`native`与`identity`区别 **1.1 native方式** - **定义**: `native`是一种动态选择标识符生成策略的方式,它根据所使用的底层数据库系统自动选择最适合的生成...
### JSP面试题详解 #### 1. HTTP 协议的无状态特性及有状态协议的概念 HTTP(HyperText Transfer Protocol)是一种应用层协议,用于从Web服务器传输超文本到本地浏览器的传输协议。它是一种**无状态协议**,这意味...
这份"Android面试题总结-史上最全.pdf"是针对Android开发者准备面试的一份详尽参考资料,涵盖了从基础到高级的众多知识点,旨在帮助求职者在面对大厂面试时能充分展现自己的技能和理解。以下是一些主要的知识点详解...
### iOS面试题-OC部分详解 #### 1. Objective-C是否支持多继承?能否实现多个接口?Category是什么?重写类的方式是用继承还是用分类? - **Objective-C多继承:**Objective-C不直接支持多继承,即一个类不能直接...
### 2024年Java面试题之设计模式面试题详解 #### 一、设计模式概述 设计模式是在软件工程领域被广泛接受的一种编码实践,它代表了一套反复使用、广为人知的代码设计经验和解决方案的集合。设计模式旨在帮助开发者...
### Java面试题知识点详解 #### 一、面向对象与面向过程的区别 1. **面向过程**: - **优点**:性能较高。因为无需实例化类,减少了内存占用和CPU开销,尤其适合对性能要求极高的场景,如单片机、嵌入式开发、...
### 2024年Java面试题:JVM之Java虚拟机面试题 #### Java内存模型及基础知识 **1. Java代码如何被计算机识别** Java程序最初由开发人员使用Java语法编写,这些代码人类可读但计算机无法直接理解。为了使计算机...
### Java面试题详解 #### 1. 异常处理机制的简单原理和应用 在Java中,异常处理机制主要用于处理程序运行过程中可能出现的各种错误。当程序违反了Java的语义规则时,Java虚拟机会捕获这些错误并将它们表示为异常。...
### Java私塾面试题解析 —— 线程与EJB部分 #### 一、线程编程基础 **1. 实现线程的两种方法及其优缺点** Java中实现线程主要有两种方式: - **继承`Thread`类**:创建一个新的类继承自`Thread`类,并重写`run...
根据提供的文件信息,以下是从标题、描述、以及部分给出的内容中提炼出的关于Vue.js的知识点详解: ### 1. Vue.js的优点 - **轻量级框架**:Vue.js仅专注于视图层,是一个构建数据驱动视图的集合,其核心库体积...
以上就是Java面试题基础方面的部分知识点详解,这些知识涵盖了Java语言的基础语法、面向对象特性、集合框架、异常处理等多个方面,是成为一名合格Java开发者所必需掌握的基础。在面试中,深入理解和熟练运用这些知识...
以下是一些Vue面试中常见的知识点详解: 1. **Vue的优点**: - **轻量级**:Vue的核心库专注于视图层,体积小巧,有利于快速加载和优化。 - **双向数据绑定**:Vue采用数据绑定使得数据和视图之间的同步变得简单...
在前端开发领域,面试题往往涵盖了广泛的理论和技术知识点,旨在测试应聘者的基础技能、实际操作能力以及对行业趋势的理解。以下是对“前端大厂最新面试题-kuangshi.docx”中涉及的一些关键点的详细解析: 1. **...
### 百度Android开发面试题解析 #### 1. Android DVM、Linux 进程与应用程序进程的关系 在Android环境中,DVM(Dalvik虚拟机)是为Android平台设计的虚拟机,每个Android应用都在其自己的进程中运行,并拥有独立的...
【Android面试知识点详解】 ...这些面试题涵盖了Android开发基础,如UI设计、性能优化、组件使用、异常处理以及系统架构等方面,是衡量开发者基础技能的重要标准。理解并掌握这些知识点对于Android开发者来说至关重要。
React中的Refs(引用)是一种机制,它允许开发者在React组件中直接访问DOM节点或React组件实例。这在处理特定交互、动画或者与第三方库集成时非常有用。在React中,有三种主要的方式来创建和使用Refs: 1. **字符串...
### 百度Android面试题深度解析 #### 1. Android DVM、Linux进程与应用程序进程的...总之,深入理解上述知识点不仅有助于应对百度的Android面试题,更能提升开发者对Android框架的全面认知,促进高质量应用的开发。
### Java面试题详解 #### 一、Java 是值传递还是引用传递? 在Java中,无论是基本数据类型还是对象,参数传递本质上都是值传递。这里的关键在于理解“值”的含义。 - **基本数据类型**: 当传递基本数据类型的参数...