一、Intent 和PendingIntent区别
1、Intent:及时启动的意图,Intent一般是用作Activity、Sercvice、BroadcastReceiver之间传递数据
例如:启动activity:startActivity(intent); 启动服务:startService(intent)\bindService(intent); 发送广播:sendBroadcast(intent)。相应的activity 、service、广播接收器只要设置响应的过滤器里的action就可以接收到。
2、PendingIntent:延时的意图,可以理解为延迟执行的intent,PendingIntent是对Intent一个包装。主要使用在:通知Notificatio的发送,短消息SmsManager的发送和警报器AlarmManager的执行 。
要得到一个pendingIntent对象,使用方法类的静态方法getActivity(intent)、getBroadcast(intent)、getService(intent)分别对应着打开一个activity、发送一个广播、启动一个服务。
二、NotificationManager 通知管理器
实现流程:
1、创建通知:Notification notification = new Notification(icon,title,when);参数为通知的图标、标题(当通知发出时在状态栏显示的内容)和什么时候发出,立刻发出的话为System.currentTimeMillis()
2、为通知对象notification添加一个意图intent,当点击这个通知时,会根据这个意图打开一个activity
Intent intent = new Intent(action);//创建一个意图
//因为是要延时显示,所以要把这个意图包裹成延时意图。意图要发送给activity就用PendingIntent.getActivity来包装,要是发送给广播接收器则用getBroadcast来包装。
PendingIntent mPI= PendingIntent.getBroadcast(this,0,intent,0);
3、绑定延时意图PendingIntent以及设置在通知栏列表的显示信息。
notification.setLastestEnventInfo(this,title,content,mPI);
4、通过系统的通知管理器发送通知:
NotificationManager mNF = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
mNF.notify(id,notification);
总结:获取系统通知管理器-》创建通知-》设置通知将要调用的activity-》发送通知
三、activity生命周期
onCreat():创建,初始化成员变量等:不可见、不可操作。
onStart():启动,可见但不可操作。
onResume():复活、活动,可见可操作。在系统的activity栈的最上层
onPause():暂停,可见但不可操作,有别的activity挡住,只部分可见。
onStop():停止,不可见不可操作,被别的activity全部遮挡。当前面的activity关闭后,进入onRestart()状态,并进入onStart()->onResume()恢复活动状态。
onDestroy():销毁。
注意:状态成对出现。
onCreat()和onDestroy():经过了摧毁的activity,在激活需要重新创建。
onStart()和onStop():经过停止的activity,在激活需要重新启动。
onResume()和onPause():经过暂停的activity,在激活需要重新恢复Resume。
四、service生命周期及两种启动方式的区别
service同activity的生命周期类似,但由于service没有界面,所以就没有onResume()、onPause()、onStop()等同界面有关的状态
1、通过startService()方法启动的服务:
若是第一次:onCreat()->onStart() ->service running服务运行状态-->onDestory();当第二次时,则不需要重新创建。
通过startService()方法启动的服务于调用者没有关系,即使调用者关闭了,服务仍然运行,想要关闭服务要调用stopService(),此时系统会调用onDestory(),使用此方法启动时,服务首次启动系统先调用服务的onCreate()-->onStart(),如果服务已经启动再次调用只会触发onStart()方法。
2、使用bindService()启动的服务:
onCreat()->onBind() ->service被绑定运行状态->onUnbind()-->onDestory();
使用bindService()启动的服务与调用者绑定,只要调用者全部关闭服务就终止,使用此方法启动时,服务首次启动系统先调用服务的onCreate()-->onBind(),如果服务已经启动再次调用不会再触发这2个方法。当所有的调用者都关闭或调用unbindService(),则服务由系统依次调用onUnbind()-->onDestory(); 服务有系统关闭。
区别:
1、应用场景不同:startService()一般在一个应用中调用,bindService()一般在进程间调用。
2、调用者同服务的关系不同:startService()服务一旦启动同调用者没有关系,即使调用者关闭,服务照常运行,bindService()只要所有的调用者都关闭,系统会把服务销毁。
3、关闭方式不同:startService():要关闭服务,只要有一个调用stopService(),就可以关闭,bindService()只有所有的调用者都关闭或都调用unbindSercice()才可关闭。
五、广播的实现
1、创建广播接收器
public class MyBroadcastReceiver extends BroadcastReceiver
{ public void onReceive(...) { } }
2、实现自己的onReceive方法:在此对接收到的广播进行处理
3、在应用中动态注册广播接收器:
先new出一个广播接收器对象:MyBroadcastReceiver myReceiver = new MyBroadcastReceiver();
然后设置这个广播接收器对象要过滤的action: IntentFilter intentfilter = new IntentFilter(); intentfilter.addAction(broadcastaction);
注册这个广播接收器:this.registerReceiver(myReceiver,intentfilter);
也可以在manifest.xml文件中静态注册:<receiver><intent-filter><action>,据说动态注册的优先级更高。
4、发送广播
既然是通信必须先创建一个意图:Intent intent = new Intent(roadcastaction);
发送广播:sendBroadcast(intent);
5、注销广播接收器:
在程序不用或退出的时候要注销:this.unregisterReceiver(myReceiver)
说明:如果通过<receiver>标签来注册的BroadcastReceiver,那么该对象的实例在onReceive被调用之后就会在任意时间内被销毁。也就是说,我们并需要担心静态注册的BroadcastReceiver的销毁问题。
六、横竖屏切换时候Activity的生命周期
几种情况:
1、禁止横竖屏切换:portrait人物、竖屏;landscape风景、横屏
<activity android:screenOrientation = "portrait">
2、不设置configChanges参数(手机状态改变参数):切屏时会重新调用各个生命周期,横切竖时执行一次,竖切横时执行两次
3、在manifest文件中设置了<activity android:configChanges="orientation">
横竖屏切换都执行一次
开始生命周期:onCreat->onStart->onResume
切换生命周期:onSaveInstanceState->onPause->onStop->onDestory->onCreat->onStart->onRestoreInstanceState->onResume
结束生命周期:onPause->onStop->onDestory
4、在manifest文件中设置了<activity android:configChanges="orientation|keyboardHidden">
切屏时不会重新调用各个生命周期,只会调用onConfigurationChanged方法
另外,要注意版本问题,据说在4.0版本要加个screenSize参数才能不重新调用各个生命周期。
总结一下整个Activity的生命周期 :
补充一点,当前Activity产生事件弹出Toast和AlertDialog的时候Activity的生命周期不会有改变
Activity运行时按下HOME键(跟被完全覆盖是一样的):onSaveInstanceState --> onPause --> onStop onRestart -->onStart--->onResume
Activity未被完全覆盖只是失去焦点:onPause--->onResume
七、android 中MVC的理解
V(view):视图是负责生成应用程序用户界面的部分,接收用户输入,显示处理结果。
M(model):模型业务逻辑层,数据库访问、计算等业务逻辑在该层实现。
C(control):控制层,根据用户的输入和业务处理结果控制界面显示,同时把用户的输入传递给模型层处理。
1、视图层(view):一般采用XML布局文件进行界面描述。通过控制层的activity加载显示界面。
2、控制层(control):activity是控制层,不要在acitivity中写业务逻辑代码,要通过activity交割model业务逻辑层处理,这样做的另外一个原因是android中的acitivity的响应时间是5s,如果耗时的操作放在这里,程序就很容易被回收掉。还记不记得ANR,就是基于这个原因
3、模型层(model):数据库操作、网络连接、业务逻辑等都在此处理。一般通过子线程或服务(service)实现.
八、application类的作用
android系统会为每个启动的应用创建一个application类对象用来存放信息,如PID等,通常不需要我们自己创建,由系统自动帮我们创建。在manifest文件中会看到application,其他的组件如activity、service、BroadcastReceiver都在其内。如果我们想自己创建,需要如下几个步骤:
1、继承Application类
2、实现其onCreate()、onTerminate()方法进行初始化和销毁工作。
3、由于是单例模式,一定要定义个静态类变量:static MyApplication mApp;同时在onCreate()初始化中把类对象赋值:mApp=this;
4、要在manifest文件中注册:只要在application标签中添加android:name = MyApplication这个属性就可以
定义自己的application类的目的:
1、数据共享:application是全局对象,数据成员可以全局共享。例如百度地图应用,在application中定义一个百度地图管理器,在应用中所有的应用activity都可以调用。
2、数据传输:进程内组件之间传递数据,如activity同activity,通过intent.putExtra(),但这个只能传递基本数据类型如int string,要传递复杂的对象类型就麻烦了。可以通过在Application中创建一个HashMap<String,Object> ,以字符串为索引,Object为value这样我们的HashMap就可以存储任何类型的对象了。在Activity A中把需要传递的对象放入这个HashMap,然后通过Intent或者其它途经再把这个索引的字符串传递给Activity
B ,Activity B 就可以根据这个字符串在HashMap中取出这个对象了。只要再向下转个型 ,就实现了对象的传递。
3、数据缓存:比如有一个Activity需要从网站获取一些数据,获取完之后我们就可以把这个数据cache到Application 当中,当页面设置到其它Activity再回来的时候,就可以直接使用缓存好的数据了。但如果需要cache一些大量的数据,最好是cache一些 (软引用)SoftReference ,并把这些数据cache到本地rom上或者sd卡上。如果在application中的缓存不存在,从本地缓存查找,如果本地缓存的数据也不存在再从网络上获取。很多新闻阅读、网页浏览软件就是采用这种策略,如百度新闻,在有网络时加载一些新闻后,没网的时候也可以看。
九、onSaveInstanceState()和onRestoreInstanceState()什么时候被调用
1、调用时机
A、当应用遇到意外情况(如内存不足、按了home键等),需要销毁activity时才被调用,但是当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。
B、另外,当屏幕的方向发生了改变, Activity会被摧毁并且被重新创建。
C、如果你想在Activity被摧毁前缓存一些数据,并且在Activity被重新创建后恢复缓存的数据。可以重写Activity的 onSaveInstanceState() 和 onRestoreInstanceState()方法。
具体的就是在摧毁时在onPause()前调用onSaveInstanceState(),重建时在onStart后调用onRestoreInstanceState()
2、自己实现这个方法的目的
保存应用的一些临时状态,如视频的播放进度,百度地图的当前中心点等,使activity重建后可以恢复到销毁前的状态。
3、实现方法举例
摧毁前:protected void onSaveInstanceState(Bundle saveState){saveState.putInt("state",5)}
可以在onCreate()中提取这个值:onCreate(Bundle saveInstanceState){int state = savaInstanceState.getInt("state")}
也可以在onCreate()-》onStart()后通过onRestoreInstanceState()提取:onRestoreInstanceState(Bundle saveInstanceState){int state = saveInstanceState.getInt("state")}
注意:每个activity的onCreate()函数的参数就是已经保存的键值对Bundle saveInstanceState.
十、view、SurfaceView区别及使用场景
1、view是如何显示视图的
每个继承自View的类,都有一个onDraw()函数,该函数在UI主线程执行,View被实例初始化时,被调用一次,实现视图显示。
视图显示要通过画布(Canvas)展现出来:我们常用的布局文件方式,是把图片绘制到你的layout中的一个View上 ,通过系统标准的View绘制过程处理。另外一种方式是创建自己View类,通过onDraw()函数调用,用Canvas绘制出来
2、View视图的绘图过程:
初始化视图->调用onDraw(Canvas canvas){}:在这个函数里可以在画布上画图片、画文字->在主线程内直接渲染到屏幕上。
画图像: canvas.drawBitmap(...),需要图片对象、图片位置、画笔参数。画笔参数可为空
画文字:canvas.drawText(...),需要文字内容、文字位置、画笔参数,文字的颜色、大小、效果等通过画笔设置。
既然在主线程中绘画就可能造成阻塞,如果在onDraw(),添加一个线程来绘画,又不被android允许,因为android只允许在UI主线程中改变UI界面,绘画完毕后渲染到屏幕的实质就是改变UI界面。
3、SurfaceView视图的绘制过程:
首先确认SurfaceView绘制也是通过Canvas绘图,然后在主UI线程中渲染到屏幕上的。SurfaceView视图有一个成员变量Surface,该成员变量又有一个Canvas对象。绘图的时候是先在后台绘图缓存到Surface里,完成后一次渲染大屏幕上。
工作过程:
获取Surface控制器:SurfaceHolder mHolder = getHolder();
启动绘图线程并通过Surface控制器获取并锁定Surface中的画布Canvas: Canvas canvas = mHolder.lockCanvas();
通过canvas在绘图线程中进行绘图操作。
绘图完成后,解锁画布,并发送Post消息给Surface,让Surface把绘制的并缓存在该Surface中的东西渲染到屏幕上:mHolder.unlockCanvasAndPost(canvas);
4、通过它们的绘图原理可以看到区别:
View:在UI的主线程中更新画面,如果更新画面时间较长或过频繁,有可能不能来及接收系统按键、触屏等事件,会引起阻塞,那么就会出现ANR错误,还记不记得!
SurfaceView:在新的子线程中更新画面操作,但不渲染到屏幕,只是缓存到Surface里,完成后再渲染到屏幕,这不会阻塞主线程。但是应用复杂而且涉及到同步。
5、应用场景:由于各有优缺点,所以在不同的场景,要用不同的方式
被动更新或更新画面时间短:用View,如通过用户操作来更新画面,可以在用户操作事件中调用视图的更新函数:view.invalidate()进而调用View的onDraw()函数实现。
主动更新或更新画面时间长:用SurfaceView,比如游戏由代码控制的频繁刷新界面,则用此比较合适。