[android基础]《疯狂android讲义》重点整理(2)


十、Service与BroadcastReceiver:


1.Service与Activity还有一点相似之处,它们都是从Context派生出来的,因此它们都可调用Context里定义的如getResources()、getContentResolver()等方法。

2.与配置Activity相似的是,配置Service时也可为<service.../>元素配置<intent-filter.../>子元素,用于说明该Service可被哪些Intent启动。

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();
}

(4)定义好上面的AIDL接口之后,ADT工具会自动生成一个ICat.java接口,在该接口里包含一个Stub内部类,该内部类实现了IBinder、ICat两个接口,这个Stub类将会作为远程Service的回调类——它实现了IBinder接口,因此可作为Service的onBind()方法的返回值。

(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<Person> CREATOR = new Parcelable.Creator<Person>() {
          @Override
           public Person createFromParcel(Parcel source) {
                     ....................................................
            }
 
          @Override
           public Person[] newArray(int size) {
                     ....................................................
            }       
};

(9)在AIDL接口中定义方法时,需要指定形参的传递模式,一般都是采用传入参数的方式,因此下面指定为in模式:

interface IPet {
         List<Pet> 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();

  TelephonyManager还提供了一个listen(PhoneStateListener listener, int events)方法监听通话状态。
  举例2:监听手机来电信息

  // 创建一个通话状态监听器 
  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/<packagename>/files目录下查看phoneList文件。
  可将此段代码放在后台执行的Service中运行,并且设置Service组件随着系统开机自动运行。

  举例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):直接设置手机的指定类型的音量值。


12.AlarmManager通常用途为开发手机闹钟,本质是一个全局的定时器(退出程序后一样会启动指定组件) ,AlarmManager可在指定时间或指定周期启动其他组件(包括Activity、Service、BroadcastReceiver)。
  获取了AlarmManager对象之后,可调用方法来设置定时启动指定组件:
         void set(int type, long triggerAtTime, PendingIntent operation):设置在triggerAtTime时间启动由operation参数指定的组件。
                 其中第一个参数指定定时服务的类型:
                  ELAPSED_REALTIME 指定从现在开始时间过了一定时间后启动operation所对应的组件。
 ELAPSED_REALTIME_WAKEUP 指定从现在开始时间过了一定时间后启动operation所对应的组件。
                         即使系统关机也会执行operation所对应的组件。
 RTC:指定当系统调用System.currentTimeMillis()方法返回值与triggerAtTime相等时启动operation所对应的组件。
 RTC_WAKEUP 指定当系统调用System.currentTimeMillis()方法返回值与triggerAtTime相等时启动operation所对应的组件。
            即使系统关机也会执行operation所对应的组件。
         void setInexactRepeating(int type, long triggerAtTime, long interval, PendingIntent operation):设置一个非精确的周期性任务。
             例如设置Alarm每个小时启动一次,但系统并一定总在每个小时的开始启动Alarm服务。
         void setRepeating(int type, long triggerAtTime, long interval, PendingIntent operation):设置一个周期性执行的定时服务。
 void cancel(PendingIntent operation):取消AlarmManager的定时服务。


  举例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();

  举例2:定时更换壁纸
  *WallpaperManager提供了clear()方法来清除壁纸。

 //  设置每隔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);
  }


13.实现开机自动运行的Service:(1)让BroadcastReceiver监听Action为ACTION_BOOT_COMPLETED常量的Intent,然后在BroadcastReceiver中启动特定Service既可。
                                                           (2)授予应用程序访问系统开机事件的权限:<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值