本文共 14207 字,大约阅读时间需要 47 分钟。
1, 谈谈你对Activity的理解?自己的理解:Activity就是一个应用程序的门面,也可以理解成就是WEB程序中的一个页面,当然web程序中的页面不同的是,web的一个页面可能只是一个纯粹的展示页面,不与用户进行任何交互,而几乎所有的Activity都会与用户交互。当然两者在架构上也有本质区别,Activity与用户的交互通过触发UI的不同事件完成的。而Web程序是通过请求,响应完成交互的。还有在Android中颠覆了很多常规想法,比如在一个Activity中可以打开另一个不在同一应用的Activity。这在其他的程序是不可想象的。当然这种设计的出发点也是为了节省系统资源。从View层的角度来看,Activity承载了与用户交互的不同控件。从控制场来看,也就是内部逻辑,Activity需要保持各个界面的状态,背后会做很多持久化的操作。包括妥善管理生命周期的各个阶段。
文档式回答:[参考别人的博客]
Activity是Android应用的四大组件之一(另外三个是Service、BroadcastReceiver和ContentProvider),它是单独的,作为用户与程序交互的一个载体,几乎所有的Activity都与用户交互。Activity创建了一个窗口,你可以通过setContentView这个方法将需要的UI放置在窗口。任何一个应用程序都可以调用单独的一个Activity。
其次,Activity的继承关系Activity->ContextThemeWrapper->ContextWrapper->Context最后大部分的Activity的子类都需要实现以下两个接口:
onCreate(Bundle savedInstanceState)接口是初始化activity的地方。在这里通常可以调用setContentView(int)设置资源文件中定义的UI,使用findViewById(int)可以获得UI中定义的窗口。onPause()接口是使用者准备离开activity的地方,在这里,任何的修改都应该被提交(通常用于ContentProvider保存数据)。
所有的Activity必须在清单文件中注册一下才可使用。
实际的应用中往往包括多个Activity,不同的Activity向用户呈现不同的操作界面。Android应用的多个Activity组成Activity栈,当前活动的Activity位于栈顶。Activity和Servlet有点类似,主要负责与用户交互,并向用户呈现应用状态。
onCreate() onStart() onRestart() onResume() onStop() onDestroy()
1)如果我们运行一个MainActivity程序,依次调用onCreate->onStart->onResume,这时MainActivity在栈顶,与我们交互的Activity都在栈顶。然后我们按下返回键,则会一次调用onPause->onStop->onDestroy。这是一个完整的生命周期。【动态图】
2)可见生命周期,从onStart()开始到onStop()结束。其实就是一个Activity被另一个Activity完全覆盖掉,然后又重新回到前台这一个过程称之为可见生命周期。
首先打开OtherAcitivity这个窗口。此时,MainActivity将会被覆盖掉。则会依次调用onPause->onStop。在内存不足的时候,系统也会杀死MainActivity进程。
然后,按下返回键,MainActivity又回到前台此时会调用onRestart->onStart->onResume。【动态gif图】
3)前台的生命周期,从onResume()开始到onPause()结束。在这段时间里,该Activity处于所有Activity的最前面,和用户进行交互。Activity可以经常性地在onResume和onPause状态之间切换。也就是一个Activity覆盖到另一个Activity上面,但是并没有完全覆盖掉。
首先,我们将OtherActivity打开,这是OtherActivity以对话框模式打开,悬浮在MainActivity上面。直接调用的onPause()。
然后,我们按下返回键,则调用onResume。对于这种没用完全覆盖的状态只会在onPause和onResume两个方法之间切换【gif动态图】。
3, Acitvity启动模式? 到底想问什么?自身理解:Activity启动其他Activity:有两种方法。 1)startActivity(Intent intent):启动其他Activity。2)startActivityForResult(Intent intent, int requestCode):以指定请求码(requestCode)启动Activity,而且程序将会等到新启动Activity的结果(通过重写onActivityResult(...)方法获取)。
如果是问Activity启动提供了四种启动模式。
launchMode:
standard:每次启动新的活动窗口(new操作)
singleTop:如果在栈顶是目标活动,则直接打开。否则开启新的活动窗口(new)。
singleTask和singleInstance基本上相同。差别在于若根活动设置singleTask时,则由此开启的活动也在同一任务中,即taskId相同。而若根活动设置为singleInstance时,由此开启的活动能够在新的任务中。即栈中只有一个活动,taskId不同。其余情况相同。
4, 在onCreate方法中Bundle savedInstanceState 这个参数有什么作用?、
在onCreate方法中有saveInstanceState这个参数,其实这个参数对应两个方法。
void onSaveInstanceState(Bundle outState):void onRestoreInstanceState(Bundle savedInstanceState)。当某个Activity变得“容易”被系统销毁时,将调用这个方法。需要注意的是它是系统调用的,并且你的Activity是被动地被销毁。你可以在销毁的时候保存一下数据。然后在onCreate方法中拿出来。那在什么情况下触发这两个方法呢?
1)当用户按下HOME键时。
系统不知道你按下HOME键后要运行多少其他的程序,自然也不知道Activity A是否会被销毁,故系统会调用onSavedInstanceState,让用户有机会保存某些非永久性的数据。
2)长按HOME键,选择运行其他的程序时。
3)按下电源案件(关闭屏幕显示)时。
4)从Activity A启动一个新的Activity时。
5)屏幕方面切换时,例如从竖屏切换到横屏时。
在屏幕切换之前,系统会销毁Activity A,在屏幕切换之后系统又会自动地创建Activity A,所以onSavedInstanceState一定会被执行。
总而言之,onSavedInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的Activity,则onSaveInstanceState会被系统调用,这是系统的责任。因为它必须要提供一个机会让你保存你的数据。
至于onRestoreInstanceState方法,需要注意的是,onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成对地被调用的,onRestoreInstanceState被调用的前提是,Activity A 确实被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,这个方法不会被调用,例如,当正在显示Activity A的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到Activity A,这种情况下Activity A一般不会因为内存的原因被系统销毁,故Activity A的onRestoreInstanceState方法不会被执行。
另外,onRestoreInstanceState的bundle参数也会传递到onCreate方法中,你也可以选择onCreate方法做数据还原。
5, 谈谈你对BroadCastRceiver的理解?
广播接受者,Android四大组件之一,也是唯一一个能够动态注册的组件。
1)广播接受者是一个专注与接收广播通知信息,并做出对应处理的组件。很多广播是源自于系统--比如,通知时区改变、电池电量低、拍摄了一张照片或者用户改变了语言选项。应用程序也可以进行广播--比如说,通知其他应用程序一些数据下载完成并处于可用状态。
2)应用程序可以拥有任意数量的广播接受者以及对所有它感兴趣的通知信息予以响应。所有的接收器均继承自BroadcastReceiver基类。
3)广播接受者没有用户界面。然而,它们可以启动一个Activity来响应它们收到的信息,或者用NotificationManager来通知用户。通知可以用很多方式来吸引用户的注意力--闪动背灯、震动、播放声音等等。一般来说,是在状态栏上放一个持久的图标,用户可以打开它并获取消息。
6, 广播分几种?他们有什么区别?
广播分为两种不同的类型:“普通广播(Normal broadcasts)”和“有序广播(Ordered broadcasts)”。普通广播是完全异步的,可以在同一时刻(逻辑上)被所有者接收到,消息传递的效率比较高,但缺点是,接受者不能将处理结果传递给下一个接受者,并且无法终止广播Intent传播。
然而,有序广播是按照接受者声明的优先级别,被接受者依次接收广播。如:A的级别高于B,B的级别高于C,那么,广播先传给A,再传给B,最后给C。优先级别声明在intent-filter元素的android:priority属性中,数越大优先级别越高,取值范围-1000-1000,优先级别也可以调用IntentFilter对象的setPriority()进行设置。有序广播的接收者可以终止广播Intent的传播,广播Intent的传播一旦被终止,后面的接收者无法接收到广播。
另外,有序广播的接收者可以将数据传递给下一个接收者,如,A得到广播后,可以往它的结果对象中存入数据,当广播传给B时,B可以从A的结果对象中得到A存入的数据。
Context.sendBroadcast()发送的是普通广播,所有订阅者都有机会获得并进行处理。
Context.sedOrderedBroadcast()发送的是有序广播,系统会根据接受者声明的优先级别按顺序逐个执行接收者,前面的接收者有权终止广播(BroadcastReceiver.abortBroadcast()),如果广播将签名的接收者终止,后面的接收者就再也无法获取到广播。对于有序广播,前面的接收者可以将数据通过setResultExtra(Bundle)方法存放进结果对象,然后传给下一个接收者,下一个接收者通过代码:Bundle bundle = getResultExtras(true)可以获取上一个接收者存入在结果对象中的数据。
7, 广播的生命周期?
广播的生命周期非常端,当发送之后intent会到AndroidManifest.xml方法中找是不是匹配的action,如果有就调用Receiver,然后获得Receiver对象,再执行onReceiver方法,这时候Receiver对象就没有用了,当我们再次点击按钮的时候就会重新获得对象,这就是BroadcastReceiver的生命周期。
在BroadcastReceiver里不能做一些比较耗时的操作,否则会弹出ANR(Application No Response)的对话框。
如果需要完成一项耗时的工作,应该通过发送Intent给Service,由Service来完成。这里不能使用子线程来解决,因为BroadcastReceiver的生命周期很短,子线程可能还没有结束,BroadcastReceiver就先结束了。BroadcastReceiver一旦结束,此时BroadcastReceiver的所在线程很容易在系统需要内存时被优先杀死,因为它属于空进程(没有任何活动组件的进程)。如果它的宿主进程被杀死,那么正在工作的子线程也会被杀死,所以采用子线程来解决是不可靠的。
8, 两种注册BroadcastReceiver的方法?
使用在AndroidManifest中注册的方法注册BroadcastReceiver,即使你的应用程序没有启动,或者已经被关闭,这个BroadcastReceiver依然会继续运行,这样的运行机制可能会给软件的用户造成困扰。所以作为程序的开发者,我们希望能够有一种灵活的机制完成BroadcastReceiver的绑定和解除绑定操作。Android当然也考虑到了这些问题,所以在Context这个类当中提供了如下两个函数可以在代码中注册:
1)registerReceiver(receiver,filter)
这个函数的作用就是将一个BroadcastReceiver注册到应用程序当中,这个函数接收两个参数,第一个参数是需要注册的BroadcastReceiver对象,第二个是IntentFilter。第一个参数是需要注册的BroadcastReceiver对象,第二个是一个IntentFilter。第一个参数是非常容易理解的,第二个参数的作用是定义了哪些Intent才能触发这个注册的BroadcastReceiver对象。
2)unregisterReceiver(receiver)
这个方法非常简单,用于解除BroadcastReceiver的绑定状态。一旦解除完成,响应的BroadcastReceiver就不会再接收所广播的Intent了。
两种注册BroadcastReceiver的方法
1.在应用程序的代码中进行注册
注册BroadcastReceiver
registerReceiver(receiver,filter);
取消注册BroadcastReceiver
unregisterReceiver(receiver);
2.在AndroidManifest.xml当中进行注册
<receiver android:name="SMSReceiver">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
两种注册BroadcastReceiver方法的比较:
现在我们了解了两种注册BroadcastReceiver的方法之后,需要考虑一下这两种方法适用的场合:
i. 第一种注册的方法可以保证在应用程序安装之后,BroadcastReceiver始终处于活动状态,通常用于监听系统状态的改变,比如说手机的电量,wifi网卡的状态(当然,监视这些东西也是取决于软件的需求)。对于这样的BroadcastReceiver,通常是在产生某个特定的系统事件之后,进行相应的操作,比如说wifi网卡打开时,给用户一个提示;
ii. 第二种注册方法相对第一种要灵活的多,这样注册的BroadcastReceiver通常用于更新UI的状态。一般来说,都是在一个Activity启动的时候使用这样的方法注册BroadcastReceiver,一旦接收到广播的事件,就可以在onReceive方法当中更新当前的这个Activity当中的控件。但是需要注意的是如果这个Activity不可见了,就应该调用unregisterReceiver方法来解除注册。
9, 粘性广播有什么作用?怎么使用?
粘性广播主要为了解决在发送完广播后,动态注册的接收者,也能够接收到广播。如,先发送一个广播,我的接收者是通过程序中的某个按钮动态注册的。如果不是粘性广播,我注册完接收者肯定无法收到广播了。这是通过发送粘性广播就能够在我的动态注册接收者后也能收到广播。
用法:
//发送粘性广播
Public void sendStickyBroadCast(){
Intent intent=new Intent();
intent.setAction(“com.iteye.myreceiver.action”);
intent.putExtra(“name”,”tom”);
this.sendStickyBroadCast(intent);
}
发送粘性广播还需要发送粘性广播的权限:<uses-permission android:name="android.permission.BROADCAST_STICKY" />
//动态注册广播接收者
MyReceiver mr = new MyReceiver ();
IntentFilter filter = new IntentFilter();
filter.addAction("com.iteye.myreceiver.action ");
filter.addCategory(Intent.CATEGORY_DEFAULT);
Intent ii = this.registerReceiver(mr, filter);
String n = ii.getStringExtra("name");
Toast.makeText(this, "动态注册接收者完成,收到粘性广播,name=" + n, 1).show();
10,如何控制接收者或者发送方得权限?
1)如果广播发送方要求接收方必须有某个权限才能收到广播怎么做呢?
/**
* 发送广播,指定接收者权限
* sendBroadcast(i, "com.iteye.permission.receiver");//指定接收者权限
*/
public void sendBroadcast() {
//隐式意图,发送广播
Intent i = new Intent();
i.setAction("com.iteye.receiver.action");
i.putExtra("name", "tom");
this.sendBroadcast(i, " com.iteye.permission.receiver ");
Log.i("Other",".send ok!");
}
在清单文件里receiver必须有这个权限才能收到广播。
首先,需要定义权限:<permission android:name=" com.iteye.permission.receiver " />
然后,声明权限:<uses-permission android:name=" com.iteye.permission.receiver " />
这时接收者就能收到发送的广播。
2)反过来,如果接收者要求发送方必须拥有某个权限,我才接收你的广播怎么办呢?
<!-- 注册广播接收者
android:permission:控制发送方需要具备指定权限,才接收其广播.
-->
<receiver android:name=".MyReceiver" android:permission="com.permission.sender">
<intent-filter android:priority="100">
<action android:name="com.iteye.receiver.action" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
即使过滤器匹配,如果发送方没有相应权限,接收者也不会接收其广播。
Service,服务,幕后工作者。默默为前台提供支持。Android的核心三组件。Android的核心三组件可通过电视台进行比喻,电视播放画面是Activity,用户可以通过遥控器切换不同的电视台可以看作不同Activity之间的切换。电视播放的新闻可以看作是广播发送方,每个观众是接收者。而电视幕后工作者可以看作是Service,从不抛头露面。默默为Activity提供所需要的内容。
说到Service,我们最熟悉莫过于windows中的服务。他们都是在后台长时间运行,接受上层指令,完成相关操作。但android中的service与其他的服务还是有所差别的,service与其他三个组件一样,其进程模型都是可以通过XML进行配置的。调用方和发布方都可以有权利来选择是把这个组件运行在同一个进程下,还是不同的进程下。如果一个Service,是有期望运行在于调用方不同进程的时候,就需要利用Android提供的RPC(Remote Procedure Call Protocol)机制,为其部署一套进程间通信的策略。当然这些策略android已经为你封装好了。你需要了解底层。直接拿来使用即可。RPC其实就是代理模式的一种实现,在调用端和服务端都去生成一个代理类,做一些序列化和反序列化的事情,使得调用端和服务器端都可以像调用一个本地接口一样使用RPC接口。
Android中用来做数据序列化的类是Parcel,封装了序列化的细节,向外提供了足够对象化的访问接口
AIDL (Android Interface Definition Language) ,一种接口定义的语言,服务的RPC接口,可以用AIDL来描述, ADT帮助你自动生成一整套的代理模式需要用到的类,不要要你自己写。下面会给个例子。
Service是一个应用程序组件,没有图形化界面,Service通常用来处理一些耗时比较长的操作。可以使用Service更新ContentProvider,发送Intent以及启动系统的通知等等。Service不是一个线程,不是一个单独的进程。
12,service的生命周期?
在有生命周期的这几个组件中,Service的生命周期最长,其次是Activity,最后是Receiver。
Service启动方式不同的生命周期方式不同,对应如下:
1)以startService()方式启动的服务,它的生命周期如下:
onCreate()->onStartCommand()这时服务已经开启,如果再点击一下启动则只会调用onStartCommand()。这说明service是单例的,只实例化一次。点击停止按钮onDestroy()。销毁。生命周期完成。【动画gif图】
2)以bindService()方式启动服务。调用方式如下:
onCreate()->onBind()此时服务绑定。再次绑定则无响应。接触绑定:onUnbind()->onDestroy()。生命周期完成。
【动画gif图】
13,启动service的两种方法?有什么区别?
一种是startService(),另一种是bindService()。这两者的区别是第一种方式调用者开启了服务,即会与服务失去联系,两者没有关联。即使访问者退出了,服务仍在运行。如需解除服务必须显式的调用stopService方法。主要用于调用者与服务没有交互的情况下,也就是调用者不需要获取服务里的业务方法。比如电话录音。而后者调用者与服务绑定在一起的。当调用者退出的时候,服务也随之退出。用于需要与服务交互。
14,实现进程内与进程间通信是怎么实现的?
在linux中进程通信机制有很多种,比如:消息队列(message queue),socket,共享内存(share mermory)等等。但是在Android中,无论是进程内还是进程间,所采用的通信机制,主要是以Binder为核心。Android之所以选择是Binder作为进程的通信机制,主要是Binder与其他通信方式相比更加快速和简洁,所消耗的内存也是相对较少。当然也有其他方面的原因,比如传统进程通信有可能会增加进程开销,也有安全方面的风险。Binder能解决这些问题。故成为首选的通信方式。
1) 进程内的通信,与进程间的通信相比,相对容易些。进程内通信肯定要采用绑定的方式开启服务。这样才能与服务进行交互。绑定服务的方法:
bindService(Intent service, ServiceConnection conn, int flags)
这个方法的第一个参数很好理解。用于激活哪个服务,第二个参数是关键,服务的连接,这是一个接口,用于接收服务开启或者关闭时返回的数据。实现这个接口要重写两个方法:
onServiceConnected(ComponentName name, IBinder service):一旦客户端与服务端绑定成功,将有这个方法的第二个参数接收服务端返回的IBinder对象。得到服务端的引用就可以在客户端调用服务里的一些业务方法。这也是进程内通信的主要部分。
onServiceDisconnected(ComponentName name):这个方法在服务断开是调用,服务被销毁或者被干掉时调用。附件处有例子。
2) 进程之间的通信,其主要的核心机制同样是Binder。其实Binder底层就是通过Parcel(邮包)来完成数据传输的。进程内通信可以通过IBinder对象来实现业务共享的,但是进程间的通信这种方式就肯定不行了。这时就用到了AIDL。
AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,实现Android设备上的两个进程间通信(IPC).AIDL的IPC机制和EJB所采用的CORBA很类似,进程之间的通信信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象.由于进程之间的通信信息需要双向转换,所以android采用代理类在背后实现了信息的双向转换,代理类由android编译器生成,对开发人员来说是透明的.
使用方式如下:
定义AIDL. //XXXService.aidl,注意扩展名
package com.iteye.androidtoast;
interface XXXService {
void sayHello(in/out/inout String name);//in|out|inout是参数的方向
}
ide会自动在gen包下生成对应的java类,接口文件中生成一个Stub的抽象类,里面包括aidl定义的方法,还包括一些其它辅助方法.值得关注的是asInterface(IBinder iBinder),它返回接口类型的实例,对于远程服务调用,远程服务返回给客户端的对象为代理对象,客户端onServiceConnected(ComponentName name, IBinder service)方法引用该对象时不能直接强转成接口类型的实例,而应该使用asInterface(IBinder iBinder)进行类型转换.
AIDL定义注意事项:
(1)接口名和aidl文件名相同.
(2)接口和方法前不用加访问权限修饰符public,private,protected等,也不能用final,static.
(3)AIDL默认支持的类型包话java基本类型(int,long,boolean等)和(String,List,Map,
CharSequence),使用这些类型时不需要import声明.对于List和Map中的元素类型必须是AIDL支持的类型.如果使用自定义类型作为参数或返回值,自定义类型必须实现Parcelable接口.
(4)自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义的包在同一个包中.
(5)在aidl文件中所有非Java基本类型参数必须加上in、out、inout标记,以指明参数是输入参数、输出参数还是输入输出参数.
(6)Java原始类型默认的标记为in,不能为其它标记.
具体步骤:
1.创建服务器android项目(server)
2.创建服务类.CustomerService
/**
* 服务类
*/
public class CustomerService extends Service {
...
}
3.配置清单注册服务类
<!-- 注册服务 -->
<service android:name=". CustomerService " />
4.定义aidl文件.
ICustomerService.aidl
package com.iteye.androidtoast.aidl;
interface ICustomerService {
String sayHello(String name);
}
5.ide在gen目录下自动生成java接口.
6.需要实现CustomerService.onBind()方法的返回值.
public IBinder onBind(Intent intent) {
return new ICustomerService.Stub() {
public String sayHello(String name) throws RemoteException {
return "hello " + name;
}
};
}
7.创建客户端项目(android项目)
8.复制服务端项目aidl文件到客户端(连同包结构一同复制),同样在客户端项目中
也会生成对应的接口类.同步骤(5).
9.绑定服务.
class CustomerConnection implements ServiceConnection{
public void onServiceConnected(ComponentName name, IBinder service) {
is = ICustomerService.Stub.asInterface(service);
}
public void onServiceDisconnected(ComponentName name) {
}
}
10.绑定服务
Intent i = new Intent();
i.setAction(“”)
this.bindService(i, conn, BIND_AUTO_CREATE);
11,调用远程服务方法