Android面试简录——组件3
四大应用程序组件
Android中的窗口:Activity
- 如何配置Activity才能让程序启动时将该Activity作为启动窗口?
在AndroidManifest.xml文件中指定MAIN动作。
<action android:name="android.intent.action.MAIN" /> - 请阐述Activity有哪几个生命周期方法以及在Activity现实和销毁的过程中生命周期方法执行的顺序。
protected void onCreate(Bundle savedInstanceState)
protected void onStart()
protected void onRestart()
protected void onResume()
protected void onPause()
protected void onStop()
protected void onDestroy()
Activity显示:onCreate -> onStart ->onResume.
Activity销毁:onPause -> onStop -> onDestroy.
onStop时Activity重新获得焦点:onRestart -> onStart -> onResume. - 调用Activity都有哪几种方法?
1.显式调用:直接指定Activity
2.隐式调用:通过Activity Action来调用Activity - 在Activity之间如何传递数据?
1.Intent对象:
Intent.putExtra ~ 设置要传递的数据
Intent.getXxxExtra ~ 获取传递的数据
2.静态变量:public static
3.剪切板:
数据保存:
Intent intent = new Intent(this, MyActivity.class);
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
clipboard.setText("数据");
startActivity(intent);
获取数据:
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
String text = clipboard.getText().toString();
4.全局对象:继承自android.app.Application类
package mobile.android.transmit.data;
import android.app.application;
public class MyApp extends Application {
public String country;
public Data data = new Data();
}
需要在AndroidManifest.xml中定义该类。
获取:
MyApp myApp = (MyApp) getApplicationContext();
【拓展】通过剪切板传递复杂数据
问题提出:高版本中支持剪切板保存Intent支持的类型,但是低版本中不支持。
解决办法:使用字符串传递任何二进制数据(包括可序列化对象、图像的等)。
1.将可序列化对象转换成Base64编码,然后保存到剪切板中:
Intent intent = new Intent(this, MyActivity3.class);
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
Data clipboardData = new Data();
clipboardData.id = 6666;
clipboardData.name = "通过Clipboard传递的数据";
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String base64Str = "";
try {
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(clipboardData);
base64Str = Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT);
oos.close();
} catch (Exception e) {
}
clipboard.setText(base64Str);
startActivity(intent);
2.从剪切板中获取Base64编码格式的字符串,并进行解码,最后还原成Data对象。
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
String base64Str = clipboard.getText().toString();
byte[] buffer = Base64.decode(base64Str, Base64.DEFAULT);
ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
try {
ObjectInputStream ois = new ObjectInputStream(bais);
Data data = (Data) ois.redObject();
textView.setText(base64Str + "\n\ndata.id:" + data.id + "\ndata.name" + data.name);
} catch (Exception e) {
}
- 请写出直接拨号、将电话号码传入拨号程序、调用拨号程序、调用系统浏览器浏览网页、调用系统程序查看联系人、显示系统设置界面和现实Wi-Fi设置界面的Java程序。
拨号代码:
Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:12345678"));
startActivity(callIntent);
将电话号码传入拨号程序:
Intent dialIntent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:87654321"));
startActivity(dialIntent);
调用拨号程序:
Intent touchDialerIntent = new Intent("com.android.phone.action.TOUCH_DIALER");
startActivity(touchDialerIntent);
调用系统浏览器网页:
Intent webIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://nokiaguy.blogjava.net"));
startActivity(webIntent);
调用系统程序查看联系人:
Intent contactListIntent = new Intent("com.android.contacts.action.LIST_CONTACTS");
startActivity(contactListIntent);
显示系统设置页面:
Intent settingIntent = new Intent("android.settings.SETTINGS");
startActivity(settingIntent);
显示Wi-Fi设置界面:
Intent wifiSettingsIntent = new Intent("android.settings.WIFI_SETTINGS");
startActivity(wifiSettingsIntent); - 如何将Activity变成半透明的对话框?
对话框:Theme.Dialog
半透明:android:windowBackground
1.定义一个新的主题:
<style name="MyTheme" parent="@android:style/Theme.Dialog">
<item name="android:windowBackground">@drawable/msg_background</item>
</style>
2.指定主题
<activity android:name=".Main" android:label="@string/appname" android:theme="@style/MyTheme">
- 如何设置Activity显示和关闭时的动画效果?
overridePendingTransition方法
1.在res/anim中建立对应的动画资源文件
2.在代码中添加动画效果:
Intent intent = new Intent(this, AnimationActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
广播接收器:Broadcast Receiver
- 如何接收广播?
接收单个广播:
1.编写广播接收类。从BroadcastReceiver或其子类继承。
2.在BroadcastReceiver.onReceive(Context context, Intent intent)方法中编写处理广播的代码。
*在AndroidManifest.xml文件中注册。
接收多个广播:
使用intent.getAction判断当前接收到的是哪个广播。
if ("action1".equals(intent.getAction())) {
...
}
if ("action2".equals(intent.getAction())) {
...
} - 如何获取短信内容?
1.编写广播接收器
onReceive方法如下:
public void onReceive(Context context, Intent intent) {
Bundle bundle = new intent.getExtras();
if (bundle != null) {
Object[] objArray = (Object[]) bundle.get("pdus");
SmsMessage[] messages = new SmsMessage[objArray.length];
for (int i = 0; i < objArray.length; i ++) {
messages[i] = SmsMessage.createFromPdu((byte[]) objArray[i]);
String s = "手机号:" + messages[i].getOriginationAddress() + "\n";
s += "短信内容:" = messages[i].getDisplayMessageBody();
Toast.makeText(context, s, Toast.LENGTH_LONG).show();
}
}
}
2.在AndroidManifest.xml文件中定义该广播接收器时添加短信广播Action。
<action android:name="android.provider.Telephony.SMS_RECEIVED" /> - 如何拦截来电,并在检测到某些特定号码时自动挂断电话?
编写一个广播接收类来拦截来电。
通过反射技术访问Android SDK的内部功能来挂断电话。
拦截来电的onReceive方法:
public void onReceive(final Context context, Intent intent) {
TelephonyManager tm = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE);
switch (tm.getCallState()) {
case TelephonyManager.CALL_STATE_RINGING:
String incomingNumber = intent.getStringExtra("incoming_number");
if ("12345678".equals(incomingNumber)) {
Class<TelephonyManager> telephonyManagerClass = TelephonyManager.class;
Method telephonyMethod = telephonyManagerClass.getDeclaredMethod("getITelephony", (Class[]) null);
telephonyMethod.setAccessible(true);
Object obj = telephonyMethod.invoke(tm, (Object[], null));
Method endCallMethod = obj.getCLass().getMethod("endCall", null);
endCallMethod.setAccessible(true);
endCallMethod.invoke(obj, null);
}
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Log.d("call_state", "offhook");
break;
case TelephonyManager.CALL_STATE_IDLE:
closeToast();
}
}
在AndroidManifest.xml文件中设置定义广播接收器:
<action android:name="android.intent.action.PHONE_STATE"/> - 如何拦截手机屏幕休眠和唤醒动作?
[注意] 该功能的广播接收器只能通过Java代码注册,不能在AndroidManifest.xml文件中注册。
通过如下两个Broadcast Action可以拦截屏幕休眠和唤醒动作:
休眠状态:Intent.ACTION_SCREEN_ON
唤醒状态:Intent.ACTION_SCREEN_OFF
注册广播接收器(ScreenOnOffReceiver)的代码如下:
ScreenOnOffReceiver screenOnOffReceiver = new ScreenOnOffReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(screenOnOffReceiver, intentFilter); - 如何让一个Acitivity在开机后自动显示?
使用广播接收器(StarupReceiver)拦截手机启动广播,然后在onReceive方法中打开Activity。
public void onReceive(Context context, Intent intent) {
Intent mainIntent = new Intent(context, Main.class);
mainIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(mainIntent);
}
注册:
<action android:name="android.intent.action.BOOT_COMPLETED"/>
* 如何发送广播?
sendBroadcast:
public void sendBroadcast(Intent intent);
服务:Service
- 请描述一下Service的生命周期。
3个生命周期。
public void onCreate(); //创建服务
public void onStart(Intent intent, int startId); //开始服务
public void onDestroy(); //销毁服务
首次创建:onCreate() -> onStart().
再次调用startService/使用startService开始一个已经停止的服务:onStart().
调用stopService:onDestroy(). - 请描述一下开发AIDL服务的步骤。
AIDL:允许一个应用程序访问另一个应用程序的对象。
建立AIDL服务的步骤:
1.建立aidl文件(Android工程的Java源文件目录)
2.如果aidl文件内容正确 -> 自动生成Java接口文件(*.java)
3.建立一个服务类
4.实现aidl文件生成的java接口
5.在AndroidManifest.xml文件中配置AIDL服务。[注意]<action>标签中android:name的属性值就是客户端要引用该服务的ID,也就是Intent类构造方法的参数值。 - AIDL服务支持哪些类型的数据?
1.Java的简单类型(int,char,boolean等)
2.String和CharSequence
3.List和Map
4.AIDL自动生成的接口类型
5.实现android.os.Parcelable接口的类
内容提供者:Content Provider
-
如何读取联系人信息?
ListView listView = (ListView) findViewById(R.id.listView);
Cursor cursor = getContentResolver().query(
ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
SimpleCursorAdapter simpleCursorAdapter = new SimpleCursorAdapter(
this, android.R.layout.simple_list_item_1, cursor,
new String[] {ContactsContract.Contacts.DISPLAY_NAME},
new int[] {android.R.id.text1});
listView.setAdapter(simpleCursorAdapter); -
如何查询收发的短信信息,以及只查收件箱和发件箱?
ListView lvShortMessages = (ListView) findViewById(R.id.lvShortMessages);
Cursor cursor = getContentResolver().query(
Uri.parse("content://sms"), null, "address like ?",
new String[] {"1%"}, null);
SMSAdapter smsAdapter = new SMSAdapter(this, cursor);
lvShortMessages.setAdapter(smsAdapter);
只查收件箱:"content://sms" -> "content://sms/inbox"
只查发件箱:"content://sms" -> "content://sms/outbox" -
请描述Content Provider URI有哪几部分组件?
四部分:
1.content:// ~ 相当于HTTP URI的http://
2.authority ~ 相当于HTTP URI的域名
3.路径(path)
4.参数(param)
举例:content://mobile.android.mydata/product/20
分析:authority:mobile.android.mydata
path:product
param:20 -
开发一个Content Provider的步骤。
1.编写一个类,继承自ContentProvider
2.实现ContentProvider中所有的抽象方法
3.定义Content Provider的URI
4.在static块中使用UriMatcher对象映射Uri和返回码
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "cities", 1);
uriMatcher.addURI(AUTHORITY, "code/#", 2);
uriMatcher.addURI(AUTHORITY, "cities_in_province/*", 3);
}
5.根须实际的需要实现相应的方法
6.实现query,insert,delete,update方法时要使用UriMatcher.match方法将URI映射成第4步与URI对应的代码(addURI方法的最后一个参数值)
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Cursor cursor = null;
switch (uriMatcher.match(uri)) {
case 1:
...
break;
case 2:
...
break;
default:
throw new IllegalArgumentException("<" + uri + ">格式不正确");
}
return cursor;
}
7.在AndroidManifest.xml文件中使用<provider>标签注册Content Provider。
<provider android:name="RegionContentProvider"
android:authorities="mobile.android.mydata" /> -
如何为Content Provider添加访问权限?
在AndroidManifest.xml文件中的<provider>设置相应的权限,并定义这个权限。
只读:readPermission
只写:wirtePermission
读写:permission
1.设置权限:
<provider
android:name="RegionContentProvider"
android:authorities="mobile.android.ch11.permission.regioncontentprovider"android:readPermission="mobile.android.ch11.permission.regioncontentprovider.READ_REGION" /> 2.定义权限: <permission android:name="mobile.android.ch11.permission.regioncontentprovider.READ_REGION" android:protectionLevel="normal" android:label="@string/label" android:description="@string/description" />