android基本组件
Activity
一个activity是一个显示界面,可以显示并处理用户的事件。
activity之间通过intent进行通信
每个activity需要在AndroidManifest.xml中声明
Service
一个无界面的后台进程。
startService()启动的服务,生命周期与其调用者无关,只要没人杀则永生。
bindService()启动的服务,生命周期与调用者相关。“不求同年同月同日生,但求同年同月同日死”。
BroadcastReceiver
接收广播消息,可以对事件进行过滤。
通过收发广播,进行系统与app、app与app之间的通信。broadcast发送的是intent
ContentProvider
用于保存和获取数据,一般用于多个应用程序之间共享数据。
用于将程序的数据库人为地暴露出来!实现一个程序可以对另个程序的数据库进行相对用的操作!主要是用来将自己的数据库共享出来供别的程序调用或者修改。
activity生命周期
由ActivityStart启动一个Activity
onCreate()->onStart->onResume ->onPause->onStop->onDestory
| | |___________| | |
| |_______________________________| |
|________________________________________________________|
AndroidManifest.xml资源配置管理文件
每个android应用程序必须有一个AndroidManifest.xml,在app/manifest目录中。它指定了应用程序的java包名,作为应用程序的唯一标识符;构成应用程序的各个组件;权限相关等。
Activity,Service,ContentProvider必须在AndroidManifest.xml中注册,BroadcastRecivier可以在AndroidManifest.xml中静态注册,也可以在代码中动态注册。
android中数据存储的方式
可供选择的存储方式有SharedPreferences、文件存储、SQLite数据库方式、内容提供器(Content provider)和网络。
android中的通信方式
一、intent
主要用于在同一进程的不同组件之间通信。
在Android中有四大组件,这些组件中有三个组件与Intent相关,可见Intent在android整个生态中的地位高度。Intent是信息的载体,用它可以去请求组件做相应的操作。也就是说Intent可以携带数据,传递给Activity/Service/BroadcastReceiver。
1,启动Activity。Activity可以简单的理解为手机屏幕中的一个页面,你可以通过将Intent传入startActivity方法来启动一个Activity的实例,也就是一个页面,同时,Intent也可以携带数据,传递给新的Activity。如果想要获取新建的Activity执行结果,可以通过onActivityResult()方法去启动Activity。
2,启动Service。Service是一个不呈现交互画面的后台执行操作组件,可以通过将Intent传入startService()方法来启动一个Service来启动服务。
3,传递广播BroadCast。广播是任何应用都可以接收到的消息,通过将Intent传递给 sendBroadcast()、sendOrderedBroadcast() 或 sendStickyBroadcast()方法,可以将广播传递接收方。
Intent类型
在Android中,Intent分为两种类型,显式和隐式。
显式Intent,可以通过类名来找到相应的组件,在应用中用显式Intent去启动一个组件,通常是因为我们知道这个组件(Activity或者Service)的名字。如下代码,我们知道具体的Activity的名字,要启动一个新的Activity,下面就是用的显示Intent。
Intent intent = new Intent(context,XXActivity.class);
startActivity(intent);
隐式Intent,不指定具体的组件,但是它会声明将要执行的操作,从而匹配到相应的组件。最简单的Android中调用系统拨号页面准备打电话的操作,就是隐式Intent。
Intent intent = new Intent(Intent.ACTION_DIAL);
Uri data = Uri.parse("tel:" + "135xxxxxxxx");
intent.setData(data);
startActivity(intent);
使用显示Intent去启动Activity或者Service的时候,系统将会立即启动Intent对象中指定的组件。
使用隐式Intent的时候,系统通过将Intent对象中的IntentFilter与组件在AndroidManifest.xml或者代码中动态声明的IntentFilter进行比较,从而找到要启动的相应组件。如果组件的IntentFilter与Intent中的IntentFilter正好匹配,系统就会启动该组件,并把Intent传递给它。如果有多个组件同时匹配到了,系统则会弹出一个选择框,让用户选择使用哪个应用去处理这个Intent,比如有时候点击一个网页链接,会弹出多个应用,让用户选择用哪个浏览器去打开该链接,就是这种情况。
IntentFilter 通常是定义在AndroidManifest.xml文件中,也可以动态设置,通常是用来声明组件想要接受哪种Intent。例如,你如果为一个Activity设置了IntentFilter,你就可以在应用内或者其他应用中,用特定的隐式Intent来启动这个Activity,如果没有为Activity设置IntentFilter,那么你就只能通过显示Intent来启动这个Activity。
注意,为了确保系统的稳定性,官方建议使用显示Intent来启动Service,同时也不要为Service设置IntentFilter,因为如果使用隐式Intent去启动Service,我们并不知道那些服务会响应Intent,而且由于服务大多是不可见的,我们也不知道那些服务被启动了,这是非常危险的。在Android 5.0(API 21)以后,如果使用隐式的Intent去调用bindService()方法,系统会抛出异常。
Intent的属性
Intent作为消息的载体,系统根据它去决定启动哪个具体的组件同时将组件执行中需要的信息传递过去。Intent能够包含的属性有 Component、Action、Data、Category、Extras、Flags 。
Component ,要启动的组件名称。这个属性是可选的,但它是显式Intent的一个重要属性,设置了这个属性后,该Intent只能被传递给由Component定义的组件。隐式Intent是没有该属性的,系统是根据其他的信息(例如,Action、Data等)来判断该Intent应该传递给哪个组件。这个属性是目标的组件的具体名称(完全限定类名),例如,com.example.DemoActivity。该属性可以通过setComonentName()、setClass()、setClassName()或者Intent的构造函数来设置。
Action ,表明执行操作的字符串。它会影响Intent的其余信息,比如Data、Extras。该属性可以通过setAction()方法或者Intent的构造函数来设置。用户可以自定义这个属性,也可以使用系统中已经有的Action值。下面列出启动Activity时候的一些通用Action属性。
ACTION_VIEW ,当有一些信息需要展示出来,可以设置Intent的Action为这个值,并调用startActivity()方法
ACTION_SEND ,当用户有一些信息需要分享到其他应用,可以设置Intent的Action为这个值,并调用startActivity()方法
ACTION_DIAL ,拨打电话,可以设置Intent的Action为这个值,并调用startActivity()方法
ACTION_EDIT ,编辑某些文件,可以设置Intent的Action为这个值,并调用startActivity()方法
Data ,它是待操作数据的引用URI或者数据MIME类型的URI,它的值通常与Intent的Action有关联。比如,如果设置Action的值为ACTION_EDIT,那么Data的值就必须包含被编辑文档的URI。当我们创建Intent的时候,设置MIME类型非常重要。例如,一个可以显示图片的Activity可能不能播放音频,图片和音频的URI非常类似,如果我们设置了MIME类型,可以帮助系统找到最合适的组件接受Intent。有时候,MIME类型也可以从URI判断出来,例如当Data是一个包含content:字符串的URI时候,可以明确的知道,待处理的数据存在设备中,而且由ContentProvider控制。
使用setData()方法设置数据引用的URI,使用setType()方法设置数据的MIME类型,使用setDataAndType()方法同时设置这两个属性。
注意:如果想要设置两个的属性,直接用setDataAndType()方法,不要同时调用setData()和setType()方法,因为这两个方法设置的值会相互覆盖
public Intent setData(Uri data) {
mData = data;
mType = null;
return this;
}public Intent setType(String type) {
mData = null;
mType = type;
return this;
}
Category
,这个属性是对处理该Intent组件信息的补充。它是一个ArraySet类型的容器,所以可以向里面添加任意数量的补充信息,同时,Intent没有设置这个属性不会影响解析组件信息。可以通过addCategory()方法来设置该属性。下面列出一些常用的Category的值。
CATEGORY_BROWSABLE,设置Category为该值后,在网页上点击图片或链接时,系统会考虑将此目标Activity列入可选列表,供用户选择以打开图片或链接。
CATEGORY_LAUNCHER,应用启动的初始Activity,这个Activity会被添加到系统启动launcher当中。
以上列出的这些关于Intent的属性(Component、Action、Data、Category)可以帮助系统来确定具体的组件,但是有一些Intent的属性,不会影响到组件的确定。
Extras ,以key-value键值对的形式来存储组件执行操作过程中需要的额外信息,可以调用putExtra()方法来设置该属性,这个方法接受两个参数,一个是key,一个是value。也可以通过实例化一个储存额外信息的Bundle对象,然后调用putExtras()方法将我们实例化的Bundle添加到Intent中。
Flags ,这个属性可以指示系统如何启动一个Activity,以及启动之后如何处理。例如Activity属于哪一个task(参考Activity的四种启动方式)。
二、bundle
两个activity之间通过bundle传递数据,它保存的数据以键值对的形式存在
三、binder
binder是android对原版linux进行修改时,新增的一种android进程间通信机制。在linux层binder是基于OpenBinder框架的一个驱动,用于提供 Android平台的进程间通信(InterProcess Communication,IPC)功能。源代码位于drivers/android/binder.c。
ps:Linux已经拥有的进程间通信IPC手段包括(Internet Process Connection): 管道(Pipe)、信号(Signal)和跟踪(Trace)、插口(Socket)、报文队列(Message)、共享内存(Share Memory)和信号量(Semaphore)。为何要新增binder这种IPC呢?
1,可靠性:在移动设备上,通常采用基于Client-Server的通信方式来实现互联网与设备间的内部通信。目前linux支持IPC包括传统的管道,System V IPC,即消息队列/共享内存/信号量,以及socket中只有socket支持Client-Server的通信方式。Android系统为开发者提供了丰富进程间通信的功能接口,媒体播放,传感器,无线传输。这些功能都由不同的server来管理。开发都只关心将自己应用程序的client与server的通信建立起来便可以使用这个服务。毫无疑问,如若在底层架设一套协议来实现Client-Server通信,增加了系统的复杂性。在资源有限的手机 上来实现这种复杂的环境,可靠性难以保证。
2,传输性能:socket主要用于跨网络的进程间通信和本机上进程间的通信,但传输效率低,开销大。消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的一块缓存区中,然后从内核缓存区拷贝到接收方缓存区,其过程至少有两次拷贝。虽然共享内存无需拷贝,但控制复杂。比较各种IPC方式的数据拷贝次数。共享内存:0次。Binder:1次。Socket/管道/消息队列:2次。3,安全性:Android是一个开放式的平台,所以确保应用程序安全是很重要的。Android对每一个安装应用都分配了UID/PID,其中进程的UID是可用来鉴别进程身份。传统的只能由用户在数据包里填写UID/PID,这样不可靠,容易被恶意程序利用。而我们要求由内核来添加可靠的UID。 所以,出于可靠性、传输性、安全性。android建立了一套新的进程间通信方式。 ——摘自:Android中的Binder机制的简要理解
基于以上原因,Android需要建立一套新的IPC机制来满足系统对通信方式,传输性能和安全性的要求,这就是Binder。Binder基于Client-Server通信模式,传输过程只需一次拷贝,为发送发添加UID/PID身份,既支持实名Binder也支持匿名
Binder是一个远程对象的基础类,核心部分是远程调用机制,这部分是由IBinder定义的。它是对IBinder类的实现,其中IBinder类提供了这样一个类的标准的本地化实现方式。大多数开发者不会去直接实现Binder类,而是用AIDL工具来描述他们自己想要的接口,借助它来生成一个合适的Binder之类。
四、AIDL(android interfacedefinition language)
binder不仅用于远程调用,也用于进程内调用,AIDL是binder机制用于远程调用时接口定义的一种语言(可用于两个app之间通信)
AIDL属于RPC(Remote procedure call)远程过程调用,即其他应用程序通过本程序提供的接口来获取数据。
五、handler
handler用于同一进程中的线程间通信,底层有linux的管道机制实现。
handler不采用Binder,并非binder完成不了这个功能,而是太浪费CPU和内存资源了。
Binder采用C/S架构,往往用于不同进程间的通信
主线程定义Handler
Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what) {case 0://完成主界面更新,拿到数据String data = (String) msg.obj;textView.setText(data);break;default:break;}}};
子线程发消息,通知Handler完成UI更新
private void getDataFromNet() {new Thread(new Runnable() {@Overridepublic void run() {//耗时操作,完成之后发送消息给Handler,完成UI更新;mHandler.sendEmptyMessage(0);//需要数据传递,用下面方法;Message msg = new Message();msg.obj = "网络数据";//可以是基本类型,可以是对象,可以是List、map等;mHandler.sendMessage(msg);}}).start();}
六、broadcast
通过收发广播,进行系统与app、app与app之间的通信。broadcast发送的是intent
broadcast的用途也很大的,比如一些系统的广播:电量低、开机、锁屏等一些操作都会发送一个广播
广播被分为两种不同的类型:“普通广播(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=".IncomingSMSReceiver">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
七、ContentProvider
用于将程序的数据库人为地暴露出来!实现一个程序可以对另个程序的数据库进行相对用的操作!主要是用来将自己的数据库共享出来供别的程序调用或者修改。这也属于进程间通信的方式。
当我们想允许自己应用程序的数据被其它应用操作时,可以在我们的app中实现ContentProvider类,同时注册一个URI,然后其它应用程序只要使用ContentResolver 根据URI就可以操作我们app中的数据了。数据不一定是数据库,也可能是文件。
URI通用资源标志符(Universal Resource Identifier, 简称"URI")。
URI一般由三部分组成:
在Android平台,URI主要分三个部分:scheme, authority and path。
其中authority又分为host和port。格式如下:scheme://host:port/path
举个实际的例子:
content://com.example.project:200/folder/subfolder/etc
\---------/ \------------------ -/ \--/ \----------------------/
scheme host port path
\---------------------------/
authority
content协议头,这个是规定的,就像http,ftp一样,ContentProvider规定的协议头就是content。
我们很经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher 和ContentUris 。URI用于应用程序内部或之间资源定位。
八、网络通信socket
通过访问远程服务器获取相关信息,也属于广义的进程间通信。