Android 开发重点内容详解.docx
《Android 开发重点内容详解.docx》由会员分享,可在线阅读,更多相关《Android 开发重点内容详解.docx(13页珍藏版)》请在冰豆网上搜索。
![Android 开发重点内容详解.docx](https://file1.bdocx.com/fileroot1/2023-1/25/d34c520d-60f4-46d2-ac16-f6750c378b79/d34c520d-60f4-46d2-ac16-f6750c378b791.gif)
Android开发重点内容详解
一、Android系统的架构
从底层往上层主要包含:
1.LinuxKernel:
Android基于Linux2.6提供核心系统服务,例如:
安全、内存管理、进程管理、网络堆栈、驱动模型。
LinuxKernel也作为硬件和软件之间的抽象层,它隐藏具体硬件细节而为上层提供统一的服务。
2.AndroidRuntime:
Android包含一个核心库的集合,提供大部分在Java编程语言核心类库中可用的功能。
每一个Android应用程序是Dalvik虚拟机中的实例,运行在他们自己的进程中。
Dalvik虚拟机设计成,在一个设备可以高效地运行多个虚拟机。
Dalvik虚拟机可执行文件格式是.dex,dex格式是专为Dalvik设计的一种压缩格式,适合内存和处理器速度有限的系统。
3.Libraries:
Android包含一个C/C++库的集合,供Android系统的各个组件使用。
这些功能通过Android的应用程序框架(applicationframework)暴露给开发者。
主要包含:
系统C库、媒体库、界面管理、LibWebCore、SGL(2D)、3D库、FreeType、SQLite
4.ApplicationFramework:
所有的应用程序其实是一组服务和系统,包括:
视图(View)、内容提供者(ContentProviders)、资源管理器(ResourceManager)、通知管理器(NotificationManager)、活动管理器(ActivityManager)、通讯、定位、消息提醒等。
5.Applications
二、activity的生命周期
Activity有四种本质区别的状态:
1.在屏幕的前台(Activity栈顶),叫做活动状态或者运行状态(activeorrunning)
2.如果一个Activity失去焦点,但是依然可见(一个新的非全屏的Activity或者一个透明的Activity被放置在栈顶),叫做暂停状态(Paused)。
一个暂停状态的Activity依然保持活力(保持所有的状态,成员信息,和窗口管理器保持连接),但是在系统内存极端低下的时候将被杀掉。
3.如果一个Activity被另外的Activity完全覆盖掉,叫做停止状态(Stopped)。
它依然保持所有状态和成员信息,但是它不再可见,所以它的窗口被隐藏,当系统内存需要被用在其他地方的时候,Stopped的Activity将被杀掉。
4.如果一个Activity是Paused或者Stopped状态,系统可以将该Activity从内存中删除,Android系统采用两种方式进行删除,要么要求该Activity结束,要么直接杀掉它的进程。
当该Activity再次显示给用户时,它必须重新开始和重置前面的状态。
三、service的周期
AndroidService生命周期与Activity生命周期是相似的,但是也存在一些细节上也存在着重要的不同:
onCreate和onStart是不同的
通过从客户端调用Context.startService(Intent)方法我们可以启动一个服务。
如果这个服务还没有运行,Android将启动它并且在onCreate方法之后调用它的onStart方法。
如果这个服务已经在运行,那么它的onStart方法将被新的Intent再次调用。
所以对于单个运行的Service它的onStart方法被反复调用是完全可能的并且是很正常的。
onResume、onPause以及onStop是不需要的
回调一个服务通常是没有用户界面的,所以我们也就不需要onPause、onResume或者onStop方法了。
无论何时一个运行中的Service它总是在后台运行。
onBind
如果一个客户端需要持久的连接到一个服务,那么他可以调用Context.bindService方法。
如果这个服务没有运行方法将通过调用onCreate方法去创建这个服务但并不调用onStart方法来启动它。
相反,onBind方法将被客户端的Intent调用,并且它返回一个IBind对象以便客户端稍后可以调用这个服务。
同一服务被客户端同时启动和绑定是很正常的。
onDestroy
与Activity一样,当一个服务被结束是onDestroy方法将会被调用。
当没有客户端启动或绑定到一个服务时Android将终结这个服务。
与很多Activity时的情况一样,当内存很低的时候Android也可能会终结一个服务。
如果这种情况发生,Android也可能在内存够用的时候尝试启动被终止的服务,所以你的服务必须为重启持久保存信息,并且最好在onStart方法内来做。
总结:
1.startService()的目的是回调onStart()方法,onCreate()方法是在Service不存在的时候调用的,如果Service存在(例如之前调用了bindService,那么Service的onCreate方法已经调用了)那么startService()将跳过onCreate()方法。
2.bindService()目的是回调onBind()方法,它的作用是在Service和调用者之间建立一个桥梁,并不负责更多的工作(例如一个Service需要连接服务器的操作),一般使用bindService来绑定到一个现有的Service(即通过StartService启动的服务)。
由于Service的onStart()方法只有在startService()启动Service的情况下才调用,故使用onStart()的时候要注意这点。
四、BroadCastReceiver,注册广播有几种方式,这些方式有何优缺点?
请谈谈Android引入广播机制的用意。
广播(Broadcast)-用于发送广播
广播接收器(BroadcastReceiver)-用于接收广播
Intent-用于连接以上各个组件,并在其间传递消息
BroadcastReceiver
在Android中,Broadcast是一种广泛运用的在应用程序之间传输信息的机制。
而BroadcastReceiver是对发送出来的Broadcast进行过滤接受并响应的一类组件。
下面将详细的阐述如何发送Broadcast和使用BroadcastReceiver过滤接收的过程:
首先在需要发送信息的地方,把要发送的信息和用于过滤的信息(如Action、Category)装入一个Intent对象,然后通过调用Context.sendBroadcast()、sendOrderBroadcast()或sendStickyBroadcast()方法,把Intent对象以广播方式发送出去。
当Intent发送以后,所有已经注册的BroadcastReceiver会检查注册时的IntentFilter是否与发送的Intent相匹配,若匹配则就会调用BroadcastReceiver的onReceive()方法。
所以当我们定义一个BroadcastReceiver的时候,都需要实现onReceive()方法。
注册BroadcastReceiver有两种方式:
一种方式是,静态的在AndroidManifest.xml中用标签生命注册,并在标签内用标签设置过滤器。
另一种方式是,动态的在代码中先定义并设置好一个IntentFilter对象,然后在需要注册的地方调Context.registerReceiver()方法,如果取消时就调用Context.unregisterReceiver()方法。
如果用动态方式注册的BroadcastReceiver的Context对象被销毁时,BroadcastReceiver也就自动取消注册了。
另外,若在使用sendBroadcast()的方法是指定了接收权限,则只有在AndroidManifest.xml中用标签声明了拥有此权限的BroascastReceiver才会有可能接收到发送来的Broadcast。
同样,若在注册BroadcastReceiver时指定了可接收的Broadcast的权限,则只有在包内的AndroidManifest.xml中用标签声明了,拥有此权限的Context对象所发送的Broadcast才能被这个BroadcastReceiver所接收。
动态注册:
IntentFilterintentFilter=newIntentFilter();
intentFilter.addAction(String);--为BroadcastReceiver指定action,使之用于接收同action的广播registerReceiver(BroadcastReceiver,intentFilter);
一般:
在onStart中注册,onStop中取消unregisterReceiver
发送广播消息:
指定广播目标Action:
IntentIntent=newIntent(action-String)
--指定了此action的receiver会接收此广播
需传递参数(可选)putExtra();
发送:
sendBroadcast(Intent);
五、ContentProvider
创建ContentProvider:
要创建我们自己的ContentProvider的话,我们需要遵循以下几步:
1.创建一个继承了ContentProvider父类的类
2.定义一个名为CONTENT_URI,并且是publicstaticfinal的Uri类型的类变量,你必须为其指定一个唯一的字符串值,最好的方案是以类的全名称,如:
publicstaticfinalUriCONTENT_URI=Uri.parse(“content:
//com.google.android.MyContentProvider”);
3.创建你的数据存储系统。
大多数ContentProvider使用Android文件系统或SQLite数据库来保持数据,但是你也可以以任何你想要的方式来存储。
4.定义你要返回给客户端的数据列名。
如果你正在使用Android数据库,则数据列的使用方式就和你以往所熟悉的其他数据库一样。
但是,你必须为其定义一个叫_id的列,它用来表示每条记录的唯一性。
5.如果你要存储字节型数据,比如位图文件等,那保存该数据的数据列其实是一个表示实际保存文件的URI字符串,客户端通过它来读取对应的文件数据,处理这种数据类型的ContentProvider需要实现一个名为_data的字段,_data字段列出了该文件在Android文件系统上的精确路径。
这个字段不仅是供客户端使用,而且也可以供ContentResolver使用。
客户端可以调用ContentResolver.openOutputStream()方法来处理该URI指向的文件资源,如果是ContentResolver本身的话,由于其持有的权限比客户端要高,所以它能直接访问该数据文件。
6.声明publicstaticString型的变量,用于指定要从游标处返回的数据列。
7.查询返回一个Cursor类型的对象。
所有执行写操作的方法如insert(),update()以及delete()都将被监听。
我们可以通过使用ContentResover().notifyChange()方法来通知监听器关于数据更新的信息。
8.在AndroidMenifest.xml中使用标签来设置ContentProvider。
9.如果你要处理的数据类型是一种比较新的类型,你就必须先定义一个新的MIME类型,以供ContentProvider.geType(url)来返回。
MIME类型有两种形式:
一种是为指定的单个记录的,还有一种是为多条记录的。
这里给出一种常用的格式:
vnd.android.cursor.item/vnd.yourcompanyname.contenttype(单个记录的MIME类型)
比如,一个请求列车信息的URI如content:
//com.example.transportationprovider/trains/122可能就会返回typevnd.android.cursor.item/vnd.example.rail这样一个MIME类型。
vnd.android.cursor.dir/vnd.yourcompanyname.contenttype(多个记录的MIME类型)
比如,一个请求所有列车信息的URI如content:
//com.example.transportationprovider/trains可能就会返回vnd.android.cursor.dir/vnd.example.rail这样一个MIME类型。
六、如果后台的Activity由于某原因被系统回收了,如何在被系统回收之前保存当前状态
onSaveInstanceState和onRestoreInstanceState
在activity被杀掉之前调用保存每个实例的状态,以保证该状态可以在onCreate(Bundle)或者onRestoreInstanceState(Bundle)(传入的Bundle参数是由onSaveInstanceState封装好的)中恢复。
这个方法在一个activity被杀死前调用,当该activity在将来某个时刻回来时可以恢复其先前状态。
例如,如果activityB启用后位于activityA的前端,在某个时刻activityA因为系统回收资源的问题要被杀掉,A通过onSaveInstanceState将有机会保存其用户界面状态,使得将来用户返回到activityA时能通过onCreate(Bundle)或者onRestoreInstanceState(Bundle)恢复界面的状态。
不要将这个方法和activity生命周期回调如onPause()或onStop()搞混淆了,onPause()在activtiy被放置到背景或者自行销毁时总会被调用,onStop()在activity被销毁时被调用。
一个会调用onPause()和onStop(),但不触发onSaveInstanceState的例子是当用户从activityB返回到activityA时:
没有必要调用B的onSaveInstanceState(Bundle),此时的B实例永远不会被恢复,因此系统会避免调用它。
一个调用onPause()但不调用onSaveInstanceState的例子是当activityB启动并处在activityA的前端:
如果在B的整个生命周期里A的用户界面状态都没有被破坏的话,系统是不会调用activityA的onSaveInstanceState(Bundle)的。
默认的实现负责了大部分UI实例状态(的保存),采用的方式是调用UI层上每个拥有id的view的onSaveInstanceState(),并且保存当前获得焦点的view的id(所有保存的状态信息都会在默认的onRestoreInstanceState(Bundle)实现中恢复)。
如果你覆写这个方法来保存额外的没有被各个view保存的信息,你可能想要在默认实现过程中调用或者自己保存每个视图的所有状态。
如果被调用,这个方法会在onStop()前被触发,但系统并不保证是否在onPause()之前或者之后触发。
AndroidcallsonSaveInstanceState()beforetheactivitybecomesvulnerabletobeingdestroyedbythesystem,butdoesnotbothercallingitwhentheinstanceisactuallybeingdestroyedbyauseraction(suchaspressingtheBACKkey)
从这句话可以知道,当某个activity变得“容易”被系统销毁时,该activity的onSaveInstanceState就会被执行,除非该activity是被用户主动销毁的,例如当用户按BACK键的时候。
注意上面的双引号,何为“容易”?
言下之意就是该activity还没有被销毁,而仅仅是一种可能性。
这种可能性有哪些?
通过重写一个activity的所有生命周期的onXXX方法,包括onSaveInstanceState和onRestoreInstanceState方法,我们可以清楚地知道当某个activity(假定为activityA)显示在当前task的最上层时,其onSaveInstanceState方法会在什么时候被执行,有这么几种情况:
1、当用户按下HOME键时。
这是显而易见的,系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activityA是否会被销毁,故系统会调用onSaveInstanceState,让用户有机会保存某些非永久性的数据。
以下几种情况的分析都遵循该原则
2、长按HOME键,选择运行其他的程序时。
3、按下电源按键(关闭屏幕显示)时。
4、从activityA中启动一个新的activity时。
5、屏幕方向切换时,例如从竖屏切换到横屏时。
在屏幕切换之前,系统会销毁activityA,在屏幕切换之后系统又会自动地创建activityA,所以onSaveInstanceState一定会被执行
总而言之,onSaveInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据(当然你不保存那就随便你了)。
至于onRestoreInstanceState方法,需要注意的是,onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成对的被调用的,onRestoreInstanceState被调用的前提是,activityA“确实”被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用,例如,当正在显示activityA的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到activityA,这种情况下activityA一般不会因为内存的原因被系统销毁,故activityA的onRestoreInstanceState方法不会被执行
另外,onRestoreInstanceState的bundle参数也会传递到onCreate方法中,你也可以选择在onCreate方法中做数据还原
七、横竖屏切换时候activity的生命周期
1、不设置Activity的android:
configChanges时:
模拟器这种情况切屏会重新调用各个生命周期,切横屏时会生命周期执行一次,切竖屏时生命周期会执行两次,且横竖屏onConfigurationChanged未被调用。
但是在HTCDESIREHD(G10)这款手机上测试结果为:
生命周期都只执行一次,且横竖屏onConfigurationChanged未被调用。
2、设置Activity的android:
configChanges="orientation"时:
模拟器这种情况切屏还是会重新调用各个生命周期,切横屏onConfigurationChanged未执行、竖屏时onConfigurationChanged执行1次。
但是在HTCDESIREHD(G10)这款手机上测试结果为:
生命周期未发生变化,且横竖屏onConfigurationChanged都只执行一次。
3、设置Activity的android:
configChanges="orientation|keyboardHidden"时:
模拟器这种情况切屏不会重新调用各个生命周期,切横屏时onConfigurationChanged会执行1次,切竖屏时onConfigurationChanged方法调用2次。
但是在HTCDESIREHD(G10)这款手机上测试结果为:
横竖屏onConfigurationChanged都只执行一次。
八、android中的动画有哪几类,它们的特点和区别是什么
两种:
一种是Tween动画、还有一种是Frame动画。
Tween动画,这种实现方式可以使视图组件移动、放大、缩小以及产生透明度的变化;
另一种Frame动画,传统的动画方法,通过顺序的播放排列好的图片来实现,类似电影。
九、对多线程的运用和理解,及多线程之间handle的传值;handler机制的原理
Android开发过程中为什么要线程呢?
我们创建的Service、Activity以及Broadcast均是一个主线程处理,这里我们可以理解为UI线程。
但是在操作一些耗时操作时,比如I/O读写的大文件读写,数据库操作以及网络下载需要很长时间,为了不阻塞用户界面,出现ANR的响应提示窗口,这个时候我们可以考虑使用Thread线程来解决。
Android中使用Thread线程会遇到哪些问题?
对于从事过J2ME开发的程序员来说Thread比较简单,直接匿名创建重写run方法,调用start方法执行即可。
或者从Runnable接口继承,但对于Android平台来说UI控件都没有设计成为线程安全类型,所以需要引入一些同步的机制来使其刷新,这点Google在设计Android时倒是参考了下Win32的消息处理机制。
1.对于线程中的刷新一个View为基类的界面,可以使用postInvalidate()方法在线程中来处理,其中还提供了一些重写方法比如postInvalidate(intleft,inttop,intright,intbottom)来刷新一个矩形区域,以及延时执行,比如postInvalidateDelayed(longdelayMilliseconds)或postInvalidateDelayed(longdelayMilliseconds,intleft,inttop,intright,intbottom)方法,
2.当然推荐的方法是通过一个Handler来处理这些,可以在一个线程的run方法中调用handler对象的postMessage或sendMessage方法来实现,Android程序内部维护着一个消息队列,会轮训处理这些,如果你是Win32程序员可以很好理解这些消息处理,不过相对于Android来说没有提供PreTranslateMessage这些干涉内部的方法。
3.Looper又是什么呢?
,其实Android中每一个Thread都跟着一个Looper,Looper可以帮助Thread维护一个消息队列,昨天的问题Can'tcreatehandlerinsidethread错误一文中提到这一概念,但是Looper和Handler没有什么关系,我们从开源的代码可以看到Android还提供了一个Thread继承类HanderThread可以帮助我们处理,在HandlerThread对象中可以通过getLooper方法获取一个Looper对象控制句柄,我们可以将其这个Looper对象映射到一个Handler