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

来源:互联网 发布:深圳电子产品出口数据 编辑:程序博客网 时间:2024/04/30 07:06


十、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以及两个函数:

@Overridepublic int describeContents() {        return 0;}@Overridepublic 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 0