1. Android的体系结构是怎样的?请简要加以说明。
Android 的系统架构采用了分层架构的思想。 从上层到底层共包括四层:
应用层、应用框架层、 系统运行库层、 Linux 内核层。
2. Android程序结构是怎样的?请简要加以分析。
src目录:java 源代码
gen目录:BuildConfig.java、R.java
res目录:res 目录是资源目录,可以存放应用使用到的各种资源,如 XML 界面文件、图片、数据等。
assets目录:assets 资源目录一般可用于存放 html 文件、数据库文件、 javascript 文件等, 还有原始格式的文件.
AndroidManifest.xml:列出了应用程序提供的功能,开发好的各种组件需要在此文件中进行配置,当应用使用到系统内置的应用还需在此文件中声明使用权限
3. Handler消息传递机制是怎样的?试以“计时器”的编程举例加以说明。
首先需要在主线程当中创建一个 Handler 对象,并重写handleMessage()方法。然后当子线程中需要进行 UI 操作时,就创建一个 Message 对象,并通过 Handler 将这条消息发送出去。之后这条消息会被添加到 MessageQueue 的队列中等待被处理,而 Looper 则会一直尝试从 MessageQueue 中取出待处理消息,最后分发回 Handler的 handleMessage()方法中。由于 Handler 是在主线程中创建的,所以此时 handleMessage()方法中的代码也会在主线程中运行,于是我们在这里就可以安心地进行 UI 操作了。
一条 Message 经过这样一个流程的辗转调用后,也就从子线程进入到了主线程,从不能更新 UI 变成了可以更新 UI,整个异步消息处理的核心思想也就是如此。
4. 什么是进程内服务?请编程加以说明。
在同一个进程下调用的服务,(通常情况下)即在一个应用程序下的服务。
Service 的启动有两种方式: context.startService() 和 context.bindService()。 新建一个MyService继承自Service,并重写父类的onCreate()、onStartCommand()和onDestroy()方法。
Service与activity通讯:context.bindService()
我们首先创建了一个ServiceConnection的匿名类,在里面重写了onServiceConnected()方法和onServiceDisconnected()方法,这两个方法分别会在Activity与Service建立关联和解除关联的时候调用。
bindService()方法接收三个参数,第一个参数就是刚刚构建出的Intent对象,第二个参数是前面创建出的ServiceConnection的实例,第三个参数是一个标志位,这里传入BIND_AUTO_CREATE表示在Activity和Service建立关联后自动创建Service,这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行。
然后如何我们想解除Activity和Service之间的关联怎么办呢?调用一下unbindService()方法就可以了,这也是Unbind Service按钮的点击事件里实现的逻辑。
5. 什么是跨进程服务?请编程加以说明。
通过一个应用程序(客户端)的Activity调用另一个应用程序(服务端)的Service为跨进程服务。
由于每个应用程序都运行在自己的进程空间,并且可以从应用程序 UI 运行另一个服务进程,而且经常会在不同的进程间传递对象。在 Android 平台,一个进程通常不能访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的基本单元,并且有序的通过进程边界。 Android 提供了 AIDL 工具来处理这项工作。
在 Android 中,如果需要在不同进程间实现通信,就需要用到 AIDL 技术去完成。AIDL 是一种接口定义语言,编译器通过*.aidl 文件的描述信息生成符合通信协议的 Java代码,无需自己去写这段繁杂的代码,只需要在需要的时候调用即可,通过这种方式我们就可以完成进程间的通信工作。
在 Android 中, 每个应用程序都有自己的进程,当需要在不同的进程之间传递对象时,该如何实现呢? 显然, Java 中是不支持跨进程内存共享的。因此要传递对象, 需要把对象解析成操作系统能够理解的数据格式, 以达到跨界对象访问的目的。
我们只是修改了ServiceConnection中的代码。可以看到,这里首先使用了MyAIDLService.Stub.asInterface()方法将传入的IBinder对象传换成了MyAIDLService对象,接下来就可以调用在MyAIDLService.aidl文件中定义的所有接口了。
ClientTest中的Activity如果想要和MyService建立关联其实也不难,首先需要将MyAIDLService.aidl文件从ServiceTest项目中拷贝过来,注意要将原有的包路径一起拷贝过来,完成后项目的结构如下图所示:
这里先是对MyAIDLService.Stub进行了实现,重写里了toUpperCase()和plus()这两个方法。这两个方法的作用分别是将一个字符串全部转换成大写格式,以及将两个传入的整数进行相加。然后在onBind()方法中将MyAIDLService.Stub的实现返回。这里为什么可以这样写呢?因为Stub其实就是Binder的子类,所以在onBind()方法中可以直接返回Stub的实现。
我们实现了 IPerson.Stub 这个抽象类的 hello 方法,然后再 onBind(Intent)方法中返回我们的 stub 实例,这样一来调用方获取的 IPerson.Stub 就是我们的这个实例, hello 方法也会按照我们的期望那样执行。
我们要重写 ServiceConnection 中的 onServiceConnected 方法将IBinder 类型的对象转换成我们的 IPerson 类型。我们再通过服务端 Service 定义的“ android.intent.action.AIDLService”这个标识符来绑定我们所需要的服务,这样客户端和服务端就实现了通信的连接,我们就可以调用 IPerson 中的 hello 方法了。
使用了MyAIDLService.Stub.asInterface()方法将传入的IBinder对象传换成了MyAIDLService对象,接下来就可以调用在MyAIDLService.aidl文件中定义的所有接口了。
IPerson 接口中的抽象内部类 Stub 继承 android.os.Binder 类并实现 IPerson 接口, 其中比较重要的方法是 asInterface(IBinder)方法,该方法会将 IBinder 类型的对象转换成 IPerson 类型,必要的时候生成一个代理对象返回结果。
6. 如何发送广播?请编程加以说明。
因此新建一个 MyBroadcastReceiver 继承自 BroadcastReceiver,代码如下所示:
public class MyBroadcastReceiver extends BroadcastReceiver { @Override
public void onReceive(Context context, Intent intent) { Toast.makeText(context, \"received in MyBroadcastReceiver\ Toast.LENGTH_SHORT).show(); } }
然后在 AndroidManifest.xml 中对这个广播接收器进行注册 然后修改 MainActivity中的代码,如下所示:
public class MainActivity extends Activity { …… @Override
protected void onCreate(Bundle savedInstanceState) { } …… }
可以看到,我们在按钮的点击事件里面加入了发送自定义广播的逻辑。首先构建出了一个 Intent 对象,并把要发送的广播的值传入,然后调用了 Context 的 sendBroadcast()方法将广播发送出去,这样所有监听 com.example.broadcasttest.MY_BROADCAST 这条广播的广播接收器就会收到消息。此时发出去的广播就是一条标准广播。
}); ……
super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new OnClickListener() { }
@Override
public void onClick(View v) {
Intent intent = new Intent(\"com.example.broadcasttest. MY_BROADCAST\"); sendBroadcast(intent);
7. 如何接收系统广播消息?请编程加以说明。
其实只需要新建一个类,让它继承自 BroadcastReceiver,并重写父类的 onReceive()方法就行了。这样当有广播到来时, onReceive()方法就会得到执行,具体的逻辑就可以在这个方法中处理。
registerReceiver(BroadcastReceiver, intentFilter);--oncreate() unregisterReceiver(BroadcastReceiver);--ondestroy()
8. 什么是观察者模式?观察者模式的使用场景是怎样的?观察者模式的优缺点是什么?试编程加以说明。
定义:定义对象间一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新 使用场景:
➢ ➢ ➢
➢ ➢
➢
关联行为场景。注意的是,关联行为是可拆分的,而不是组合关系。 事件多级触发场景
跨系统的消息交换场景,如消息队列的处理机制 观察者和被观察这之间是抽象耦合的 建立一套触发机制
需要考虑下开发效率和运行效率
优点:
缺点:
9. 什么是装饰模式?装饰模式的使用场景是怎样的?装饰模式的优缺点是什么?试编程
加以说明。
装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
装饰器模式的应用场景:( 1) 需要扩展一个类的功能。( 2) 动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。) 缺点:产生过多相似的对象,不易排错。 优点: ➢ ➢ ➢
装饰类和被装饰类可以独立发展,而不会互相耦合 装饰模式是继承关系的一个替代方案
装饰模式可以动态地扩展为一个实现类的功能
10. Executor、ExecutorService和Executors的区别是什么?
Executor 是一个简单的标准化接口,用于定义类似于线程的自定义子系统,包括线程池、异步 IO 和轻量级任务框架。根据所使用的具体 Executor 类的不同,可能在新创建的线程中,现有的任务执行线程中,或者调用 execute() 的线程中执行任务,并且可能顺序或并发执行。
ExecutorService提供了多个完整的异步任务执行框架。 ExecutorService 管理任务的排队和安排, 并允许受控制的关闭。
Executors 类提供大多数 Executor 的常见类型和配置的工厂方法,以及使用它们的几种实用工具方法
Java里面线程池的顶级接口是 Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是 ExecutorService。ExecutorService 继承Executor。Executors 类为创建 ExecutorService 提供了便捷的工厂方法。
11. 为什么说Executors类为创建ExecutorService提供了便捷的工厂方法?
要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在 Executors 类里面提供了一些静态工厂,生成一些常用的线程池。
( 1) newCachedThreadPool: 创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲( 60 秒不执行任务)的线程,当任务数增加时,此线程池又可以智能地添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说 JVM)能够创建的最大线程大小。
( 2) newFixedThreadPool: 创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
( 3) newSingleThreadExecutor: 创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一
个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。 ( 4) newScheduledThreadPool:创建一个定长线程池, 此线程池支持定时以及周期性执行任务的需求。
12. MyBatis与JDDC相比的优势是什么?
(1) 数据库连接不再过于频繁。 (2) SQL语句比较集中 (3) SQL语句参数比较灵活 (4) 支持结果映射和结果缓存。 (5) SQL 重用度提高。 (6) 相对安全
13. 将MyBatis引入到项目中都有哪些步骤?
(1)引入 MyBatis 的 JAR 包; (2)配置 MyBatis 的 XML 配置文件; (3)编写映射接口和 SQL 映射 XML 文件; (4) 在应用中使用接口,获得结果。
14. Fragment的生命周期是什么?试编程加以说明。
(1) 当一个 Fragment 被创建的时候,它会经历以下状态: onAttach()→onCreate()→onCreateView()→onActivityCreated()。
(2) 当这个 Fragment 对用户可见的时候,它会经历以下状态: onStart()→onResume()。 (3) 当这个 Fragment 进入后台的时候,它会经历以下状态: onPause()→onStop()。 (4) 当这个 Fragment 被销毁了(或者持有它的 Activity 被销毁了),它会经历以下状态: onPause()→onStop()→onDestroyView()→onDestroy()→onDetach()。
(5) 一旦 Activity 进入 Resumed 状态(也就是 Running 状态),你就可以自由地添加和删除 Fragment 了。因此,只有当 Activity 在 Resumed 状态时, Fragment 的生命周期才能独立地运转,其它时候是依赖于 Activity 的生命周期变化的。
15. 如何把Fragment加入到Activity中?试编程加以说明。
当 Fragment 被加入 Activity 中时,它会处在对应的 View Group 中。 加载方式①:通过 Activity 的布局文件将 Fragment 加入 Activity 加载方式②:通过编程的方式将 Fragment 加入到一个 ViewGroup 中。
16. 如何安装配置Maven?请简要加以说明。
1. 安装JDK 2. 下载Maven 3. 配置环境变量
4. 给Maven添加本地仓库 5. 配置用户范围settings.xml 6. 错误处理
7. 设置MAVEN_OPTS环境变量
17. 何为Maven坐标?何为Maven仓库?如何编写POM?
坐标:
maven 的坐标通过 5 个元素进行定义,其中 groupId、 artifactId、 version 是必须的, packaging 是可选的(默认为 jar), classifier 是不能直接定义的。
groupId:定义当前 Maven 项目所属的实际项目, 跟 Java 包名类似,通常与域名反向
一一对应。
artifactId:定义当前 Maven 项目的一个模块,默认情况下, Maven 生成的构件,其文
件名会以 artifactId 开头,如 hibernate-core-3.6.5.Final.jar。 version:定义项目版本。
packaging:定义项目打包方式,如 jar, war, pom, zip,……,默认为 jar。 classifier : 定 义 项 目 的 附 属 构 件,如 hibernate-core-3.6.6.Final-sources.jar ,
hibernate-core-3.6.6.Final-javadoc.jar,其中 sources 和 javadoc 就是这两个附属构件的 classifier。 classifier 不能直接定义,通常由附加的插件帮助生成。
仓库:
在 Maven 的术语中,仓库是一个位置(place),例如目录,可以存储所有的工程 jar 文件、library jar 文件、插件或任何其他的工程指定的文件。 Maven 仓库有三种类型:
• • •
本地(local) 中央(central) 远程(remote)
Maven仓库就是放置所有JAR文件(WAR,ZIP,POM等等)的地方,所有Maven项目可以从同一个Maven仓库中获取自己所需要的依赖JAR,这节省了磁盘资源。
编写POM
http://www.blogjava.net/qileilove/articles/410638.html
就像Make的Makefile、Ant的build.xml一样,Maven项目的核心是pom.xml。POM(Project Object Model,项目对象模型)定义了项目的基本信息,用于描述项目如何构建,声明项目依赖,等等。现在先为Hello World项目编写一个最简单的pom.xml。
首先创建一个名为hello
代码清单3-1 Hello World的POM
1.
2. instance\" 4. xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 6.
world SNAPSHOT
world的文件夹,打开该文件夹,新建一个名为pom.xml的文件,输
入其内容,如代码清单3-1所示。
10.
代码的第一行是XML头,指定了该xml文档的版本和编码方式。紧接着是project元素,project是所有pom.xml的根元素,它还声明了一些POM相关的命名空间及xsd元素,虽然这些属性不是必须的,但使用这些属性能够让第三方工具(如IDE中的XML编辑器)帮助我们快速编辑POM。
根元素下的第一个子元素modelVersion指定了当前POM模型的版本,对于Maven 2及Maven 3来说,它只能是4.0.0。
这段代码中最重要的是包含groupId、artifactId和version的三行。这三个元素定义了一个项目基本的坐标,在Maven的世界,任何的jar、pom或者war都是以基于这些基本的坐标进行区分的。
groupId定义了项目属于哪个组,这个组往往和项目所在的组织或公司存在关联。譬如在googlecode上建立了一个名为myapp的项目,那么groupId就应该是
com.googlecode.myapp,如果你的公司是mycom,有一个项目为myapp,那么groupId就应该是com.mycom.myapp。本书中所有的代码都基于groupId com.juvenxu.mvnbook。
artifactId定义了当前Maven项目在组中唯一的ID,我们为这个Hello World项目定义artifactId为hellomyapp
顾名思义,version指定了Hello World项目当前的版本——1.0升级为1.0、1.1
最后一个name元素声明了一个对于用户更为友好的项目名称,虽然这不是必须的,但还是推荐为每个POM声明name,以方便信息交流。
没有任何实际的Java代码,我们就能够定义一个Maven项目的POM,这体现了Maven的一大优点,它能让项目对象模型最大程度地与实际代码相独立,我们可以称之为解耦,或者正交性。这在很大程度上避免了Java代码和POM代码的相互影响。比如当项目需要升级版本时,只需要修改POM,而不需要更改Java代码;而在POM稳定之后,日常的Java代码开发工作基本不涉及POM的修改。
SNAPSHOT。SNAPSHOT意为
快照,说明该项目还处于开发中,是不稳定的版本。随着项目的发展,version会不断更新,如
SNAPSHOT、1.1、2.0等。6.5节会详细介绍SNAPSHOT,第13章会介绍如
何使用Maven管理项目版本的升级发布。
world,本书其他章节代码会分配其他的artifactId。而在前面的groupId为util、myapp
domain、myapp
web等。
com.googlecode.myapp的例子中,你可能会为不同的子项目(模块)分配artifactId,如
参考书:《Android编程权威指南》
18. FragmentManager是如何管理fragment并将它们的视图添加到activity的视图层级结构中
的?
P122-7.6
19. android:layout_weight属性的工作原理是什么?
Android:layout_weight属性告知LinearLayout如何进行子组件的布置安排 LinearLayout是分两个步骤来设置视图的宽度的 第一步:LinearLayout查看layout_width属性值
第二步:LinearLayoout依据layout_weight属性值进行额外的空间分配
20. 如何使用抽象activity托管fragment?
P150
21. 如何使用fragment argument?试举例说明。
P169
22. 在“《Android编程权威指南》- 第12章 对话框”中,CrimeFragment和DatePickerFragment
由同一activity托管。CrimeFragment是如何传递数据给DatePickerFragment的?DatePickerFragment又是如何返回数据给CrimeFragment的?
➢ 要传递crime记录日期给DatePickerFragment,需将记录日期保存在
DatePickerFragment的argument bundle中,这样, DatePickerFragment便可直接获取到它。
1设置目标fragment ➢ ○
建立关联,可以将CrimeFragment设置为DataPickerFragment的目标fragment Public void setTargetFragment(Fragment fragment, int requestCode) 2传递数据给目标fragment ○
建立CrimeFragment与DataPickerFragment间的联系之后,将数据返回给CrimeFragment。返回的日期数据将作为extra附加给Intent
补充了解:
android inflater 用法 在实际开发中LayoutInflater这个类还是非常有用的,它的作用类似于findViewById()。不同点是LayoutInflater是用来找res/layout/下的xml布局文件,并且实例化;而findViewById()是找xml布局文件下的具体widget控件(如Button、TextView等)。 具体作用: 1、对于一个没有被载入或者想要动态载入的界面,都需要使用LayoutInflater.inflate()来载入; 2、对于一个已经载入的界面,就可以使用Activiyt.findViewById()方法来获得其中的界面元素。
FragmentTransaction
如果需要添加、删除、替换Fragment,则需要借助FragmentTransaction对象,FragmentTransaction代表Acticity对Fragment执行的多个改变
Handler“计时器”编程
布局文件:
Java 程序中, 定义了一个 Runnable 对象,重写了其 run()方法, 该方法实现间隔 1 秒 向 Handler 对象发送一条消息, Handler 对象接收都消息以后设置编辑框文本,显示最新 计时。 启动应用程序后,点击开始计时, 设置该按钮的单击事件监听,单击事件定义了一 个接收 Runnable 对象的 Thread, 借由该 Thread 的 start()方法来启动 Runnable。 点击结束
计时,设置按钮的单击事件监听,来停止 Runnable。 具体实现代码如下所示: public class MainActivity extends Activity { TextView txt; Button btnstart; Button btnend; Date oldtime; boolean isstop; Thread thread;
Handler handler=new Handler(){ @Override
public void handleMessage(Message msg) {
if(msg.what == 0x123) {
SimpleDateFormat format=new SimpleDateFormat(\"HH:mm:ss\"); txt.setText(format.format(oldtime)); }
else if(msg.what==0x456) {
thread.stop(); } } };
Runnable runnable=new Runnable() { @Override
public void run() { while(isstop) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) { e.printStackTrace(); }
Date newtime=new Date(oldtime.getTime()+1000); oldtime=newtime;
handler.sendEmptyMessage(0x123); } } };
@Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
btnstart=(Button)findViewById(R.id.btnstart); btnend=(Button)findViewById(R.id.btnend); txt=(TextView)findViewById(R.id.txttime); oldtime=new Date(); oldtime.setHours(0); oldtime.setMinutes(0); oldtime.setSeconds(0);
btnstart.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View arg0) { isstop=true; if(thread==null){
thread=new Thread(runnable); thread.start(); } } });
btnend.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View arg0) { isstop=false; if(thread!=null){
thread.interrupt(); thread=null; } } }); } }
因篇幅问题不能全部显示,请点此查看更多更全内容