十、Service与BroadcastReceiver:
1.Service与Activity还有一点相似之处,它们都是从Context派生出来的,因此它们都可调用Context里定义的如getResources()、getContentResolver()等方法。
2.与配置Activity相似的是,配置Service时也可为
3.每当Service被创建时会回调onCreate方法,每次Service被启动时都会回调onStart方法——多次启动一个已有的Service组件将不会再回调onCreate方法,但每次启动时都会回调onStart方法。
4.当程序通过startService()和stopService()启动、关闭Service时,Service与访问者之间基本上不存在太多的关联,因此Service和访问者之间也无法进行通信、数据交换;如果Service和访问者之间需要进行方法调用或数据交换,则应该使用bindService()和unbindService()方法启动、关闭服务。
5.客户端访问Service时,Android并不是直接返回Service对象给客户端,Service只是将一个回调对象(IBinder对象)通过onBind()方法返回给客户端。
6.与多次调用startService()方法启动Service不同的是,多次调用bindService()方法并不会执行重复绑定,系统只会回调Service的onBind()方法一次。
7.有一种特殊情形,当某个Service之前已由某个客户端通过startService()方法启动了,接下来其他客户端再调用bindService()方法来绑定该服务后,再调用unbindService()方法解除绑定,最后又调用了bindService()方法再次绑定到服务,这个过程所触发的生命周期方法为onCreate()->onStart()->onBind()->onUnbind()[重写该方法时返回了true]->onRebind()。
8.跨进程调用Service(AIDL服务):
(1)本地Service的onBind()方法会直接把IBinder对象本身传给客户端,但远程Service的onBind()方法只是将IBinder对象的代理传给了客户端,当客户端获取了远程Service的IBinder对象的代理之后,接下来就可通过该IBinder对象去回调远程Service的属性或方法了。
(2)AIDL接口中用到的数据类型,除了基本类型、String、List、Map、CharSequence之外,其他类型全部都需要导包,即使它们在同一个包中也需要导包。
(3)开发人员定义的AIDL接口只是定义了进程之间的通信接口,Service端、客户端都需要实现该接口。
AIDL接口代码:
interface ICat {
String getColor();
double getWeight();
}
(5)定义一个Service实现类,该Service的onBind()方法所返回的IBinder对象应该是ADT所生成的ICat.Stub的子类的实例。
(6)开发客户端的第一步就是将Service端的AIDL接口文件复制到客户端应用中。
(7)在ServiceConnection的onServiceConnected方法中需要对onBind()方法所返回的对象的代理进行处理:
private ICat catService = ICat.Stub.asInterface(service);
(8)Android没有直接使用Java提供的序列化机制,而是提供了Parcelable这种轻量级的序列化机制,要求调用远程Service的参数和返回值都必须实现Parcelable接口。
定义Person类,先用AIDL来定义Person类:
parcelable Person;
然后定义一个实现Parcelable接口的Person类,不仅要求实现该接口里定义的方法,而且要求在实现类中定义一个名为CREATOR、类型为Parcelable.Creator的静态Field以及两个函数:
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
..............................................//该方法负责把Person对象的数据写入Parcel中
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
@Override
public Person createFromParcel(Parcel source) {
....................................................
}
@Override
public Person[] newArray(int size) {
....................................................
}
};
(9)在AIDL接口中定义方法时,需要指定形参的传递模式,一般都是采用传入参数的方式,因此下面指定为in模式:
interface IPet {
List getPets(in Person owner);
}
(10)除了由用户自行开发、启动的服务之外,Android系统本身提供了大量的系统服务,只要在程序中调用Context方法即可获得这些系统服务。
9.TelephonyManager是一个管理手机通话状态、电话网络信息的服务类,该类提供了大量的getXxx()方法来获取电话网络的相关信息。
举例1:获取手机网络和SIM卡信息
// 获取系统的TelephonyManager对象
TelephonyManager tManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
// 获取设备编号
tManager.getDeviceId();
// 获取系统平台的版本
tManager.getDeviceSoftwareVersion() != null ? tManager.getDeviceSoftwareVersion() : "未知";
// 获取网络运营商代号
tManager.getNetworkOperator();
// 获取网络运营商名称
tManager.getNetworkOperatorName();
// 获取手机网络类型
tManager.getPhoneType();
// 获取设备所在位置
tManager.getCellLocation() != null ? tManager.getCellLocation().toString() : "未知位置";
// 获取SIM卡的国别
tManager.getSimCountryIso();
// 获取SIM卡序列号
tManager.getSimSerialNumber();
// 获取SIM卡状态
tManager.getSimState();
// 创建一个通话状态监听器
PhoneStateListener listener = new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
// 来电铃响时
case TelephonyManager.CALL_STATE_RINGING:
OutputStream os = null;
try {
os = openFileOutput("phoneList", MODE_APPEND);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
PrintStream ps = new PrintStream(os);
// 将来电号码记录到文件中
ps.printIn(new Date() + "来电:" + incomingNumber);
ps.close();
break;
default:
break;
}
super.onCallStateChanged(state, incomingNumber);
}
};
// 监听电话通话状态的改变
tManager.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
可在data/data/ 举例3:黑名单来电自动挂断
* Android没有对外公开挂断电话的API,如果需要挂断电话,必须使用AIDL与电话管理服务进行通信,并调用服务中的API实现结束通话。
为了调用远程的AIDL Service,开发者需要将Android源代码中如下两个文件复制到项目的相应位置:
com.android.internal.telephony包下的ITelephony.aidl。
android.telephony包下的NeighboringCellInfo.aidl。
开发者需要在项目中建立对应的包,然后将这两个文件复制到相应的包下。
接下来就可以在程序中调用ITelephony的endCall方法来挂断电话了。
// 查询联系人的电话号码
final Cursor cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
// getView()中对联系人列表的处理
cursor.moveToPosition(position);
String number = cursor.getString(cursor.getColumnIndex(Contacts.Contract.CommonDataKinds.Phone.NUMBER)).replace("-", ""); // 获取联系人的电话号码,并去掉中间的中划线
// 当电话呼入时
case TelephonyManager.CAL_STATE_RINGING:
// 如果该号码属于黑名单
Method method = Class.forName("android.os.ServiceManager").getMethod("getService", String.class);
IBinder binder = (IBinder) method.invoke(null, new Object[] { TELEPHONY_SERVICE }); // 获取远程TELEPHONY_SERVICE的IBinder对象的代理
ITelephony telephony = ITelephony.Stub.asInterface(binder); // 将IBinder对象的代理转换为Itelephony对象
telephony.endCall(); // 挂断电话
break;
10.短信管理器(SmsManager):
11.可调用AudioManager的常用方法控制手机音频:
adjustStreamVolume(int streamType, int direction, int flags):调整手机指定类型的声音。
第一个参数指定声音类型:STREAM_ALARM 手机闹铃的声音
STREAM_DTMF DTMF音调的声音
STREAM_MUSIC 手机音乐的声音
STREAM_NOTIFICATION 系统提示的声音
STREAM_RING 电话铃声的声音
STREAM_SYSTEM 手机系统的声音
STREAM_VOICE_CALL 语音电话的声音
第二个参数指定对声音进行增大、还是减少;
第三个参数是调整声音时的标志,例如指定FLAG_SHOW_UI,则指定调整声音时显示音量进度条。
setMicrophoneMute(boolean on):设置是否让麦克风静音。
setMode(int mode):设置声音模式,可设置的值有NORMAL、RINGTONE和IN_CALL。
setRingerMode(int ringerMode):设置手机的电话铃声的模式。
RINGER_MODE_NORMAL:正常的手机铃声。
RINGER_MODE_SILENT:手机铃声静音。
RINGER_MODE_VIBRATE:手机振动。
setSpeakerphoneOn(boolean on):设置是否打开扩音器。
setStreamMute(int streamType, boolean state):将手机的指定类型的声音调整为静音。
setStreamVolume(int streamType, int index, int flags):直接设置手机的指定类型的音量值。
举例1:设置闹钟
Calendar currentTime = Calendar.getInstance();
// 创建一个TimePickerDialog实例,并把它显示出来
new TimePickerDialog(Activity.this, new TimePickerDialog.onTimeSetListener() {
@Override
public void onTimeSet(TimePicker tp, int hourOfDay, int minute) {
// 指定启动AlarmActivity组件
Intent intent = new Intent(AlarmTest.this, AlarmActivity.class);
// 创建PendingIntent对象
PendingIntent pi = PendingIntent.getActivity(Activity.this, 0, intent, 0);
Calendar c = Calendar.getInstance();
c.setTimeInMillis(System.currentTimeMillis());
// 根据用户选择时间来设置Calendar对象
c.set(Calendar.HOUR, hourOfDay);
c.set(Calendar.MINUTE, minute);
// 设置AlarmManager将在Calendar对应的时间启动指定组件
aManager.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pi);
}
}, currentTime.get(Calendar.HOUR_OF_DAY), currentTime.get(Calendar.MINUTE), false).show();
// 设置每隔5秒执行pi代表的组件一次
aManager.setRepeating(AlarmManager.RTC_WAKEUP, 0, 5000, pi);
// 取消对pi的调度
aManager.cancel(pi);
// 定义定时更换的壁纸资源
int[] wallpapers = new int[] {
R.drawable.shuangta,
R.drawable.lijiang,
R.drawable.qiao,
R.drawable.shui
};
// 重写service的onStart()方法,程序每次启动该Service都会执行onStart()方法中的代码
@Override
public void onStart(Intent intent, int startId) {
// 如果到了最后一张,系统重头开始
if (current > = 4)
current = 0;
try {
// 改变壁纸
wManager.setResource(wallpapers[current++]);
} catch(Exception e) {
e.printStackTrace();
}
super.onStart(intent, startId);
}