Android基础技术核心归纳(一)
转载请声明出处:http://blog.csdn.net/andrexpert/article/details/77887363
Android Java 数据结构
Android基础技术核心归纳(一) Java基础技术核心归纳(一) 数据结构基础知识核心归纳(一)
Android基础技术核心归纳(二) Java基础技术核心归纳(二) 数据结构基础知识核心归纳(二)
Android基础技术核心归纳(三) Java基础技术核心归纳(三) 数据结构基础知识核心归纳(三)
Android基础技术核心归纳(四) Java基础技术核心归纳(四)
不知不觉又是一年的9月,今天跟一个师弟聊天,谈到了他现在面试的一些情况,突然想起自己当年也是这么走过来的,顿时感慨良多。Android/Java经验汇总系列文章,是当初自己毕业时笔试、面试和项目开发中相关的总结,虽然不是很高深的东西,也没有归纳得很全面,但是对Android、算法、Java把握个大概还是没问题,今天特意将这些文章放出来,希望能够对看到这个系列文章的毕业生朋友一点帮助吧。当然,由于受当时知识面的限制,归纳得可能不是很准确,若有疑问就留言吧,我就不细看了。
1.ListView控件与优化
ListView是Android开发过程中较为常见的组件之一,它将数据以列表的形式展现出来。一般而言,一个ListView由以下三个元素组成:
(1)View:展示列表项视图,通常是一个xml所指定的
大家都知道Android的界面基本上是由xml文件负责完成的,所以ListView的界面也理所应当的使用了xml定义。例如在ListView中经常用到的“android.R.layout.simple_list_item_1”等, 就是Android系统内部定义好的一个xml文件。
(2)Adapter适配器:用于将不同的数据映射到View上
不同的数据对应不同的适配器,如ArrayAdapter,SimpleCursorAdapter, SimpleAdapter等, 他们能够将数组,指针指向的数据库,Map等数据或基本组件映射到View上。也正是由于适配器的存在,使得ListView的使用相当灵活,经过适配器的处理后,在 view看来所有的数据映射过来都是一样的。
(3)数据:具体的数据和资源
可以是字符串图片、Map映射等,通过适配器这些数据将会被现实到 ListView上。
总之,所有的数据和资源要显示到ListView上都通过适配器来完成。ArrayAdapter,CursorAdapter, SimpleAdapter为系统已有的适配器,可以将如数组、Cursor指向的数据(一般为数据库)、Map里的数据。
2.Adapter有什么作用?常见的Adapter有哪些?
Adapter是连接后端数据和前端视图显示的适配器接口,是数据和UI(View)之间的桥梁。通过适配器,可以将不同的数据映射并显示到View上(如ListView、GridView)。Date、Adapter、View之间的关系如下图:
常见的Adapter如下:
(1)ArrayAdapter适配器:支持泛型操作,最为简单,列表项只能展示一行字符串;
ListView mListView = new ListView(this);(2)SimpleAdapter适配器:simpleAdapter的扩展性最好,可以定义各种各样的列表项布局出来,可以放上ImageView(图片),还可以放上Button(按钮),CheckBox(复选框)等。使用Map集合作为数据源,通过键到组件Id显示每一个列表项的内容。
String[] mDataSrc={"第一项","第二项","第三项","第四项"}; //数据源
ArrayAdapter<String> mAdapter = new ArrayAdapter<String>(
this, //上下文
android.R.layout.simple_expandable_list_item_1, //列表项视图(只有一个文本控件)
mDataSrc //数据源(也可以为一个List集合)
);
mListView.setAdapter(mAdapter);//将数据源数据映射到ListView的每一列表项
SimpleAdapter adapter =new SimpleAdapter(this, //上下文(3)SimpleCursorAdapter适配器:将数据库中的数据显示到列表项视图中,其中,Cursor对象指向数据库表中的一条记录
getDataSrc(), //指定数据源(List列表,每个元素均为Map对象)
R.layout.simple, //指定列表项布局文件
new String[]{"img","title1","info"}, //获得 数据源Map集合中的所有键值
new int[]{R.id.img,R.id.title1,R.id.info});//获得列表项布局所有控件Id
setListAdapter(adapter); //适配,将Map集合键对应的值一一对应到列表项布局控件Id
其中,getDataSrc实现如下:
private List<Map<String,Object>> getDataSrc() {
List<Map<String,Object>> list=new ArrayList<Map<String,Object>>();
Map<String,Object> map1 = new HashMap<String,Object>();
map1.put("img", R.drawable.a);
map1.put("title1", "华为荣耀");
map1.put("info", "学人家玩抢购,真TM坑爹!");
list.add(map1); //将一个map对象添加到列表中
Map<String,Object> map2= new HashMap<String,Object>();
map2.put("img", R.drawable.b);
map2.put("title1", "中国移动");
map2.put("info", "god knows");
list.add(map2); //将一个map对象添加到列表中
return list;
}
Cursor cursor=getContentResolver().(4)将数据显示在ListView组件中可以使用BaseAdapter的子类,请描述如何使用BaseAdapter类中的抽象方法?
query(People.CONTENT_URI, null, null, null, null);
startManagingCursor(cursor); //获得一个指向系统通讯录数据库的CurSor对象
ListAdapter adapter=new SimpleCursorAdapter(
this, //上下文 //列表项界面(只含一个文本控件)
android.R.layout.simple_expandable_list_item_1,
cursor, //指向数据库的指针
new String[] {People.NAME}, //数据库NAME字段
new int[] {android.R.id.text1}); //列表项布局TextView Id
setListAdapter(adapter); //将数据映射到列表项布局对应的控件
BaseAdapter类有4个抽象方法:getItem、getItemId、getCount和getView,其中前两个方法分别用来返回Object对象和long类型的值。不一定要在这个两个方法中编写具体的代码,一般用这两个方法返回与当前列表项相关的对象和列表项的ID。但必须在getCount和getView方法中编写实际的代码。
getCount方法返回列表数据的总数,例如列表数据来自数组,getCount方法应返回数组的长度。getView方法返回在当前列表项显示的View对象,我们并不需要为每一个列表项创建一个新的View对象。系统会保留曾经显示过的列表项使用的View对象,如果要显示新的列表项,可以利用这些被保存起来的View对象。getView方法有一个convertView参数,如该参数值为Null,表示没有View对象可以利用,需要创建新的View对象。如果该参数值不为Null,这可以直接返回这个参数值,代码如下:
public View getView(int position,View convertView,ViewGroup parent){3.Activity的生命周期以及设计原因
//需要创建一个新的View对象
if(convertView == null){
convertView = LayoutInflater.inflate(R.layout.list_item_view,null);
}
return convertView;
}
(1)设计原因
了解Activity的生命周期的根本目的就是为了设计用户体验更加良好的应用,Activity相当于MVC中的Control层,是为了更好的向用户展现数据,并与之交互。了解Activity的生命周期和各回调方法的触发时机,有利于应用程序在合适的地方向用户展现数据,并保证数据的完整性和程序的良好运行。
(2)Activity的生命周期
Activity生命周期包含七种回调方法:onCreate()、onStart()、onResume()、onPause()、onStop()、onRestart()、onDestroy().
◆void onCreate(Bundle savedInstanceState):当Activity被首次加载时,回调该方法。其中,savedInstanceState参数的作用在于,假如该Activity所在的栈和进程被系统销毁后,用户需要重新回到该Activity之前的状态,可以覆写Activity的void onSaveInstanceState(Bundle outState)事件,通过向outState中写入一些我们需要在窗体销毁前保存的状态或信息,这样在窗体重新执行onCreate的时候,则会通过 savedInstanceState将之前保存的信息传递进来,此时我们就可以有选择的利用这些信息来初始化窗体,而不是一切从头开始。 --与onResume方法结合。
◆void onStart() :启动Activity被回调,activity变为在屏幕上对用户可见时调用;
◆void onResume():activity开始与用户交互时调用;
◆void onPause() : activity被暂停或收回cpu和其他资源时调用,该方法用于保存活动状态,也是保护现场,压栈;
◆void onStop():activity被停止并转为不可见阶段及后续的生命周期事件时调用;
◆void onRestart() :重新启动activity时调用。该Activity仍在栈中,而不是启动新的活动;
◆void onDestroy(): activity被完全从系统内存中移除时调用,该方法被调用可能是因为有人直接调用onFinish()方法或者系统决定停止该活动以释放资源;
(3)Activity几种生命状态
>活动状态:当前Activity位于前台,用户可见,可以获得焦点;
>暂停状态:其他Activity位于前台,该Activity依然可见,只是不能获得焦点;
>停止状态:Activity仍在栈中,但不可见,失去焦点
>销毁状态:该Activity被结束,或Activity所在的Dalvik进程被结束
(4)典型实例
解析:
A 设置Activity的android:screenOrientation="portrait"属性时,无法切换横竖屏,因此不但不会重新调用各个生命周期方法,而且onConfigurationChanged()方法也不会执行。
B 未设置Activity的android:configChanges属性,API上这样说"the activity will be restarted if any of these configuration changes happen in the system.";如果配置有改变,就会重启activity。如果设置了android:configChanges = "orientation|screenSize",并在activity中复写onConfigurationChanged方法,就不会重新走生命周期,执行复写方法中方法。
C launchMode为singleTask的时候,通过Intent启到一个Activity, 如果系统已经存在一个实例,系统就会将请求发送到这个实例上, 但这个时候,系统就不会再调用通常情况下我们处理请求数据的onCreate方法,而是调用onNewIntent方法
D 用户正在操作某个Activity,这时如果其他应用程序需要内存。 此时的Activity是Foreground process,应该按照Empty process,Background process,Service process,Visible process顺序kill,最后才是前台进程。
4.Handler消息处理机制原理
Handler消息传递机制的实质是: Looper、Handler、MessageQueue三者共同实现Android系统中的消息处理机制(即线程间通信)。如在A、B两个子线程之间需要传递消息,首先给每个子线程绑定一套handler、looper、messageQueue机制,然后这三个对象都与其所属线程对应。然后A线程通过调用B线程的Handler对象,发送消息。Android消息机制中引入了消息池,子线程通过Handler对象创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例,这个消息会被Handler发送到B线程的messageQueue中,而属于B线程的Looper对象一直在for循环里无限遍历MessageQueue,一旦发现该消息队列里收到了新的消息,就会去对消息进行处理,处理过程中会回调对应的Handler的handleMessage方法(程序开发人员实现),从而实现了不同线程间通信问题。
(1)Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里的Message Queue(消息队列)。
(2)Handler: 你可以构造Handler对象来与Looper沟通,以便push新消息到Message Queue里;或者接收Looper从Message Queue取出)所送来的消息。
(3)Message Queue(消息队列):用来存放线程放入的消息。
(4)线程:UI thread 通常就是main thread,而Android启动程序时会替它建立一个Looper对象,以及与之对应的唯一的Message Queue。
注释:Handler对象、Looper、MessageQueue同属一个线程,通常是以UI线程为例。
a)Looper实现原理
Looper类里包含一个消息队列对象和一个线程对象。当创建Looper时,会自动创建一个消息队列,同时将内部线程对象指向创建Looper的线程。当开启Looper后(looper.loop())会自动进入无限for循环中,不断去遍历消息队列,如果没有消息则阻塞,有消息则回调handler的handleMessage()方法进行处理。
b)Looper.prepare()
首先,要使用Looper机制一般会在当前线程中创建Handler对象,里面会自动创建一个looper对象和消息队列,这里面的消息队列属于当前线程空间。但此时的looper还不会遍历消息队列,也没有绑定到当前线程。其中,looper对象内部包含一个空消息队列对象和空线程。通过Looper.prepare()方法,先让该消息队列指向当前线程的消息队列,让空线程也指向当前线程,从而实现了绑定。
优点:
◆代码结构清晰,功能定义明确,当需频繁地控制在一个单独的线程中执行操作时,Handler类会是一个很有用的工具。
◆该类允许开发这准确地控制操作的执行时间,还可以重复多次使用它。执行操作的线程会一直运行,直到被显式的终止,适合操作频繁且需要快速间隔地执行操作的情况。
缺点:执行单任务操作时,代码较为复杂。
参考:http://blog.csdn.net/liuhe688/article/details/6407225
5.Android进程间通信的方法,线程间通信的方法
1.线程间通信方法
Handler消息传递机制
2.进程间通信方法
(1)AIDL-----IBinder
(2)ContentProvider
(3)Intent
(4)BroadCastReciever
参考:http://my.oschina.net/zhoulc/blog/199199
参考:http://www.fx114.net/qa-165-90691.aspx(线程间通信)
6.Android中的长度单位详解
如果设置表示长度、高度等属性时可以使用dp 或sp。但如果设置字体,需要使用sp。dp 是与密度无关,sp 除了与密度无关外,还与scale(刻度) 无关。如果使用dp 和sp,系统会根据屏幕密度的变化自动 进行转换。
px(像素):屏幕上的点。
in(英寸):长度单位。
mm(毫米):长度单位。
pt(磅):1/72英寸。
dp(与密度无关的像素):一种基于屏幕密度的抽象单位。在每英寸160点的显示器上,1dp = 1px。
dip:与dp相同,多用于android/ophone示例中。
sp(与刻度无关的像素):与dp类似,但是可以根据用户的字体大小首选项进行缩放。
分辨率:整个屏是多少点,比如800x480,它是对于软件来说的显示单位,以px为单位的点。 density(密度)值表示每英寸有多少个显示点,与分辨率是两个概念。apk的资源包中,
当屏幕density=240时使用hdpi标签的资源
当屏幕density=160时,使用mdpi标签的资源
当屏幕density=120时,使用ldpi标签的资源。
一般android设置长度和宽度多用dip,设置字体大小多用sp. 在屏幕密度为160,1dp=1dip=1px, 1pt = 160/72 sp=2.2sp 1pt = 1/72 英寸.当屏幕密度为240时,1dp=1dip=1.5px.
7.App 出现crash(或称Force close)的常见原因
APP出现崩溃一般考虑以下几个因素:
(1)内存管理错误:可能是可用内存过低,app所需的内存超过设备的限制,app跑不起来导致App crash。或是内存泄露,程序运行的时间越长,所占用的内存越大,最终用尽全部内存,导致整个系统崩溃。亦或非授权的内存位置的使用也可能会导致App crash。
(2)程序逻辑错误:数组越界、堆栈溢出、并发操作、逻辑错误。e.g. app新添加一个未经测试的新功能,调用了一个已释放的指针,运行的时候就会crash。
(3)设备兼容:由于设备多样性,app在不同的设备上可能会有不同的表现(兼容性)
(4)网络因素:可能是网速欠佳,无法达到app所需的快速响应时间,导致app crash。或者是不同网络的切换也可能会影响app的稳定性。
使用android.app.Application和java.lang.Thread.UncaughtExceptionHandler处理异常:
(1)Application:用来管理应用程序的全局状态。在应用程序启动时Application会首先创建,然后才会根据情况(Intent)来启动相应的Activity和Service。本示例中将在自定义加强版的Application中注册未捕获异常处理器。
(2)Thread.UncaughtExceptionHandler:线程未捕获异常处理器,用来处理未捕获异常。如果程序出现了未捕获异常,默认会弹出系统中强制关闭对话框。我们需要实现此接口,并注册为程序中默认未捕获异常处理。
参考:http://blog.csdn.net/liuhe688/article/details/6584143
8.组件的几个重要属性
1.简述android:padding和android:layout_margin属性区别?---间隔距离
(1)android:padding属性:用于设置组件内容到组件边缘的距离(同时设置上下左右的间隔距离)
android:paddingTop(Bottom/Left/Right),则分别设置上、下、左、右的间隔距离
(2)android:layout_margin属性:用于设置组件之间的间隔距离,或距离父组件边缘的距离;
android:layout_marginTop(Bottom/Left/Right),则分别设置上、下、左、右的间隔距离
2.简述android:gravity和android:layout_gravity属性的区别?--相对位置
(1)android:gravity属性:用于设置组件内容相对组件的位置,如可以设置TextView组件中国的文本水平居中和垂直居中;
(2)android:layout_gravity属性:用于设置当前组件相对于父组件(如容器)的位置;
注意:android:layout_gravity有时并不是所有的属性值都起作用。例如,<LinearLayout>标签的android:orientation属性值为vertical时,android:layout_gravity属性值只有center_vertical、left、right起作用,center属性值只保留水平居中的特性。如果将android:orientation属性值为horizontal时,android:layout_gravity属性值只有center_vertical、top、bottom起作用,center属性值只保留垂直居中的特性。
3.简述android:layout_weight属性的功能,并举例说明?
android:layout_weight属性用于设置当前组件在水平或垂直方向所占空间的大小。
(1)组件的android:layout_width或android:layout_height="fill_parent或match_parent",组件所占的宽度或高度与android:layout_weight属性的值成反比;
(2)组件的android:layout_width或android:layout_height="0dp",组件所占的宽度或高度与android:layout_weight属性的值成正比;
9.SQLite开发技巧
1.SQLite简介
SQLite是一个开源的轻量级关系型数据库,它支持SQL语言,SQLite本身就是一个文件,因此只需占用很少的内存就能获得很好的性能。SQLite并没有包含大型数据库(如Oracle)的所有特性,但它包含了操作本地数据的所有功能,主要特点如下:
(1)SQLite占用内存小(本身就是个文件),适合嵌入式设备,简单易用、反应迅速;
(2)SQLite字段类型无关。即允许把各种类型的数据保存到任何字段中,而不用关心字段实际声明的数据类型是什么,INTERGER PRIMARY KEY类型字段(存储整数)除外。例如,可以把字符类型的值存入声明为INTEGER类型中。
(3)SQLite在解析建表语句时,会忽略建表语句中跟在字段后面的数据类型信息
如:create table person(id integer primary key autoincrement,name varchar(20));
等价于:create table person(id primary key autoincrement,name)
(4)SQLite内部只支持NULL、INTEGER、REAL(浮点数)、TEXT(字符串文本)和BLOB(二进制对象)这五种数据类型,但实际上SQLite也接受varchar(n)、char(n)、decimal(p,s)等数据类型,只不过在运算或保存时会转换成上面对应的数据类型。
2.Android SQLite API
Android系统内置了丰富的API来供开发人员操作SQLite,以便完成对数据的存取。
(1)SQLiteOpenHelper:Android提供管理数据库的工具类,主要用于数据库的创建、打开和版本更新。调用SQLiteOpenHelper的getWritableDatabase()方法(--->可读可写的形式打开一个数据库)或者getReadableDatabase()方法(-->以只读的形式打开一个数据库)获取SQLiteDatabase实例,如果数据库不存在,Android系统会自动生成一个数据库,然后调用OnCreate()方法,在onCreate()方法里生成数据库表结构以及添加应用需要的初始化数据。
(2)SQLiteDatabase是Android提供的代表数据库的类(即SQLiteDataBase对象就是一个数据库文件),还封装了一些操作数据库的API,使得该类可以完成对数据进行添加、查询、更新和删除操作。可以使用execSQL(..)方法执行SQL语句:
◆execSQL(String sql,Object[] bindArgs):执行带占位符的SQL语句(如insert、delete、update、create table等),如果sql语句中没有占位符,则第二个参数可传null;
◆execSQL(String sql):执行SQL语句;
◆ rawQuery(String sql,String[] selectionArgs):执行带占位符的SQL查询(select语句)。
除此之外, SQLiteDatabase还专门提供了对应于添加insert()、删除delete()、更新update()、查询query()的操作方法。
(3)Cursor接口:Cursor是结果集游标(指向数据库表中的一条记录),用于对结果集进行随机访问,其提供了一些方法来移动查询结果的记录游标,并且提供getString(0)或getXXX(一条记录的字段序号)返回一条记录第x字段的数值。
3.SQLite使用举例:将查询的所有记录,存放到ArrayList集合中
MySQLiteUtil mySQLiteUtil = new MySQLiteUtil(MainActivity.this, "memeto.db", null, 1);参考:
SQLiteDatabase myDatabase=mySQLiteUtil.getReadableDatabase(); //获取SQLiteDatabase实例并以读写方式打开,自动回调MySQLiteUtil的onCreat()方法创表
String SELECT_SQL="select * from memeto_tb where subject like ? and content like ? and date like ?";
Cursor cursor= myDatabase.rawQuery(SELECT_SQL, new String[]{"%"+subject+"%","%"+content+"%","%"+time+"%"}); //结果集cursor存储数据库表的所有记录
ArrayList<Map<String,Object>> records= new ArrayList<Map<String,Object>>();
while(cursor.moveToNext()) //cursor指向一条记录
{
Map<String,Object> item = new HashMap<String, Object>();
item.put("id", cursor.getInt(0)); //将一条记录的ID字段值映射到键"id"
item.put("subject", cursor.getString(1));
item.put("content", cursor.getString(2));
item.put("date", cursor.getString(3));
records.add(item);
}
myDatabase.close(); //否则,会出现SQLiteException异常
cursor.close();
注:MySQLiteUtil为继承于SQLiteOpenHelper的子类,主要用于数据库的创建并通过db.execSQL()方法生成一个数据表结构。
public class MySQLiteUtil extends SQLiteOpenHelper {
public MySQLiteUtil(Context context, String name, CursorFactory factory, int version) {
super(context, name, factory, version);
}
/*2.onCreate方法 :生成数据库表结构、添加初始化数据*/
public void onCreate(SQLiteDatabase db) {
String CREATE_TABLE_SQL="create table memeto_tb(id primary key autoincrement,
subject,content,date)";
db.execSQL(CREATE_TABLE_SQL);
}
}
http://blog.csdn.net/liuhe688/article/details/6715983#comments
10.Android常用控件介绍
1.单选框(RadioButton与RadioGroup)
RadioGroup用于对单选框进行分组,相同组内的单选框只有一个单选框被选中。
事件:setOnCheckedChangeListener(),处理单选框被选择事件。把RadioGroup.OnCheckedChangeListener实例作为参数传入。
2.多选框(CheckBox)
每个多选框都是独立的,可以通过迭代所有的多选框,然后根据其状态是否被选中在获取其值。 事件:setOnCheckChangeListener()处理多选框被选择事件。把CompoundButton.OnCheckedChangeListener实例作为参数传入
3.下拉列表框(Spinner)
Spinner.getItemAtPosition(Spinner.getSelectedItemPosition());获取下拉列表框的值。
事件:setOnItemSelectedListener(),处理下拉列表框被选择事件把AdapterView.OnItemSelectedListener实例作为参数传入;
4.拖动条(SeekBar)
SeekBar.getProgress()获取拖动条当前值
事件:setOnSeekBarChangeListener(),处理拖动条值变化事件,把SeekBar.OnSeekBarChangeListener实例作为参数传入。
5.菜单(Menu)
重写Activity的onCreatOptionMenu(Menu menu)方法,该方法用于创建选项菜单,当用户按下手机的"Menu"按钮时就会显示创建好的菜单,在onCreatOptionMenu(Menu Menu)方法内部可以调用menu.add()方法实现菜单的添加。
重写Activity的onMenuItemSelected()方法,该方法用于处理菜单被选择事件。
6.进度对话框(ProgressDialog)
创建并显示一个进度对话框:ProgressDialog.show(ProgressDialogActivity.this,"请稍等","数据正在加载中....",true); 设置对话框的风格:setProgressStyle()
参数:
ProgressDialog.STYLE_SPINNER 旋转进度条风格(为默认风格)
ProgressDialog.STYLE_HORIZONTAL 横向进度条风格
11.请介绍下Android中常用的五种布局
在Android应用开发中,Android官方推荐使用布局管理器来管理组件的分布、大小,而不是直接设置组件的位置和大小、最常用的方法是预先设置好容器边距(分布)、大小,然后其包含的组件使用"fill_match"或"wrap_content"自动适应父容器。
1.LinearLayout线性布局
线性布局特点是:通过android:orientation属性控制布局内组件横向或纵向排列,Android
的线性布局不会换行,当组件一个挨着一个地排到头之后,剩下的组件将不会被显示出来。
(1)android:gravity属性:用于设置组件内容相对组件的位置,如可以设置TextView组件中国的文本水平居中和垂直居中;
(2)android:layout_gravity属性:用于设置当前组件相对于父组件(如容器)的位置;
(3)android:layout_weight属性用于设置当前组件在水平或垂直方向所占空间的大小,一般采用
android:layout_height="0dp"(垂直布局)或android:layout_width="0dp"(水平布局)形式
2.TableLayout表格布局
TableLayout表格布局管理器继承于LinearLayout,采用行、列的形式来管理UI组件,每次向TableLayout中添加一个TableRow(也为容器),就表示添加一行;向每个TableRow中添加其他子组件该表格就增加一列.
(1)android:collapseColumns:如果某个列或多个被设为Collapsed,该列的所有单元格被隐藏;
(2)android:shrinkColumns:如果某个列或多个列设为Shrinkable,该列的所有单元格的宽度可以被收缩,以保证该表格能适应父容器的宽度;
(3)android:stretchColumns:如果某个列或多个列设为Strtchable,该列的所有单元格的宽度可以被拉伸,以保证组件能完全填满表格剩余空间。
3.FrameLayout帧布局
帧布局容器为每个加入其中的组件创建一个空白的区域(即为一帧)。每个子组件占据一帧,这些帧都会根据gravity属性执行自动对齐,帧布局的效果即把组件一个一个地叠加在一起。
4.RelativeLayout相对布局
相对布局管理器内子组件的位置总是相对兄弟组件、父容器来决定,每个组件或容器事先需指定android:id属性。
(1)android:layout_above(below/toLeftOf/toRightOf):控制该子组件位于给出ID组件的上方
(2)android:layout_centerInParent:控制某个组件是否位于父容器的*位置;
(3)android:layout_alignParentLeft(Buttom等):控制某组件是否位于布局容器的左边对齐;
(4)android:layout_alignRight(Left/Bottom/Top):控制某组件位于给出ID组件的右边界对齐;
5.GridLayout网格布局
GridLayout网格布局是Android 4.0新增的布局管理器,GridLayout类似于HTML中的table标签,它把整个容器划分成为"行*列"个网格,每个网格可以放置一个组件,另外,也可以设置一个组件横跨多个列和多个行。
(1)android:columnCount:设置该网格的列数量;
(2)android:rowCount:设置该网格的行数量;
(3)android:layout_column:设置某个组件在GridLayout的第几列
(4)android:layout_columnSpan:设置某个组件在GridLayout横向上跨几行列
(5)android:layout_row:设置某个组件在GridLayout的第几行
(6)android:layout_rowSpan:设置某个组件在GridLayout纵向上跨几行
12.如何启用Service,如何停用Service?
Service是Android四大组件之一,有自己的生命周期,它只运行在后台且没有交互界面,但可以和其他组件进行交互。需要注意的是,Service不是一个单独的进程或者线程,而是像其他应用对象一样运行在其托管进程的主线程中,若希望在Service执行耗时任务,仍需要新建一个线程来实现。Servcie的生命周期有两种形式:
startService启动:onCreate()->onStart()->....running.....->onDestory
bindService启动:onCreate()->onBind()->....bind&running....->onUnbind()->onDestroy()
开发思路:首先,实现一个继承Service的子类,并且在应用的AndroidManifest.xml文件中配置该Service;其次,在本应用的Activity或另一个应用的Activity中通过调用startService(Intent intent)或bindService(Intent service,ServiceConnection conn,int flags)方法启动该Service即可。
1.startService(Intent intent)方式启动
当客户端(组件)通过Context的startService()启动Service后,访问者与Service之间没有关联,该Service将一直在后台执行,即使调用startService的进程结束了,Service仍然还存在,直到有访问者调用stopService(),或者Service自己自杀(stopSelf()).在这种情况下,Service与访问者之间无法进行通信、数据交换。
Intent intent = new Intent();
intent.setAction("com.example.service.FIRST_SERVICE"); //指定启动哪个Service
startService(intent); //启动指定的Service
stopService(intent); //关闭指定的Service
注意:当用户多次启动Service时,Service子类的onCreate()方法只会被调用一次,但是onStartCommand()方法会被调用同等次数。
2.bindService(....)方式启动
当访问者(应用/组件)该方法启动Service,访问者与Service绑定在一起,访问者一旦退出,Service也就终止了,Service组件允许通过IBinder对象与访问者进行通信和数据交换。其中,这种启动方式的关闭方式可以是访问者调用onUnbind()或stopService()方法终止所启动的Service,或者Service自己调用stopSelf()自杀。其中,
Context.bindService(Intent intent,ServiceConnection conn,int flags)
参数分析:
(1)intent:访问者通过Intent对象指定要启动的Service;
(2)conn:ServiceConnection对象,该对象用于监听访问者与Servcie之间的连接情况,当访问者连接指定的Service成功且onBind()返回的IBinder对象不为null,访问者将回调该对象的onServiceConnected(ComponentName name,IBinder service)方法获得Service传递过来的IBinder对象;当Service所在的宿主进程由于异常终止导致该Service与访问者之间断开连接时回调onServiceDisconnected(..)方法。需要注意的是,当访问自己调用unBindService()方法与Service断开连接时,ServiceConnection对象的onServiceDisconnected(ComponentName name)方法并不会被调用。
13.Android的service与activity的通讯方法
1.绑定并与本地Service通信
要实现访问者(Activity)与本地Service进行通信,访问者需调用bindService()方法绑定并启动指定的Service,并通过Service返回的IBinder对象实现访问者(Acitvity)与Service之间的通信,最后访问者调用unBindService与Service解绑并停止Service。
(1)Service实现类
首先,定义一个IBinder子类用于实现业务逻辑代码;并回调onBind()方法将IBinder对象返回给访问者用于实现通信。
public class BindService extends Service {(2)AndroidManifest.xml配置Service
....
private MyIBinder binder=new MyIBinder(); //实例一个IBinder对象
//IBinder子类,实现业务逻辑代码
public class MyIBinder extends IBinder{
/****成员变量***/
/****成员方法(业务逻辑代码)***/
}
//回调方法,用于返回IBinder对象 给访问者
public IBinder onBind(Intent intent) {
return binder;
}
//回调方法,启动Service调用该方法
public void onCreate(){
}
//回调方法,Service被断开调用该方法
public boolean onUnbind(Intent intent) {
return true;
}
//回调方法,Service被关闭,所有资源被回收
public void onDestroy() {
}
<service android:name=".BindService">(3)Activity实现类
<intent-filter>
<!-- 为该Service组件的intent-filter配置action -->
<action android:name="com.example.service.mBIND_SERVICE"/>
</intent-filter>
</service>
public class BindServiceTest extends Activity {2.跨进程调用Service
BindService.MyIBinder binder;
private ServiceConnection conn = new ServiceConnection(){
public void onServiceConnected(ComponentName name
, IBinder service)
{
//获取Service的onBind方法所返回的MyBinder对象
binder=(BindService.MyIBinder)service;
}
public void onServiceDisconnected(ComponentName name) {
}
};
//注释:其实可以实例一个ServiceConnection对象作为binService参传递
protected void onCreate(Bundle savedInstanceState) {
.....
Intent intent = new Intent();
intent.setAction("com.example.service.mBIND_SERVICE");
bindService(intent,conn,Service.BIND_AUTO_CREATE); //启动指定的Service
...
//接下来就可以使用binder对象调用Service内部类MyBinder实现的业务逻辑代码
//实现Activity与Service的数据交互了
}
........
}
使用AIDL接口实现访问远程(其他应用进程)的Sercvice,具体步骤如下:
(1)应用进程A提供Service服务
◆>首先,定义一个AIDL接口(Itest.aidl)保存在src目录下,该接口中声明进程通信需要用到的业务逻辑方法。
package com.example.aidlservice;◆>其次,实现一个Service子类。
interface Itest
{
String getColor();
double getWeight();
}
//实例化一个IBinder对象,返回给访问者并作为通信桥梁◆>最后,重写onBind(Intent intent)方法
private TestBinder mBinder = new TestBinder();
//继承Stub接口的内部类,由于Stub继承于IBinder,即实现IBinder接口
public class TestBinder extends Itest.Stub
{
/*业务逻辑代码,方法定义在AIDL接口中
*注:如果进程通信需要更多的方法,需要在.aidl接口中声明
*/
public String getColor() throws RemoteException {
return null;
}
public double getWeight() throws RemoteException {
return null;
}
}
将IBinder对象返回给启动该Service的Intent对象◆>清单文件中配置Service
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
<service android:name=".AidlService">(2)应用进程B访问远程的Service服务
<intent-filter>
<action android:name="com.jiangdongguo.service"/>
</intent-filter>
</service>
首先将.aidl接口文件,拷贝到客户端进程B工程源码中,生成一个相应的Java接口文件。
private ITest.Stub mBinder;注:由于mBinder对象是全局的,在类中可以通过mBinder对象访问服务进程A中的方法或数据。
//获取远程Service的onBind方法的对象的代理
private ServiceConnection conn=new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder serviceIBinder) {
mBinder = ITest.Stub.asInterface(serviceIBinder);
//注释:Test为自动生成.aidl接口对应的类,Stub为内部类
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
//通过Intent启动远程Service
public void startMyService(){
Intent intent=new Intent("com.jiangdongguo.service");
bindService(intent,conn,Service.BIND_AUTO_CREATE);
}
//通过获得的IBinder对象,调用Test.aidl定义的逻辑业务方法
public void getDate(){
String valueOfcolor = mBinder. getColor() ;
double valueOfweight=mBinder.getWeight()
}
14.AIDL工作原理?
1.AIDL简介
AIDL,Android Interface Definition Language,即Android接口定义语言,用于定义两个进程之间的通信接口。首先,在服务端定义好AIDL接口后(xxx.aidl),ADT工具会自动在gen目录下生成相应的包,并生成一个xxx.java类,在该类包含一个Stub内部类,该内部类实现了IBinder(即继承了IBinder),这个Stub类将会作为远程Service的回调类,通过asInterface静态方法可以返回一个IBinder对象。然后,将相同的AIDL接口拷贝到客户端,客户端获取了远程Service的IBinder对象的代理(AIDL接口实现类)之后,客户端进程就可通过该IBinder对象去回调远程Service的属性或方法,从而实现进程间的通信。
2.AIDL接口语法
(1)AIDL定义的接口源码必须以.aidl为后缀;
(2)AIDL接口中用到数据类型,除了基本类型、String、List、Map、CharSequence之外,其他类型全部都需要导包.如/src/com/android_aidlservice/Itest.aidl:
package com.example.aidlservice;15.4种activity的启动模式(launchMode)
interface Itest{
/*此处为逻辑业务方法
* 数据交互*/
String getColor();
int getWeight();
}
保存后,ADT工具会自动在gen/com.example.aidlservice目录下生成一个Itest.java接口,Itest.java源码(部分)如下:
public interface Itest extends IInterface{
public static abstract class Stub extends Binder.........{
public static asInterface(IBinder obj)
{
..........
return obj; //返回服务端IBinder对象
}
@Override
public int getWeight() throws android.os.RemoteException
{
}
@Override
public java.lang.String getColor() throws android.os.RemoteException
{
}
}
}
Activity的启动模式在多个Activity跳转的过程中扮演着重要的角色,它可以决定是否生成新的Activity实例、是否重用已存在的Activity实例、是否与其他Activity实例共用一个task。其中,task是一个具有栈结构的对象,一个task可以管理多个Activity,创建一个新的Activity实例为入栈,销毁一个存在的Activity实例为出栈。
Android有4种lanuchMode,可以在清单文件中<activity..>元素中配置android:launchMode属性即可指定相应的启动模式。
(1)standard(Activity默认启动模式)
每次启动一个Activity,无论栈中是否存在该Activity实例,都会生成一个新的实例并且将其存放到同一个栈的栈顶位置。
(2)singleTop模式---
每次启动一个Activity,系统会先在栈中寻找栈是否存在该Activity的实例。如果存在且位于栈顶,则不会创建一个新的Activity实例而是直接使用它;如果不存在或者存在但不位于栈顶,则会重新生成一个实例。
(3)singleTask模式--一个栈
若将某个Activity(如FirstActivityinstance)设置为singleTask模式,假如从另一个Activity启动FirstActivityinstance,如果栈中存在FirstActivityinstance,则先会将FirstActivityinstance之上的所有Activity统统出栈,将FirstActivityinstance变为栈顶对象,显示到屏幕。
(4)singleInstance模式---栈只需包含一个实现
若将某个Activity设置为singleInstance模式,当新建该Activity实例时,系统会启动一个新的栈结构来存放该Activity对象,并保证不再有其他Activity实例进入这个新建的栈结构。
注意:从SecondActivity跳转到FirstActivity的时候,我们的起点变成了SecondActivity实例所在的栈结构。
参考:http://blog.csdn.net/liuhe688/article/details/6754323
16.什么是ANR,如何避免它?
1.ANR
ANR(Application Not Responding),即应用程序无响应异常。在Android上,如果应用程序在一段时间内无响应,系统会向用户显示一个应用程序无响应对话框。用户可以选择“等待”而让程序继续运行,也可以选择“强制关闭”。默认情况下,当主线程在5秒内没有响应输入的事件(如按键按下、屏幕触摸);BroadcastReceiver在onReccieve()方法中10s内没有执行完毕就会触发ANR异常。
2.避免ANR
(1)为了防止阻塞主线程,要避免在UI主线程中处理耗时操作,如访问网络、访问数据库、网络图片加载、耗时计算,应该在子线程里来完成。
(2)对于BroadcastReceiver的执行,广播接收器执行的任务尽可能要快,如是长时间任务,可以在BroadcastReceiver中(Intent)启动一个Service后台服务(在后台创建一个子线程)来完成。
(3)添加增强响应灵敏性的方法,告诉用户应用处于正常工作中
a. 通过对话框(ProgressBar和ProgressDialog)告知用户要等待;
b. 显示Splash屏幕,用于执行耗时的初始化过程
3.OOM--内存溢出
Out Of Memory,一般是由于程序编写者对内存使用不当,如对该释放的内存资源没有释放,导致其一直不能被再次使用而使计算机内存被耗尽的现象。
.....
....
...
...
4.Froce Close
Froce Close,即强制退出对话框,与ANR会再次响应不同的是,FC会因为某些异常会直接退出程序。常见的原因有:
(1)内存问题 内存溢出OOM、内存泄漏、内存访问错误
(2)程序逻辑错误 异常(不可查异常RuntimeException)
(2)设备不兼容
(3)网络达不到实时性要求
17.Android Intent的使用
Intent类对象是Android组件(Activity、Service、BroadcastReceiver)间的通信载体,它封装了Android应用需要启动某个组件的"意图",如Component属性、Action属性等,通过Intent对象实现指定启动哪个组件并完成什么样的动作(包含动作所需的数据) 。
1.Intent工作原理
以启动一个Activity组件为例,首先发出"意图"的组件,通过调用startActivity(intent)开始启动指定的组件,其中Intent对象封装了被启动组件的相关信息。调用"动作"实际上执行了
Instrumentation对象,它是整个应用激活的Activity管理者,集中负责应用内所有Activity的运行。它有一个隐藏的方法execStartActivity,它去掉了一些细节,将Intent对象封装的相关信息传递到ActivityManagerService。然后,ActivityManagerService会将这些关键信息递交给另一个服务PackageManagerService,此服务掌握整个软件包及其各组件的信息,它会将传递过来的Intent,与已知的所有Intent Filters进行匹配(如果带有Component信息就无需匹配)。如果找到,就将相关的Component信息通知回ActivityManagerService。最后,启动指定的Activity。
2.Intent对象属性
(1)Action属性:它代表了该Intent对象要完成一个什么样的"动作",比如查看、拨打、编辑等.
(2)Data属性:为Intent对象的Action属性提供了操作的数据,Data属性只接受一个Uri对象.
(3)Catagory属性:为了使的"意图"更加精确,通过该属性给意图添加一些约束.
(4)Extras属性:该属性实现参数传递,是一个Bundle对象,由一组可序列化key/value对组成
(5)Flags属性:该属性表示一些附件的指令,这些指令用于指明组件的运行模型,如
FLAG_ACTIVITY_NEW_TASK标识表示Activity组件运行在两个不同的栈结构中。
3.Intent启动组件方式
(1)隐式Intent
指的是Intent不能确定它将要启动哪个组件(没有指定Component属性)-通过AndroidManifest.xml文件中的Intent Filter来对组件进行筛选来确定启动的组件。
Uri uri=Uri.parse("http://blog.csdn.net/u012637501");
Intent intent=new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(uri);
startActivity(intent);
(2)显式Intent
所谓显示intent,指的是Intent已经明确了它将要启动哪个组件-通过指定Intent对象的Component属性实现。
Intent intent=new Intent();
//ComponentName comp=new ComponentName(FActivity.this,SActivity.class);
//intent.setComponent(comp);
intent.setClassName(FActivity.this,SActivity.class);
//也可直接是包名,实际也是通过指定Intent对象的Component属性实现。
startActivity(intent);
4.不同Activity/Service/BroadcastReciever之间的数据传递
Bundle对象用于不同组件(如Activity)之间的数据传递,对于不同Activity而言,Bundle对象就相当于一个数据包(保存的是多组键值对),Intent对象就充当搬运工(载体)。
(1)首先,打包。
创建一个Bundle对象bundle并调用其成员方法PutString或者putInt或者putDouble等方法,将要传递的数据赋值给指定的变量(键值)再将他们压入bundle对象中,即“制作数据包”;
(2)然后,装载。把已经制作好的"数据包"(即Bundle对象),通过Intent对应Extras属性的putExtras方法,将Bundle对象(数据包)交给Intent对象(搬运工);
(3)最后,卸载。被启动的Activity先通过getIntent()方法获得启动它的Intent对象,然后调用Bundle bundle = this.getIntent().getExtras()方法将Bundle对象从Intent中分离。得到Bundle对象后,就可以使用其getString("键值")或getXXX(xx)等方法从数据包(Bundle对象)中获取键对应的值。
5.Intent数据传递
解析:Intent可以传递如下几种数据类型
◆8大基本数据类型及其数组;
◆Bundle CharSequence parcelable及其数组、Serializable String及其数组;
◆Arraylist (Integer String Parcelable类型);
其中,实现序列化的对象存放在本地文件,实现parcelable的对象存放在内存中。
(1)Serializable :将 Java 对象序列化为二进制文件的 Java 序列化技术,被序列化的类需要实现 Serializable 接口,使用ObjectInputStream 和 ObjectOutputStream 进行对象的读写;
(2)charsequence :在JDK1.4中,引入了CharSequence接口,实现了这个接口的类有:CharBuffer、String、StringBuffer、StringBuilder这个四个类。 CharBuffer为nio里面用的一个类,String实现这个接口理所当然,StringBuffer也是一个CharSequence,StringBuilder是Java抄袭C#的一个类,基本和StringBuffer类一样,效率高,但是不保证线程安全,在不需要多线程的环境下可以考虑。
(3)Parcelable : android提供了一种新的类型:Parcel。本类被用作封装数据的容器,封装后的数据可以通过Intent或IPC传递。 除了基本类型以外,只有实现了Parcelable接口的类才能被放入Parcel中.另一种序列化,功能和Serializable相似,主要是序列化的方式不同。
(4)Bundle:Bundle是将数据传递到另一个上下文中或保存或恢复你自己状态的数据存储方式。它的数据不是持久化状态。
参考:http://blog.csdn.net/u012637501/article/details/41091299
18.BroadcastReciever,广播接收器工作机制
1.广播发送
广播是一种广泛运行在应用程序组件之间传输信息的机制,主要分为两类:
(1)普通广播:该类广播完全异步,可以在同一时刻被所有接收者接收到,消息传递的效率比较高,但缺点是接收者不能将处理的结果传递给下一个接收者,且无法终止Broadcast Intent的传播。发送普通广播:
Intent intent = new Intent();
intent.setAction("com.jiangdongguo.android.myBroadcastReceover");
Context.sendBroadcast(intent),其中,Intent对象封装了接收该广播的广播接收者所需要满足的条件以及传递的数据。
(2)有序广播:该类广播的接收者将按预先声明的优先级次序接收广播,有序广播接收者可以终止广播的传播,且可以将处理的结果数据传递给下一个接收者。发送有序广播:
Intent intent = new Intent();
intent.setAction("com.jiangdongguo.android.myBroadcastReceover");
Context.sendOrderedBroadcast(Intent intent,String receiverPermission),其中,receiverPermission表示接收该广播的许可权限。
注:由于发送普通广播和有序广播的方法属于Context对象,因此,可以在应用程序任意地方实现发送一条广播,并通过Intent对象封装广播接收器是否能接收到该广播的条件。
2.广播接收
BroadcastReceiver(广播接收器)是对发出的广播进行过滤接收并响应的组件,BroadcastReceiver本质是一种全局监听器,用于监听系统全局的广播消息并接收指定的广播,它可以非常方便地实现系统中不同组件之间的通信。通常一个广播可以被订阅了该Intent的多个广播接收者所接收,如同一个广播台可以被多位听众收听。
(1)实现广播接收器
实现一个广播接收器,接收到指定广播后自动回调onReceive方法。在这个方法中我们可以启动一个Activity或者启动一个Service,或者通过NotifucationManager提醒用户。
public class MyBroadcastReceiver extends BroadcastReceiver
{
public void onReceiver(Context context,Intent intent){
}
}
注:在onReceiver()方法中,接收了一个Intent的参数,通过它可以获取广播所携带的数据,也可以启动一个Service服务来处理耗时任务等(在Service服务中创建新线程处理耗时任务)。不应考虑在onReceiver方法中创建新线程去完成耗时的操作,因为BroadcastReceiver本身的生命周期极短,可能出现的情况是子线程可能没有结束,BroadcastReceiver就已经退出了。
(2)注册该广播接收器,指定可以接收的广播
创建完广播接收者后,并不能马上使用,还必须为它(广播接收者)注册一个指定的广播,就如同我们有了收音机后,还必须选择收听哪个频道一样。
(1)静态注册:是指在配置文件(清单文件)AndroidManifest.xml文件中进行注册。
<receiver android:name=".MyBroadcastReceiver">
<!--指定可以接收的广播-->
<intent-filter>
<action android:name = "com.jiangdongguo.android.myBroadcastReceover"/>
</intent-filter>
</receiver>
(2)动态注册:需要在代码中动态的指定广播地址并注册,通常是在Activity或Service中调用ContextWrapper的
MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
IntentFilter filter = new IntentFilter("com.jiangdongguo.android.myBroadcastReceover"); //指定接收哪个广播
registerReceiver(myBroadcastReceiver ,filter )方法进行注册
其中,MyBroadcastReceiver为基类BroadcastReceiver子类。注册完成后,即可接收相应的广播消息,一旦广播(Broadcast)事件发生后,系统就会创建对应的BroadcastRecevier实例,并自动触发它的onReceiver()方法,onReceiver()方法执行后,BroadcastReceiver的实例就会被销毁。
参考:http://blog.csdn.net/u012637501/article/details/46364569
19.Android系统的特点与架构
1.Android系统的特点
(1)Android拥有完善的应用程序框架,支持4大应用组件(Activity、Service、ContentProvider和Broadcast),可以在任意层次上进行复用和更换。
(2)虽然Android的主要编程语言是Java,但Android中的Java字节码是运行在Dalvid虚拟机上的。传统的JVM是基于堆栈的,而Dalvid虚拟机是基于寄存器的。因此,Dalvid虚拟机上运行的Java程序要比在传统的JVM上运行的Java程序速度更快。
(3)Android中内置了WebKit核心的浏览器,支持HTML5等新的Web标准;
(4)支持轻量级的SQLite数据库
(5)支持众多的硬件传感器(如方向传感器、重力传感器、压力传感器等)和其他一些硬件如蓝牙、Wifi等
(6)开源的移动操作系统,研发成本低
2.Android系统的架构
android的系统架构和其操作系统一样,采用了分层的架构。从架构图看,android分为四个层,从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和linux核心层。
(1)应用层:该层由运行在Dalvid虚拟机上的应用程序组成,如日历、地图、浏览器等;
(2)应用框架层:该层主要由View、通知管理器(Notification Manager)、活动管理器(Activity Manager)等由开发人员直接调用的API组成;
(3)系统运行库层:Java本身是不能直接访问硬件的,要想让Java访问硬件,必须使用NDK才可以。NDK是一些由C/C++语言编写的库(主要是*.so文件)。这些程序也是该层的主要组成部分,该层主要包括C语言标准库、多媒体库、OpenGL ES、SQLite、WebKit、Dalvid虚拟机等,即该层是对应用框架层提供支持的层。
(4)Linux内核层:该层主要包括驱动、内存管理、进程管理、网络协议栈等组件。目前Android的版本基于Linux2.6内核。
20.Dalvid虚拟机和JVM区别?
Android运行时由两部分组成:Android核心库集和Dalvid虚拟机。其中,核心库集提供了Java语言核心库所能使用的绝大部分功能,而虚拟机则负责运行Android应用程序。每个Android应用程序都运行在单独的Dalvid虚拟机内,即每个Android应用程序对应一条Dalvid进程,Dalvid专门针对同时高效地运行多个虚拟机进行优化。
即一个Android进程,对应一个Dalvid进程,负责运行Android应用程序。
Dalvid的特点如下:
(a)运行专有的.dex文件。专有的.dex文件减少了.class文件中的冗余信息,而且会把所有.class文件整合到一个文件中,从而提高运行性能。DX工具还会对.dex文件进行一些性能的优化
(b)基于寄存器实现。大多数虚拟机(包括JVM)都是基于栈(内部存储器)的,而Dalvid虚拟机则是基于寄存器的。一般来说,基于寄存器的虚拟机具有更好的性能表现,但在硬件通用性上略差。
与JVM虚拟机区别如下:
(1)Dalvid并未完全遵守JVM规范,两种是不兼容的;
(2)JVM虚拟机运行的Java字节码(.class文件),但是Dalvid运行的是其专有的dex(Dalvid Executable)文件。JVM直接从.class文件或JAR包中加载字节码然后运行,而Dalvid则无法直接从.class文件或JAR包中加载字节码,它需要通过DX工具将应用程序的所有.class文件编译成.dex文件,Dalvid则运行该.dex文件;
(3)传统的JVM是基于堆栈的,而Dalvid虚拟机是基于寄存器的。因此,Dalvid虚拟机上运行的Java程序要比在传统的JVM上运行的Java程序速度更快。
(4)Dalvid虚拟机不需要很快的CPU计算速度和大量的内存空间,因此,Dalvid虚拟机非常适合在移动终端上使用,相对于在PC或服务器上运行的虚拟机(如JVM)而言,
21.Anroid线程的创建方式有哪些及其原理?
(1)AsyncTask实现原理
AsyncTask(异步任务),是android提供的轻量级封装过的后台任务类,AsyncTask异步任务允许开发者定义一个运行在单独线程中的任务,还能在任务的不同阶段提供回调函数。AsyncTask的本质是一个线程池,所有提交的异步任务都会在这个静态线程池中的工作线程内执行doInBackground(mParams)方法(空方法,由程序员实现),当工作线程需要跟UI线程交互时,工作线程会通过向在UI线程创建的Handler(AsyncTask内部的InternalHandler)传递消息的方式,调用相关的回调函数,从而实现UI界面的更新。
优点:AsyncTask实现容易,通过AsyncTask类开发者可以很容易在其他线程中执行耗时任务,也可以在需要时很方便地和主线程(UI线程)通信。
缺点:
◆该类的实例只能使用一次,每次执行操作都要新建一个MyAsyncTask对象。因此,它不适合哪些频繁的操作,因为这么快速聚集需要垃圾回收的对象,并最终导致应用程序卡顿。◆AsyncTask不能对操作设置执行时间,也无法间隔一段时间执行操作,它适合文件下载。
◆最大并发线程数目不超过5个,因为在使用多个异步操作并需要进行UI通信时,会变得很复杂。
(2)Handler消息传递机制
Handler消息传递机制的实质是: Looper、Handler、MessageQueue三者共同实现Android系统中的消息处理机制(即线程间通信)。如在A、B两个子线程之间需要传递消息,首先给每个子线程绑定一套handler、looper、messageQueue机制,然后这三个对象都与其所属线程对应。然后A线程通过调用B线程的Handler对象,发送消息。Android消息机制中引入了消息池,子线程通过Handler对象创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例,这个消息会被Handler发送到B线程的messageQueue中,而属于B线程的Looper对象一直在for循环里无限遍历MessageQueue,一旦发现该消息队列里收到了新的消息,就会去对消息进行处理,处理过程中会回调对应的Handler的handleMessage方法(程序开发人员实现),从而实现了不同线程间通信问题。
a)Looper实现原理
Looper类里包含一个消息队列对象和一个线程对象。当创建Looper时,会自动创建一个消息队列,同时将内部线程对象指向创建Looper的线程。当开启Looper后(looper.loop())会自动进入无限for循环中,不断去遍历消息队列,如果没有消息则阻塞,有消息则回调handler的handleMessage()方法进行处理。
b)Looper.prepare()
首先,要使用Looper机制一般会在当前线程中创建Handler对象,里面会自动创建一个looper对象和消息队列,这里面的消息队列属于当前线程空间。但此时的looper还不会遍历消息队列,也没有绑定到当前线程。其中,looper对象内部包含一个空消息队列对象和空线程。通过Looper.prepare()方法,先让该消息队列指向当前线程的消息队列,让空线程也指向当前线程,从而实现了绑定。
优点:
◆代码结构清晰,功能定义明确,当需频繁地控制在一个单独的线程中执行操作时,Handler类会是一个很有用的工具。
◆该类允许开发这准确地控制操作的执行时间,还可以重复多次使用它。执行操作的线程会一直运行,直到被显式的终止,适合操作频繁且需要快速间隔地执行操作的情况。
缺点:执行单任务操作时,代码较为复杂。
(3)线程池(Thread pool)
ExecutorService适合处理并行运行的多个任务,非常适合编写响应多客户端的服务器应用