1. 请描述下Activity的生命周期。
2. 如果后台的Activity由于某原因被系统回收了,如何在被系统回收之前保存当前状态?
3. 如何将一个Activity设置成窗口的样式。(Edited by Sodino)
4. 如何退出Activity?如何安全退出已调用多个Activity的Application?
5. 请介绍下Android中常用的五种布局。
6. 请介绍下Android的数据存储方式。(Edited by Sodino)
7. 请介绍下ContentProvider是如何实现数据共享的。(Edited by Sodino)
8. 如何启用Service,如何停用Service。(Edited by Sodino)
9. 注册广播有几种方式,这些方式有何优缺点?请谈谈Android引入广播机制的用意。
10. 请解释下在单线程模型中Message、Handler、Message Queue、Looper之间的关系。
11. AIDL的全称是什么?如何工作?能处理哪些类型的数据?
12. 请解释下Android程序运行时权限与文件系统权限的区别。(Edited by Sodino)
13. 系统上安装了多种浏览器,能否指定某浏览器访问指定页面?请说明原由。
14. 有一个一维整型数组int[]data保存的是一张宽为width,高为height的图片像素值信息。请写一个算法,将该图片所有的白色不透明(0xffffffff)像素点的透明度调整为50%。
15. 你如何评价Android系统?优缺点。
1. 请描述下Activity的生命周期
http://weizhulin.blog.51cto.com/1556324/311495
详细介绍一下这几个方法中系统在做什么以及我们应该做什么:
onCreate: 在这里创建界面 ,做一些数据的初始化工作
onStart: 到这一步变成用户可见不可交互 的
onResume: 变成和用户可交互 的,(在activity 栈系统通过栈的方式管理这些个
Activity的最上面,运行完弹出栈,则回到上一个Activity)
onPause: 到这一步是可见但不可交互 的,系统会停止动画 等消耗CPU 的事情
从上文的描述已经知道,应该在这里保存你的一些数据,因为这个时候
你的程序的优先级降低,有可能被系统收回。在这里保存的数据,应该在
onResume里读出来,注意:这个方法里做的事情时间要短,因为下一
个activity不会等到这个方法完成才启动
onstop: 变得不可见 ,被下一个activity覆盖了
onDestroy: 这是activity被干掉前最后一个被调用方法了,可能是外面类调用finish方
法或者是系统为了节省空间将它暂时性的干掉,可以用isFinishing()来判
断它,如果你有一个Progress Dialog在线程中转动,请在onDestroy里
把他cancel掉,不然等线程结束的时候,调用Dialog的cancel方法会抛
异常的。
onPause,onstop, onDestroy,三种状态 下 activity都有可能被系统干掉
为了保证程序的正确性,你要在onPause()里写上持久层操作的代码,将用户编辑的内容都保存到存储介质上(一般都是数据库)。实际工作中因为生命周期的变化而带来的问题也很多,比如你的应用程序起了新的线程在跑,这时候中断了,你还要去维护那个线程,是暂停还是杀掉还是数据回滚,是吧?因为Activity可能被杀掉,所以线程中使用的变量和一些界面元素就千万要注意了,一般我都是采用Android的消息机制 [Handler,Message]来处理多线程和界面交互的问题。这个我后面会讲一些,最近因为这些东西头已经很大了,等我理清思绪再跟大家分享。
2. 如果后台的Activity由于某原因被系统回收了,如何在被系统回收之前保存当前状态? 当你的程序中某一个Activity A 在运行时中,主动或被动地运行另一个新的Activity B 这个时候A会执行
Java代码
B 完成以后又会来找A, 这个时候就有两种情况,一种是A被回收,一种是没有被回收,被回
收的A就要重新调用onCreate()方法,不同于直接启动的是这回onCreate()里是带上参数
savedInstanceState,没被收回的就还是onResume就好了。
savedInstanceState是一个Bundle对象,你基本上可以把他理解为系统帮你维护的一个Map对象。在onCreate()里你可能会用到它,如果正常启动onCreate就不会有它,所以用的时候要判断一下是否为空。
Java代码
if(savedInstanceState != null){
long id = savedInstanceState.getLong("id");
}
if(savedInstanceState != null){ long id = savedInstanceState.getLong("id");}
就像官方的Notepad教程里的情况,你正在编辑某一个note,突然被中断,那么就把这个note的id记住,再起来的时候就可以根据这个id去把那个note取出来,程序就完整一些。这也是看你的应用需不需要保存什么,比如你的界面就是读取一个列表,那就不需要特殊记住什么,哦,没准你需要记住滚动条的位置...
3. 如何将一个Activity设置成窗口的样式
简单你只需要设置 一下Activity的主题就可以了在AndroidManifest.xml 中定义 Activity的
Xml代码
这就使你的应用程序变成对话框的形式弹出来了,或者
Xml代码
就变成半透明的,[友情提示-.-]类似的这种activity的属性可以在android.R.styleable 类的AndroidManifestActivity 方法中看到,AndroidManifest.xml中所有元素的属性的介绍都可以参考这个类android.R.styleable
上面说的是属性名称,具体有什么值是在android.R.style中可以看到,比如这个"@android:style/Theme.Dialog" 就对应于android.R.style.Theme_Dialog ,('_'换成'.' <--注意:这个是文章内容不是笑脸)就可以用在描述文件中了,找找类定义和描述文件中的对应关系就都明白了。
4. 如何退出Activity 对于单一Activity的应用来说,退出很简单,直接finish()即可。
当然,也可以用killProcess()和System.exit()这样的方法。 现提供几个方法,供参考:
1、抛异常强制退出:
该方法通过抛异常,使程序Force Close。
验证可以,但是,需要解决的问题是,如何使程序结束掉,而不弹出Force Close的窗口。
2、记录打开的Activity:
每打开一个Activity,就记录下来。在需要退出时,关闭每一个Activity即可。
3、发送特定广播:
在需要结束应用时,发送一个特定的广播,每个Activity收到广播后,关闭即可。
4、递归退出
在打开新的Activity时使用startActivityForResult,然后自己加标志,在onActivityResult中处理,递归关闭。
除了第一个,都是想办法把每一个Activity都结束掉,间接达到目的。
但是这样做同样不完美。
你会发现,如果自己的应用程序对每一个Activity都设置了nosensor,在两个Activity结束的间隙,sensor可能有效了。
但至少,我们的目的达到了,而且没有影响用户使用。
为了编程方便,最好定义一个Activity基类,处理这些共通问题。 5.请介绍下Android中常用的五种布局 Android布局是应用界面开发的重要一环,在Android中,共有五种布局方式,分别是:FrameLayout(框架布 局),LinearLayout (线性布局),AbsoluteLayout(绝对布局),RelativeLayout(相对布局),TableLayout(表格布局)。
一、FrameLayout
这个布局可以看成是墙脚堆东西,有一个四方的矩形的左上角墙脚,我们放了第一个东西,要再放一个,那就在放在原来放的位置的上面,这样依次的放,会盖住原来的东西。这个布局比较简单,也只能放一点比较简单的东西。
二、LinearLayout
线性布局,这个东西,从外框上可以理解为一个div,他首先是一个一个从上往下罗列在屏幕上。每一个LinearLayout里面又可分为垂直布局 (android:orientation="vertical")和水平布局(android:orientation="horizontal" )。当垂直布局时,每一行就只有一个元素,多个元素依次垂直往下;水平布局时,只有一行,每一个元素依次向右排列。
linearLayout中有一个重要的属性 android:layout_weight="1",这个weight在垂直布局时,代表行距;水平的时候代表列宽;weight值越大就越大。
三、AbsoluteLayout
绝对布局犹如div指定了absolute属性,用X,Y坐标来指定元素的位置android:layout_x="20px" android:layout_y="12px" 这种布局方式也比较简单,但是在垂直随便切换时,往往会出问题,而且多个元素的时候,计算比较麻烦。
四、RelativeLayout
相对布局可以理解为某一个元素为参照物,来定位的布局方式。主要属性有:
相对于某一个元素
android:layout_below="@id/aaa" 该元素在 id为aaa的下面
android:layout_toLeftOf="@id/bbb" 改元素的左边是bbb
相对于父元素的地方
android:layout_alignParentLeft="true" 在父元素左对齐
android:layout_alignParentRight="true" 在父元素右对齐
还可以指定边距等,具体详见API
五。TableLayout
表格布局类似Html里面的Table。每一个TableLayout里面有表格行TableRow,TableRow里面可以具体定义每一个元素,设定他的对齐方式 android:gravity="" 。
每一个布局都有自己适合的方式,另外,这五个布局元素可以相互嵌套应用,做出美观的界面。 6. 请介绍下Android的数据存储方式
Android提供了5种方式存储数据:
--使用SharedPreferences存储数据;
--文件存储数据;
--SQLite数据库存储数据;
--使用ContentProvider存储数据;
--网络存储数据;
先 说下,Preference,File, DataBase这三种方式分别对应的目录是/data/data/Package Name/Shared_Pref, /data/data/Package Name/files, /data/data/Package Name/database 。
在Android中通常使用File存储方式是用Context.openFileOutput(String fileName, int mode)和Context.openFileInput(String fileName)。
Context.openFileOutput(String fileName, int mode)生成的文件自动存储在/data/data/Package Name/files目录下,其全路径是/data/data/Package Name/files/fileName 。注意下,这里的参数fileName不可以包含路径分割符(如"/")。
通常来说,这种方式生成的文件只能在这个apk内访问。但这个结论是指使用Context.openFileInput(String fileName)的方式。使用这种方式,每个apk只可以访问自己的/data/data/Package Name/files目录下的文件,原因很简单,参数fileName中不可以包含路径分割符,Android会自动在/data/data /Package Name/files目录下寻找文件名为fileName的文件。
一:使用SharedPreferences存储数据
首先说明SharedPreferences存储方式,它是Android提供的用来存储一些简单配置信息的一种机制,例如:登录用户的用户名与密码。其采用了Map数据结构来存储数据,以键值的方式存储,可以简单的读取与写入,具体实例如下:
void ReadSharedPreferences(){
String strName,strPassword;
SharedPreferences user = getSharedPreferences(“user_info”,0);
strName = user.getString(“NAME”,””);
strPassword = user getString(“PASSWORD”,””);
}
void WriteSharedPreferences(String strName,String strPassword){
SharedPreferences user = getSharedPreferences(“user_info”,0);
uer.edit();
user.putString(“NAME”, strName);
user.putString(“PASSWORD” ,strPassword);
user.commit();
}
数据读取与写入的方法都非常简单,只是在写入的时候有些区别:先调用edit()使其处于编辑状态,然后才能修改数据,最后使用commit()提交修改的数据。实际上SharedPreferences是采用了XML格式将数据存储到设备中,在DDMS中的File Explorer中的/data/data/<package name>/shares_prefs下。以上面的数据存储结果为例,打开后可以看到一个user_info.xml的文件,打开后可以看到:
<?xml version=”1.0″ encoding=”UTF-8″?>
<map>
<string name=”NAME”>moandroid</string>
<string name=” PASSWORD”>SharedPreferences</string>
</map>
使用SharedPreferences是有些限制的:只能在同一个包内使用,不能在不同的包之间使用。
二:文件存储数据
文件存储方式是一种较常用的方法,在Android中读取/写入文件的方法,与 Java中实现I/O的程序是完全一样的,提供了openFileInput()和openFileOutput()方法来读取设备上的文件。 FilterInputStream, FilterOutputStream等可以到Java io package说明中去详细学习,不再此详细说明,具体实例如下:
String fn = “moandroid.log”;
FileInputStream fis = openFileInput(fn);
FileOutputStream fos = openFileOutput(fn,Context.MODE_PRIVATE);
除此之外,Android还提供了其他函数来操作文件,详细说明请阅读Android SDK。
三:网络存储数据
网络存储方式,需要与Android 网络数据包打交道,关于Android 网络数据包的详细说明,请阅读Android SDK引用了Java SDK的哪些package?。
四:ContentProvider
1、ContentProvider简介
当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读写数据。而使用ContentProvider共享数据的好处是统一了数据访问方式。?
2、Uri类简介
Uri代表了要操作的数据,Uri主要包含了两部分信息:1.需要操作的ContentProvider ,2.对ContentProvider中的什么数据进行操作,一个Uri由以下几部分组成:
1.scheme:ContentProvider(内容提供者)的scheme已经由Android所规定为:content://。
2.主机名(或Authority):用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。
3.路径(path):可以用来表示我们要操作的数据,路径的构建应根据业务而定,如下:
? 要操作contact表中id为10的记录,可以构建这样的路径:/contact/10
? 要操作contact表中id为10的记录的name字段, contact/10/name
? 要操作contact表中的所有记录,可以构建这样的路径:/contact?
要操作的数据不一定来自数据库,也可以是文件等他存储方式,如下:
要操作xml文件中contact节点下的name节点,可以构建这样的路径:/contact/name
如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:
Uri uri = Uri.parse("content://com.changcheng.provider.contactprovider/contact")
3、UriMatcher、ContentUrist和ContentResolver简介
因为Uri代表了要操作的数据,所以我们很经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher 和ContentUris 。掌握它们的使用,会便于我们的开发工作。
? UriMatcher:用于匹配Uri,它的用法如下:
1.首先把你需要匹配Uri路径全部给注册上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码(-1)。
UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://com.changcheng.sqlite.provider.contactprovider/contact路径,返回匹配码为1
uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact”, 1);//添加需要匹配uri,如果匹配就会返回匹配码
//如果match()方法匹配 content://com.changcheng.sqlite.provider.contactprovider/contact/230路径,返回匹配码为2
uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact/#”, 2);//#号为通配符
2.注册完需要匹配的Uri后,就可以使用uriMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用 addURI()方法传入的第三个参数,假设匹配 content://com.changcheng.sqlite.provider.contactprovider/contact路径,返回的匹配码为1。
?
ContentUris:用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
? withAppendedId(uri, id)用于为路径加上ID部分
? parseId(uri)方法用于从路径中获取ID部分
? ContentResolver:当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用 ContentResolver 类来完成,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。 ContentResolver使用insert、delete、update、query方法,来操作数据。
五:总结说明
以上5中存储方式,在以后的开发过程中,根据设计目标、性能需求、空间需求等找到合适的数据存储方式。Android 中的数据存储都是私有的,其他应用程序都是无法访问的,除非通过ContentResolver获取其他程序共享的数据。采用文件方式对外共享数据,需要进行文件操作读写数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读写数据。而使用ContentProvider共享数据的好处是统一了数据访问方式。
8.如何启用Service,如何停用Service
Android中的服务和windows中的服务是类似的东西,服务一般没有用户操作界面,它运行于系统中不容易被用户发觉,可以使用它开发如监控之类的程序。服务的开发比较简单,如下:
第一步:继承Service类
public class SMSService extends Service {
}
第二步:在AndroidManifest.xml文件中的<application>节点里对服务进行配置:
<service android:name=".SMSService" />
服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可以启动Service,但是它们的使用场合有所不同。使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。
如果打算采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。
如果打算采用Context.bindService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onBind()方法。这个时候调用者和服务绑定在一起,调用者退出了,系统就会先调用服务的onUnbind()方法,接着调用onDestroy()方法。如果调用bindService()方法前服务已经被绑定,多次调用bindService()方法并不会导致多次创建服务及绑定(也就是说onCreate()和onBind()方法并不会被多次调用)。如果调用者希望与正在绑定的服务解除绑定,可以调用unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()-->onDestroy()方法。
onCreate() 该方法在服务被创建时调用,该方法只会被调用一次,无论调用多少次startService()或bindService()方法,服务也只被创建一次。
onDestroy()该方法在服务被终止时调用。
与采用Context.startService()方法启动服务有关的生命周期方法
onStart() 只有采用Context.startService()方法启动服务时才会回调该方法。该方法在服务开始运行时被调用。多次调用startService()方法尽管不会多次创建服务,但onStart() 方法会被多次调用。
与采用Context.bindService()方法启动服务有关的生命周期方法
onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。
onUnbind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务解除绑定时被调用
采用Context.startService()方法启动服务的代码如下:
public class HelloActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
......
Button button =(Button) this.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
Intent intent = new Intent(HelloActivity.this, SMSService.class);
startService(intent);
}});
}
}
采用Context. bindService()方法启动服务的代码如下:
public class HelloActivity extends Activity {
ServiceConnection conn = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
}
public void onServiceDisconnected(ComponentName name) {
}
};
@Override public void onCreate(Bundle savedInstanceState) {
Button button =(Button) this.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
Intent intent = new Intent(HelloActivity.this, SMSService.class);
bindService(intent, conn, Context.BIND_AUTO_CREATE);
//unbindService(conn);//解除绑定
}});
}
}
9. 注册广播有几种方式,这些方式有何优缺点?请谈谈Android引入广播机制的用意。
Android 的广播机制
在 Android 里面有各种各样的广播,比如电池的使用状态,电话的接收和短信的接收都会产生一个广播,应用程序开发者也可以监听这些广播并做出程序逻辑的处理。下面我画一张粗略的图来帮助大家理解广播的运行机制。
Android 中有各式各样的广播,各种广播在Android 系统中运行,当系统/应用程序运行时便会向 Android 注册各种广播,Android 接收到广播会便会判断哪种广播需要哪种事件,然后向不同需要事件的应用程序注册事件,不同的广播可能处理不同的事件也可能处理相同的广播事件,这时就需要 Android 系统为我们做筛选。
案例分析:
一个经典的电话黑名单,首先通过将黑名单号码保存在数据库里面,当来电时,我们接收到来电广播并将黑名单号码与数据库中的某个数据做匹配,如果匹配的话则做出相应的处理,比如挂掉电话、比如静音等等。。。
Demo 分析:
下面通过一个小DEMO 来讲解一下广播在Android 中如何编写,在Demo中我们设置了一个按钮为按钮设置点击监听通过点击发送广播,在后台中接收到广播并打印LOG信息。代码如下:
BroadCastActivity 页面代码
public class BroadCastActivity extends Activity {
public static final String ACTION_INTENT_TEST = "com.terry.broadcast.test";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn = (Button) findViewById(R.id.Button01);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent(ACTION_INTENT_TEST);
sendBroadcast(intent);
}
});
}
}
接收器代码如下:
public class myBroadCast extends BroadcastReceiver {
public myBroadCast() {
Log.v("BROADCAST_TAG", "myBroadCast");
}
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Log.v("BROADCAST_TAG", "onReceive");
}
}
Android 广播的生命周期 在上面的接收器中,继承了BroadcastReceiver 并重写了它的onReceive 并构造了一个函数,下面通过图片来一步一步认识 Android 广播的生命周期。当我点击一下按钮,它向Android 发送了一个广播,如下图: 这时我们再点击一下按钮,它还是会再向 Android 系统发送广播,此时日志信息如下: 下面本人画一张图像,描述了Android 中广播的生命周期,其次它并不像Activity 一样复杂,运行原理很简单如下图: 下面来看一下SDK给出的解释: 大意为:如果一个广播处理完onReceive 那么系统将认定此对象将不再是一个活动的对象,也就会finished掉它。 至此,大家应该能明白 Android 的广播生命周期的原理,代码也不用多介绍,很简单的一个发送广播并处理广播的Demo。 Android 如何判断并筛选广播? 前 面说过 Android 的广播有各式各样,那么Android 系统是如何帮我们处理我们需要哪种广播并为我们提供相应的广播服务呢?这里有一点需要大家注意,每实现一个广播接收类必须在我们应用程序中的 manifest 中显式的注明哪一个类需要广播,并为其设置过滤器,如下图: Tip:action 代表一个要执行的动作,在Andriod 中有很action 比如 ACTION_VIEW,ACTION_EDIT
那么有些人会问了,如果我在一个广播接收器中要处理多个动作呢?那要如何去处理?
在Android 的接收器中onReceive 以经为我们想到的,同样的你必须在Intent-filter 里面注册该动作,可以是系统的广播动作也可以是自己需要的广播,之后你之需要在onReceive 方法中,通过intent.getAction()判断传进来的动作即可做出不同的处理,不同的动作。具体大家可以去尝试测试一下。
小结:
Android广播机制(两种注册方法)
在android下,要想接受广播信息,那么这个广播接收器就得我们自己来实现了,我们可以继承BroadcastReceiver,就可以有一个广播接受器了。有个接受器还不够,我们还得重写BroadcastReceiver里面的onReceiver方法,当来广播的时候我们要干什么,这就要我们自己来实现,不过我们可以搞一个信息防火墙。具体的代码:
public class SmsBroadCastReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
Bundle bundle = intent.getExtras();
Object[] object = (Object[])bundle.get("pdus");
SmsMessage sms[]=new SmsMessage[object.length];
for(int i=0;i<object.length;i++)
{
sms[0] = SmsMessage.createFromPdu((byte[])object[i]);
Toast.makeText(context, "来自"+sms[i].getDisplayOriginatingAddress()+" 的消息是:"+sms[i].getDisplayMessageBody(), Toast.LENGTH_SHORT).show();
}
//终止广播,在这里我们可以稍微处理,根据用户输入的号码可以实现短信防火墙。
abortBroadcast();
}
}
当实现了广播接收器,还要设置广播接收器接收广播信息的类型,这里是信息:android.provider.Telephony.SMS_RECEIVED
我们就可以把广播接收器注册到系统里面,可以让系统知道我们有个广播接收器。这里有两种,一种是代码动态注册:
//生成广播处理
smsBroadCastReceiver = new SmsBroadCastReceiver();
//实例化过滤器并设置要过滤的广播
IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
//注册广播
BroadCastReceiverActivity.this.registerReceiver(smsBroadCastReceiver, intentFilter);
一种是在AndroidManifest.xml中配置广播
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="spl.broadCastReceiver"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".BroadCastReceiverActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!--广播注册-->
<receiver android:name=".SmsBroadCastReceiver">
<intent-filter android:priority="20">
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
</application>
<uses-sdk android:minSdkVersion="7" />
<!-- 权限申请 -->
<uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
</manifest>
两种注册类型的区别是:
1)第一种不是常驻型广播,也就是说广播跟随程序的生命周期。
2)第二种是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。
BroadcastReceiver用于监听被广播的事件
必须被注册,有两种方法:
1、在应用程序的代码中注册
注册BroadcastReceiver:
registerReceiver(receiver,filter);
取消注册BroadcastReceiver:
unregisterReceiver(receiver);
当BroadcastReceiver更新UI,通常会使用这样的方法注册。启动Activity时候注册BroadcastReceiver,Activity不可见时候,取消注册。
2、在androidmanifest.xml当中注册
<receiver>
<intent-filter>
<action android:name = "android.intent.action.PICK"/>
</intent-filter>
</receiver>
使用这样的方法注册弊端:它会始终处于活动状态,毕竟是手机开发,cpu和电源资源比较少,一直处于活动耗费大,不利。
10. 请解释下在单线程模型中Message、Handler、Message Queue、Looper之间的关系。
1. Android进程
在了解Android线程之前得先了解一下Android的进程。当一个程序第一次启动的时候,Android会启动一个LINUX进程和一个主线程。默认的情况下,所有该程序的组件都将在该进程和线程中运行。
同时,Android会为每个应用程序分配一个单独的LINUX用户。Android会尽量保留一个正在运行进程,只在内存资源出现不足时,Android 会尝试停止一些进程从而释放足够的资源给其他新的进程使用,也能保证用户正在访问的当前进程有足够的资源去及时地响应用户的事件。Android会根据进程中运行的组件类别以及组件的状态来判断该进程的重要性,Android会首先停止那些不重要的进程。按照重要性从高到低一共有五个级别:
前台进程
前台进程是用户当前正在使用的进程。只有一些前台进程可以在任何时候都存在。他们是最后一个被结束的,当内存低到根本连他们都不能运行的时候。一般来说,在这种情况下,设备会进行内存调度,中止一些前台进程来保持对用户交互的响应。
可见进程
可见进程不包含前台的组件但是会在屏幕上显示一个可见的进程是的重要程度很高,除非前台进程需要获取它的资源,不然不会被中止。
服务进程
运 行着一个通过startService() 方法启动的service,这个service不属于上面提到的2种更高重要性的。service所在的进程虽然对用户不是直接可见的,但是他们执行了用户非常关注的任务(比如播放mp3,从网络下载数据)。只要前台进程和可见进程有足够的内存,系统不会回收他们。
后台进程
运 行着一个对用户不可见的activity(调用过 onStop() 方法).这些进程对用户体验没有直接的影响,可以在服务进程、可见进程、前台进程需要内存的时候回收。通常,系统中会有很多不可见进程在运行,他们被保存在LRU (least recently used) 列表中,以便内存不足的时候被第一时间回收。如果一个activity正确的执行了它的生命周期,关闭这个进程对于用户体验没有太大的影响。
空进程
未运行任何程序组件。运行这些进程的唯一原因是作为一个缓存,缩短下次程序需要重新使用的启动时间。系统经常中止这些进程,这样可以调节程序缓存和系统缓存的平衡。
Android 对进程的重要性评级的时候,选取它最高的级别。另外,当被另外的一个进程依赖的时候,某个进程的级别可能会增高。一个为其他进程服务的进程永远不会比被服务的进程重要级低。因为服务进程比后台activity进程重要级高,因此一个要进行耗时工作的activity最好启动一个service来做这个工作,而不是开启一个子进程――特别是这个操作需要的时间比activity存在的时间还要长的时候。例如,在后台播放音乐,向网上上传摄像头拍到的图片,使用service可以使进程最少获取到“服务进程”级别的重要级,而不用考虑activity目前是什么状态。broadcast receivers做费时的工作的时候,也应该启用一个服务而不是开一个线程。
2. 单线程模型
当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。
2.1 子线程更新UI
Android的UI是单线程(Single-threaded)的。为了避免拖住GUI,一些较费时的对象应该交给独立的线程去执行。如果幕后的线程来执行UI对象,Android就会发出错误讯息
CalledFromWrongThreadException。以后遇到这样的异常抛出时就要知道怎么回事了!
2.2 Message Queue
在单线程模型下,为了解决类似的问题,Android设计了一个Message Queue(消息队列), 线程间可以通过该Message Queue并结合Handler和Looper组件进行信息交换。下面将对它们进行分别介绍:
1. Message
Message消息,理解为线程间交流的信息,处理数据后台线程需要更新UI,则发送Message内含一些数据给UI线程。
2. Handler
Handler处理者,是Message的主要处理者,负责Message的发送,Message内容的执行处理。后台线程就是通过传进来的 Handler对象引用来sendMessage(Message)。而使用Handler,需要implement 该类的 handleMessage(Message)
方法,它是处理这些Message的操作内容,例如Update UI。通常需要子类化Handler来实现handleMessage方法。
3. Message Queue
Message Queue消息队列,用来存放通过Handler发布的消息,按照先进先出执行。
每个message queue都会有一个对应的Handler。Handler会向message queue通过两种方法发送消息:sendMessage或post。这两种消息都会插在message queue队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过sendMessage发送的是一个message对象,会被 Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。
4. Looper
Looper是每条线程里的Message Queue的管家。Android没有Global的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper() 得到当前线程的Looper就有可能为NULL。
对于子线程使用Looper,API Doc提供了正确的使用方法:
这个Message机制的大概流程:
1. 在Looper.loop()方法运行开始后,循环地按照接收顺序取出Message Queue里面的非NULL的Message。
2. 一开始Message Queue里面的Message都是NULL的。当Handler.sendMessage(Message)到Message Queue,该函数里面设置了那个Message对象的target属性是当前的Handler对象。随后Looper取出了那个Message,则调用该Message的target指向的Hander的dispatchMessage函数对Message进行处理。
在dispatchMessage方法里,如何处理Message则由用户指定,三个判断,优先级从高到低:
1) Message里面的Callback,一个实现了Runnable接口的对象,其中run函数做处理工作;
2) Handler里面的mCallback指向的一个实现了Callback接口的对象,由其handleMessage进行处理;
3) 处理消息Handler对象对应的类继承并实现了其中handleMessage函数,通过这个实现的handleMessage函数处理消息。
由此可见,我们实现的handleMessage方法是优先级最低的!
3. Handler处理完该Message (update UI) 后,Looper则设置该Message为NULL,以便回收!
在网上有很多文章讲述主线程和其他子线程如何交互,传送信息,最终谁来执行处理信息之类的,个人理解是最简单的方法——判断Handler对象里面的Looper对象是属于哪条线程的,则由该线程来执行!
1. 当Handler对象的构造函数的参数为空,则为当前所在线程的Looper;
2. Looper.getMainLooper()得到的是主线程的Looper对象,Looper.myLooper()得到的是当前线程的Looper对象。
现在来看一个例子,模拟从网络获取数据,加载到ListView的过程:
这个例子,我自己写完后觉得还是有点乱,要稍微整理才能看明白线程间交互的过程以及数据的前后变化。随后了解到AsyncTask类,相应修改后就很容易明白了!
11. AIDL的全称是什么?如何工作?能处理哪些类型的数据 详情请参看:http://buaadallas.blog.51cto.com/399160/372090 部分概念: 在Android中, 每个应用程序都可以有自己的进程. 在写UI应用的时候, 经常要用到Service. 在不同的进程中, 怎样传递对象呢? 显然, Java中不允许跨进程内存共享. 因此传递对象, 只能把对象拆分成操作系统能理解的简单形式, 以达到跨界对象访问的目的. 在J2EE中,采用RMI的方式, 可以通过序列化传递对象. 在Android中, 则采用AIDL的方式. 理论上AIDL可以传递Bundle,实际上做起来却比较麻烦.
AIDL(AndRoid接口描述语言)是一种借口描述语言; 编译器可以通过aidl文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程的目的. 如果需要在一个Activity中, 访问另一个Service中的某个对象, 需要先将对象转化成AIDL可识别的参数(可能是多个参数), 然后使用AIDL来传递这些参数, 在消息的接收端, 使用这些参数组装成自己需要的对象.
AIDL的IPC的机制和COM或CORBA类似, 是基于接口的,但它是轻量级的。它使用代理类在客户端和实现层间传递值. 如果要使用AIDL, 需要完成2件事情: 1. 引入AIDL的相关类.; 2. 调用aidl产生的class.
AIDL的创建方法:
AIDL语法很简单,可以用来声明一个带一个或多个方法的接口,也可以传递参数和返回值。由于远程调用的需要, 这些参数和返回值并不是任何类型.下面是些AIDL支持的数据类型:
1. 不需要import声明的简单Java编程语言类型(int,boolean等)
2. String, CharSequence不需要特殊声明
3. List, Map和Parcelables类型, 这些类型内所包含的数据成员也只能是简单数据类型, String等其他比支持的类型.
(另外: 我没尝试Parcelables, 在Eclipse+ADT下编译不过, 或许以后会有所支持).
12. 请解释下Android程序运行时权限与文件系统权限的区别
默认生成的APK文件是debug签名的。
获取system权限时用到的签名,见:如何使Android应用程序获取系统权限
(2)基于UserID的进程级别的安全机制
大家都知道,进程有独立的地址空间,进程与进程间默认是不能互相访问的,是一种很可靠的保护机制。
Android通过为每一个安装在设备上的包(apk)分配唯一的linux userID来实现,名称为"app_"加一个数字,比如app_43
不同的UserID,运行在不同的进程,所以apk之间默认便不能相互访问。
Android提供了如下的一种机制,可以使两个apk打破前面讲的这种壁垒。
在AndroidManifest.xml中利用sharedUserId属性给不同的package分配相同的userID,通过这样做,两个package可以被当做同一个程序,
系统会分配给两个程序相同的UserID。当然,基于安全考虑,两个package需要有相同的签名,否则没有验证也就没有意义了。
(这里补充一点:并不是说分配了同样的UserID,两程序就运行在同一进程, 下面为PS指令摘取的,
显然,system、app_2分别对应的两个进程的PID都不同,不知Android到底是怎样实现它的机制的)
User PID PPID
system 953 883 187340 55052 ffffffff afe0cbcc S system_server
app_2 1072 883 100264 19564 ffffffff afe0dcc4 S com.android.inputmethod.
system 1083 883 111808 23192 ffffffff afe0dcc4 S android.process.omsservi
app_2 1088 883 156464 45720 ffffffff afe0dcc4 S android.process.acore
(3)默认apk生成的数据对外是不可见的
实现方法是:Android会为程序存储的数据分配该程序的UserID。
借助于Linux严格的文件系统访问权限,便实现了apk之间不能相互访问似有数据的机制。
例:我的应用创建的一个文件,默认权限如下,可以看到只有UserID为app_21的程序才能读写该文件。
-rw------- app_21 app_21 87650 2000-01-01 09:48 test.txt
如何对外开放?
<1> 使用MODE_WORLD_READABLE and/or MODE_WORLD_WRITEABLE 标记。
When creating a new file with getSharedPreferences(String, int), openFileOutput(String, int), or openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory), you can use the MODE_WORLD_READABLE and/or MODE_WORLD_WRITEABLE flags to allow any other package to read/write the file. When setting these flags, the file is still owned by your application, but its global read and/or write permissions have been set appropriately so any other application can see it.
(4)AndroidManifest.xml中的显式权限声明
Android默认应用是没有任何权限去操作其他应用或系统相关特性的,应用在进行某些操作时都需要显式地去申请相应的权限。
一般以下动作时都需要申请相应的权限:
A particular permission may be enforced at a number of places during your program's operation:
在应用安装的时候,package installer会检测该应用请求的权限,根据该应用的签名或者提示用户来分配相应的权限。
在程序运行期间是不检测权限的。如果安装时权限获取失败,那执行就会出错,不会提示用户权限不够。
大多数情况下,权限不足导致的失败会引发一个 SecurityException, 会在系统log(system log)中有相关记录。
(5)权限继承/UserID继承
当我们遇到apk权限不足时,我们有时会考虑写一个linux程序,然后由apk调用它去完成某个它没有权限完成的事情,很遗憾,这种方法是行不通的。
前面讲过,android权限是经营在进程层面的,也就是说一个apk应用启动的子进程的权限不可能超越其父进程的权限(即apk的权限),
即使单独运行某个应用有权限做某事,但如果它是由一个apk调用的,那权限就会被限制。
实际上,android是通过给子进程分配父进程的UserID实现这一机制的。
(三)常见权限不足问题分析
首先要知道,普通apk程序是运行在非root、非system层级的,也就是说看要访问的文件的权限时,看的是最后三位。
另外,通过system/app安装的apk的权限一般比直接安装或adb install安装的apk的权限要高一些。
言归正传,运行一个android应用程序过程中遇到权限不足,一般分为两种情况:
(1)Log中可明显看到权限不足的提示。
此种情况一般是AndroidManifest.xml中缺少相应的权限设置,好好查找一番权限列表,应该就可解决,是最易处理的情况。
有时权限都加上了,但还是报权限不足,是什么情况呢?
Android系统有一些API及权限是需要apk具有一定的等级才能运行的。
比如 SystemClock.setCurrentTimeMillis()修改系统时间,WRITE_SECURE_SETTINGS权限好像都是需要有system级的权限才行。
也就是说UserID是system.
(2)Log里没有报权限不足,而是一些其他Exception的提示,这也有可能是权限不足造成的。
比如:我们常会想读/写一个配置文件或其他一些不是自己创建的文件,常会报java.io.FileNotFoundException错误。
系统认为比较重要的文件一般权限设置的也会比较严格,特别是一些很重要的(配置)文件或目录。
如
-r--r----- bluetooth bluetooth 935 2010-07-09 20:21 dbus.conf
drwxrwx--x system system 2010-07-07 02:05 data
dbus.conf好像是蓝牙的配置文件,从权限上来看,根本就不可能改动,非bluetooth用户连读的权利都没有。
/data目录下存的是所有程序的私有数据,默认情况下android是不允许普通apk访问/data目录下内容的,通过data目录的权限设置可知,其他用户没有读的权限。
所以adb普通权限下在data目录下敲ls命令,会得到opendir failed, Permission denied的错误,通过代码file.listfiles()也无法获得data目录下的内容。
上面两种情况,一般都需要提升apk的权限,目前我所知的apk能提升到的权限就是system(具体方法见:如何使Android应用程序获取系统权限),
至于是否有root级的,如何提升至root级不得而知,知道的朋友劳烦告知,感激不尽。
13. 系统上安装了多种浏览器,能否指定某浏览器访问指定页面?请说明原由。
13. 系统上安装了多种浏览器,能否指定某浏览器访问指定页面?请说明原由。