Service

1.1. 基本概念

     Service只能在后台运行,适合那些不需要界面的操作,像是播放音乐或者监听动作等,因为它 的名字就已经提示了:它就是一个服务。

      Service同样也是运行在主线程中,所以不能用它来做耗时的请求或者动作,否则就会阻塞住主线程。如果真的要这么做,可以跟Activity一样的做法:新开一个线程。

 

1.2. 启动方式

 

      Service根据启动方式分为两类:StartedBound。其中,Started()是通过startService()来启动,主要用于程序内 部使用的Service,而Bound是通过bindService()来启动,允许多个应用程序共享同一个Service。

      如果单单只是为了使用Service,两种方式都可以,但正如我们上面所看到 的,Bound启动的Service可以允许多个应用程序绑定到Service,所以,如果该Service是多个程序共享的,必须使用Bound来启动 Service。

 

1,声明Service:

<application>

   <service android:name=".MyService"/>

</application>

2,共享Service:

如果没有定义Intent Filters,默认下是私有的,无法访问,当然,我们也可以显示的指定该访问权限:android:exported=false。

 

 

startService(); 服务一旦开启就跟开启者(activity)没有什么关系.activity挂了,

服务还会开心的在后台运行.

 

Ÿ 创建Service,定义类继承Service,AndroidManifest.xml中定义<service>

Ÿ 开启Service,在其他组件中调用startService方法

Ÿ 停止Service,调用stopService方法

 

 

bindService();  服务跟activity就有了千丝万缕的联系. 如果调用者activity挂了
服务也跟着挂了.  不求同时生,但求同时死.
服务只能被显示的解除绑定一次,多次的解除绑定服务 应用程序会报异常

 

Ÿ 创建Service,定义类继承Service,AndroidManifest.xml中定义<service>

Ÿ 开启Service,在其他组件中调用Context.bindService()来启动

Ÿ 绑定Service,onBind()

Ÿ 解绑Service,onUnbind()

Ÿ 停止Service,调用stopService方法

 

 

混合开启服务

需求: 服务长期的后台运行, 又想调用服务里面的方法.

startservice 开启服务 (保证服务长期后台运行)

  
unbindService 解除绑定服务

//解除绑定,不解除无法直接关闭服务
stopService  停止服务.

 

 

 

1.3. 生命周期

 

 

      

 

服务常用生命周期回调方法

Started

     Service的生命周期不像Activity那样复杂,当我们通过Context.startService()启动Service的时候,系统就会调 用Service的onCreate(),接着就是onStartCommand(Intent, int, int),过去这个方法是onStart(),但现在onStart()已经不被鼓励使用了,onStartCommand()里面的代码就是我们 Service所要执行的操作,接着就是onDestroy(),关闭Service。

 

Bound

     通过Context.bindService()来启动,系统同样会调用Service的onCreate(),接着并不是 onStartCommand()而是onBind(),它会将多个客户端绑定到同一个服务中。如果我们想要停止Service,必须先对客户端解绑,也 就是调用onUnbind(),然后就是onDestroy()

 

混合模式
     如果先采用startService()方法启动服务,然后调用bindService()方法绑定到服务,再调用unbindService()方法解除绑定,最后调用bindService()方法再次绑定到服务

 

 

注意:

服务一旦被创建,就不会重新创建了.
服务的oncreate方法只会执行一次,如果服务已经开启,就不会再去执行oncreate方法
服务在停止的时候,会调用ondestroy方法

每一次开启服务的时候 都会调用 onStartCommand()  onStart();

 

 

 

 

1.4. Started Service 详细用法

 

 

    接下来我们就来创建一个简单的Service:MyService,用于在后台播放MP3。这是介绍Service时经常用到的例子。

public class MyService extends Service {

    MediaPlayer mediaPlayer = null;

 

    @Override

    public IBinder onBind(Intent arg0) {

        return null;

    }

 

    @Override

    public void onCreate() {

        if (mediaPlayer == null) {

            mediaPlayer = MediaPlayer.create(this, uri); // uri 为要播放的歌曲的路径

            super.onCreate();

        }

    }

 

    @Override

    public int onStartCommand(Intent intent, int flags, int startId) {

        mediaPlayer.start();

        return START_STICKY;

    }

 

    @Override

    public void onDestroy() {

        mediaPlayer.stop();

        super.onDestroy();

    }

}

      由于我们是通过Started启动Service,所以onBind()返回的是NULL。
      其实,onStartCommand()中的代码是我们Service主要的操作,它会在每次Service运行的时候被调用,而onCreate()是 当Service第一次被创建的时候才会被调用,当Service已经在运行的时候,不会调用该方法,可以将一些初始化的操作放在这里。

      定义好Service后,我们就可以在Activity中启动该Service:

Intent intent = new Intent(this, MyService.class);

startService(intent);

      这就像是在Activity中跳转到另一个Activity一样。
      看上去Service就好像是在Activity中新开一个Thread一样,事实上,这两者也是挺像的,那么,我们是如何确定我们需要的是Thread还是Service?

      如果是当用户使用我们的应用程序,像是点击按钮的时候,才执行一个在主线程外面的操作,我们需要的就是一个Thread而不是Service。

      还是我们上面的例子,如果我们想要播放音乐是在我们应用程序运行的时候,那么我们可以再onCreate()中新建一个Thread, 然后在onStartCommand()中开始该Thread,接着是在onStop()中停止该Thread。这样的做法非常自然,是一般的新手都会想 到的,但是android鼓励我们使用AsyncTask或者HandlerThread来处理这个过程。关于这个话题,我们还是放在其他文章里讲吧,毕 竟这是一个不小的话题。

      当我们开启了一个Service,我们就有义务去决定什么时候关闭该Service。可以通过stopSelf()让Service自己关闭自己或者调用stopService()来关闭该Service。

      当内存不够的时候,系统也会自动关闭Service。这时系统是如何选择哪些Service应该被关闭呢?如果一个Service被绑定到 Activity并且得到用户的焦点,那么它就很少可能会被关闭,但如果它是被声明运行在前台,它就永远也不可能会被关闭。

      知道这个事实对我们有什么用呢?当然是考虑如果内存足够的时候我们如何重启Service。

      解决这个问题的关键就在于onStartCommand()的返回值

      onStartCommand()指定要求返回一个整数值,这个整数值描述的就是系统应该以何种方式来重启该Service,一共有下面三种方式:

1.START_NOT_STICKY:系统在关闭Service后,不需要重新创建该Service,除非我们传递Intent要求创建Service。这是避免不必要的Service运行的最安全的方式,因为我们可以自己指定什么时候重新启动该Service。

2.START_STICKY:要求重新创建Service并且系统会通过一个空的Intent来调用onStartCommand()除非我们传递Intent。我们看到,上面的例子就使用了该返回值,这样当音乐播放被停止后,重新启动的时候就会重新播放音乐。

3.START_REDELIVER_INTENT:这种方式会通过关闭前的Intent来调用onStartCommand()。它非常适合像是下载文件这类的操作,这样当重新启动的时候,就会继续之前的下载而不会丢失之前的下载进度。

      关闭Service有两种方式:stopSelf()和stopService(),它们是有区别的,而且区别非常大。如果我们在一个应用程序中开启了多 个Service,那么我们就不能贸然的使用stopService()来关闭Service,因为前一个Service的关闭可能会影响到后面 Service的使用。这时我们就需要使用stopSelf(int startId)来决定关闭哪个Service。

      关闭Service是非常重要的,这对于减少内存消耗和电量消耗来说,都是一件好事。

     Service是在后台运行的,但是有时候我们需要向用户发送一些提示,像是下载完成之类的,这时就可以通过Toast或者Status Bar了。

     我们上面提到,Service可以运行在前台,这点就非常奇怪了:Service不是后台运行的吗?其实不然,有时候我们也想要清楚的知 道Service的进度,像是下载文件的进度或者歌曲的播放进度。这种Service一般都会在android手机上显示"正在进行的"的提示框里可以看 到(就是我们手机上可以拉下来的那个框)。

     这种Service一般都是通过Status Bar来提示进度,只有Service完成工作后才会消失:

Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),

        System.currentTimeMillis());

Intent notificationIntent = new Intent(this, MyService.class);

PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

notification.setLatestEventInfo(this, getText(R.string.notification_title),

        getText(R.string.notification_message), pendingIntent);

startForeground(ONGOING_NOTIFICATION_ID, notification);

 

      关键的方法就是startForeground(),它需要两个参数:唯一标识notification的id(不能为0)以及Status Bar的Notification。

     

final void

startForeground(int id, Notification notification)

使这项服务在前台运行,显示给用户,而在这种状态下提供持续的通知。

 

      当然,我们可以通过stopForeground()方法来停止Service在前台的显示,该方法并不会停止该Service。

      要想使用Started Service,我们除了继承自Service之外,还可以继承自另一个类:IntentService

      IntentService是Service的子类。我们一般都是一个Intent就开启一个Service,但是 IntentService是专门有一个线程来处理所有的开启请求,每次处理一个。这是我们需要在应用程序中开启多个Service但又想避免多线程的最 佳选择。

public class MyService extends IntentService {

    MediaPlayer mediaPlayer = null;

 

    public MyService() {

        super("MyService");

    }

 

    @Override

    protected void onHandleIntent(Intent intent) {

        if (mediaPlayer == null) {

            mediaPlayer = MediaPlayer.create(this, uri);

        }// uri 为要播放的歌曲的路径        mediaPlayer.start();

    }

}

      和继承自Service不一样,我们需要一个构造器,该构造器的主要作用就是为工作线程命名。
      我们仅需要覆写onHandleIntent()一个方法,因为该方法会为我们处理所有的Intent,并且会在处理完毕后关闭该Service。

      IntentService就像是一个封装好的Service,方便我们处理多个Intent的情况,但如果我们想要覆写其他方法,也是可以的,但要确保每个方法最后都有调用super的实现。

 

 

 

 

 

 

 

 

 

 

1.5. Bound Service 详细用法

 

要想创建Bound Service,我们就必须定义一个IBinder,它用于说明客户端是如何和服务通信的。Bound Service是一个非常大的话题,因为它涉及到本地服务还有远程服务。

 

 

1.5.1. 本地服务

 

 

绑定本地服务调用服务里面方法的流程:


1. 在activity 采用bindService方式开启服务...
2. 写一个实现类 MyConn implements ServiceConnection
3.  接口里面有一个方法onServiceConnected 在服务成功绑定的时候调用的方法
4.  在service代码 实现 onBind方法  返回一个IBinder接口的实现( 里面必须一个调用服务方法的api)
5.  在服务成功绑定的时候 服务里面返回的IBinder对象会传给 activity里面onServiceConnected 方法
6. 获取IBinder对象 中间人, 调用中间人的方法. 间接调用了服务里面的方法

 

应用

音乐播放器 : 音乐后台长期播放. service. 绑定本地服务.

 

 

 

 

 

      我们先从简单的本地服务开始:

public class LocalService extends Service {

    // Binder given to clients

    private final IBinder mBinder = new LocalBinder();

    // Random number generator

    private final Random mGenerator = new Random();

 

    /**

     * Class used for the client Binder.  Because we know this service always

     * runs in the same process as its clients, we don't need to deal with IPC.

     */

    public class LocalBinder extends Binder {

        LocalService getService() {

            // Return this instance of LocalService so clients can call public methods

            return LocalService.this;

        }

    }

 

    @Override

    public IBinder onBind(Intent intent) {

        return mBinder;

    }

 

    /** method for clients */

    public int getRandomNumber() {

      return mGenerator.nextInt(100);

    }

}

      这个例子非常简单,就是为每个绑定到该服务的客户产生一个0-100的随机数。

      我们首先必须提供一个IBinder的实现类,该类返回的是我们Service的一个实例,这是为了方便客户调用Service的公共方法,接着我们在onBind()方法中返回这个IBinder的实现类。
      这种做法适合Service只在应用程序内部共享,我们可以这样使用这个Service:

public class BindingActivity extends Activity {

    LocalService mService;

    boolean mBound = false;

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

    }

 

    @Override

    protected void onStart() {

        super.onStart();

        // Bind to LocalService

        Intent intent = new Intent(this, LocalService.class);

        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

    }

 

    @Override

    protected void onStop() {

        super.onStop();

        // Unbind from the service

        if (mBound) {

            unbindService(mConnection);

            mBound = false;

        }

    }

 

    /** Called when a button is clicked (the button in the layout file attaches to

      * this method with the android:onClick attribute) */

    public void onButtonClick(View v) {

        if (mBound) {

            // Call a method from the LocalService.

            // However, if this call were something that might hang, then this request should

            // occur in a separate thread to avoid slowing down the activity performance.

            int num = mService.getRandomNumber();

            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();

        }

    }

 

    /** Defines callbacks for service binding, passed to bindService() */

    private ServiceConnection mConnection = new ServiceConnection() {

 

        @Override

        public void onServiceConnected(ComponentName className,

                IBinder service) {

            // We've bound to LocalService, cast the IBinder and get LocalService instance

            LocalBinder binder = (LocalBinder) service;

            mService = binder.getService();

            mBound = true;

        }

 

        @Override

        public void onServiceDisconnected(ComponentName arg0) {

            mBound = false;

        }

    };

}

       我们通过bindService()将Activity和Service绑定到一起,接着必须定义一个 ServiceConnection,为的就是通过绑定的IBindergetService()方法来获得Service,然后我们再调用 Service的getRandomNumber()。使用完后,我们需要通过unbindService()来解除绑定。

       这里充分利用了回调的价值。

       这种方式仅仅适合客户和服务都在同一个应用程序和一个进程内,像是音乐程序的后台播放。

 

 

混合调用:
       结合我们上面有关Started Service的讨论,我们知道,可以用两种方式来开启服务,StartedService完全可以变成Bound Service,这样我们就不用显式的关闭服务,当没有任何客户和该Service绑定的时候,系统会自动的关闭该Service。但如果我们在一个 Started Service中也同样使用了onBind()呢?像是这样的情况:我们在一个音乐播放程序中利用Started Service开启音乐播放,然后用户离开程序后,音乐会在后台播放,当用户重新返回到程序中时,Activity可以绑定到Service上以便对音乐 进行控制。我们可以使用onRebind()方法来做到这点。

 

       

       这就是既是Started Service又是Bound Service的整个生命周期。

 

 

 

 

1.5.2. 远程服务

 

1.5.2.1. 通过Messenger实现进程间通信


       但我们有时候需要和远程的进程进行通信,这时就需要使用Messenger,这是为了实现进程间通信但又不想使用AIDL的唯一方式

       我们还是先来个简单例子:

public class MessengerService extends Service {

    /** Command to the service to display a message */

    static final int MSG_SAY_HELLO = 1;

 

    /**

     * Handler of incoming messages from clients.

     */

    class IncomingHandler extends Handler {

        @Override

        public void handleMessage(Message msg) {

            switch (msg.what) {

                case MSG_SAY_HELLO:

                    Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();

                    break;

                default:

                    super.handleMessage(msg);

            }

        }

    }

 

    /**

     * Target we publish for clients to send messages to IncomingHandler.

     */

    final Messenger mMessenger = new Messenger(new IncomingHandler());

 

    /**

     * When binding to the service, we return an interface to our messenger

     * for sending messages to the service.

     */

    @Override

    public IBinder onBind(Intent intent) {

        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();

        return mMessenger.getBinder();

    }

}

     可以看到,我们必须先在Service里面实现一个Handler,然后将这个Handler传递给Messenger,然后在onBind()中返回的是该Messenger的getBinder()的返回值。 

public class ActivityMessenger extends Activity {

    /** Messenger for communicating with the service. */

    Messenger mService = null;

 

    /** Flag indicating whether we have called bind on the service. */

    boolean mBound;

 

    /**

     * Class for interacting with the main interface of the service.

     */

    private ServiceConnection mConnection = new ServiceConnection() {

        public void onServiceConnected(ComponentName className, IBinder service) {

            // This is called when the connection with the service has been

            // established, giving us the object we can use to

            // interact with the service.  We are communicating with the

            // service using a Messenger, so here we get a client-side

            // representation of that from the raw IBinder object.

            mService = new Messenger(service);

            mBound = true;

        }

 

        public void onServiceDisconnected(ComponentName className) {

            // This is called when the connection with the service has been

            // unexpectedly disconnected -- that is, its process crashed.

            mService = null;

            mBound = false;

        }

    };

 

    public void sayHello(View v) {

        if (!mBound) return;

        // Create and send a message to the service, using a supported 'what' value

        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);

        try {

            mService.send(msg);

        } catch (RemoteException e) {

            e.printStackTrace();

        }

    }

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

    }

 

    @Override

    protected void onStart() {

        super.onStart();

        // Bind to the service

        bindService(new Intent(this, MessengerService.class), mConnection,

            Context.BIND_AUTO_CREATE);

    }

 

    @Override

    protected void onStop() {

        super.onStop();

        // Unbind from the service

        if (mBound) {

            unbindService(mConnection);

            mBound = false;

        }

    }

}

 

      Activity所要做的就是创建一个Messenger,该Messenger拥有Service返回的IBinder,接着就是发送消息给 Service,然后Service中的Handler根据消息进行处理。我们可以看到,Messenger之所以能够实现进程间的通信,靠的还是 Handler。

 

 

 

 

 

1.5.2.2. 通过AIDL实现进程间通信 Android Interface Definition Language

 


    在Android中, 每个应用程序都有自己的进程,当需要在不同的进程之间传递对象时,该如何实现呢? 显然, Java中是不支持跨进程内存共享的。因此要传递对象, 需要把对象解析成操作系统能够理解的数据格式, 以达到跨界对象访问的目的。在JavaEE中,采用RMI通过序列化传递对象。在Android中, 则采用AIDL(Android Interface Definition Language:接口定义语言)方式实现。

    AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,实现Android设备上的两个进程间通信(IPC)。AIDL的IPC机制和EJB (Enterprise Java Bean JAVA中的商业应用组件技术) 所采用的CORBA (Common Object Request Broker Architecture 是一组用来定义“分布式对象系统”的标准) 很类似,进程之间的通信信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象。由于进程之间的通信信息需要双向转换,所以android采用代理类在背后实现了信息的双向转换,代理类由android编译器生成,对开发人员来说是透明的。

 

 


实现进程通信,一般需要下面四个步骤:

 

假设A应用需要与B应用进行通信,调用B应用中的download(String path)方法,B应用以Service方式向A应用提供服务。需要下面四个步骤:


1> 在B应用中创建*.aidl文件,aidl文件的定义和接口的定义很相类,

如:在cn.itcast.aidl包下创建IDownloadService.aidl文件,内容如下:
package cn.itcast.aidl;
interface IDownloadService {
     void download(String path);
}


当完成aidl文件创建后,eclipse会自动在项目的gen目录中同步生成IDownloadService.java接口文件。接口文件中生成一个Stub的抽象类,里面包括aidl定义的方法,还包括一些其它辅助方法。值得关注的是asInterface(IBinder iBinder),它返回接口类型的实例,对于远程服务调用,远程服务返回给客户端的对象为代理对象,客户端在onServiceConnected(ComponentName name, IBinder service)方法引用该对象时不能直接强转成接口类型的实例,而应该使用asInterface(IBinder iBinder)进行类型转换。

     编写Aidl文件时,需要注意下面几点:

  1.接口名和aidl文件名相同。
  2.接口和方法前不用加访问权限修饰符public,private,protected等,也不能用final,static。
  3.Aidl默认支持的类型包话java基本类型(int、long、boolean等)和(String、List、Map、CharSequence),使用这些类型时不需要import声明。对于List和Map中的元素类型必须是Aidl支持的类型。如果使用自定义类型作为参数或返回值,自定义类型必须实现Parcelable接口。
  4.自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义的包在同一个包中。
  5.在aidl文件中所有非Java基本类型参数必须加上in、out、inout标记,以指明参数是输入参数、输出参数还是输入输出参数。
  6.Java原始类型默认的标记为in,不能为其它标记。

 


2> 在B应用中实现aidl文件生成的接口(本例是IDownloadService),但并非直接实现接口,而是通过继承接口的Stub来实现(Stub抽象类内部实现了aidl接口),并且实现接口方法的代码。内容如下:
public class ServiceBinder extends IDownloadService.Stub {
     @Override
     public void download(String path) throws RemoteException {
          Log.i("DownloadService", path);
     }         
}

3> 在B应用中创建一个Service(服务),在服务的onBind(Intent intent)方法中返回实现了aidl接口的对象(本例是ServiceBinder)。内容如下:
public class DownloadService extends Service {
     private ServiceBinder serviceBinder = new ServiceBinder();
     @Override
     public IBinder onBind(Intent intent) {
          return serviceBinder;
     }
     public class ServiceBinder extends IDownloadService.Stub {
          @Override
          public void download(String path) throws RemoteException {
               Log.i("DownloadService", path);
          }         
     }
}
其他应用可以通过隐式意图访问服务,意图的动作可以自定义,AndroidManifest.xml配置代码如下:
<service android:name=".DownloadService" >
     <intent-filter>
          <action android:name="cn.itcast.process.aidl.DownloadService" />
     </intent-filter>
</service>

4> 把B应用中aidl文件所在package连同aidl文件一起拷贝到客户端A应用eclipse会自动在A应用的gen目录中为aidl文件同步生成IDownloadService.java接口文件,接下来就可以在A应用中实现与B应用通信,代码如下:
public class ClientActivity extends Activity {
     private IDownloadService downloadService;

     @Override
     public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.main);
          this.bindService(new Intent("cn.itcast.process.aidl.DownloadService"), this.serviceConnection, BIND_AUTO_CREATE);//绑定到服务
     }

     @Override
     protected void onDestroy() {
          super.onDestroy();
          this.unbindService(serviceConnection);//解除服务
     }    
    
     private ServiceConnection serviceConnection = new ServiceConnection() {
          @Override
          public void onServiceConnected(ComponentName name, IBinder service) {
               downloadService = IDownloadService.Stub.asInterface(service);
               try {
                    downloadService.download("http://www.itcast.cn");
               } catch (RemoteException e) {
                    Log.e("ClientActivity", e.toString());
               }
          }
          @Override
          public void onServiceDisconnected(ComponentName name) {
               downloadService = null;
          }
     };
}

 

 

 

 

 

 

 

1.5.2.3. 通过Parcelable实现AIDL间传递自定义类型参数

 

 

Aidl默认支持的类型包话java基本类型(int、long、boolean等)和(String、List、Map、CharSequence),如果要传递自定义的类型该如何实现呢?

Parcelable接口

要传递自定义类型,首先要让自定义类型支持parcelable协议,实现步骤如下:
1>自定义类型必须实现Parcelable接口,并且实现Parcelable接口的public void writeToParcel(Parcel dest, int flags)方法 。
2>自定义类型中必须含有一个名称为CREATOR的静态成员,该成员对象要求实现Parcelable.Creator接口及其方法。
3> 创建一个aidl文件声明你的自定义类型。

Parcelable接口的作用:实现了Parcelable接口的实例可以将自身的状态信息(状态信息通常指的是各成员变量的值)写入Parcel,也可以从Parcel中恢复其状态。 Parcel用来完成数据的序列化传递。




进程间传递自定义类型的实现过程:

 

1> 创建自定义类型,并实现Parcelable接口,使其支持parcelable协议。如:在cn.itcast.domain包下创建Person.java:

package cn.itcast.domain;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable
     private Integer id;
     private String name;
    
     public Person(){}
     public Person(Integer id, String name) {
          this.id = id;
          this.name = name;
     }
     public Integer getId() {
          return id;
     }
     public void setId(Integer id) {
          this.id = id;
     }
     public String getName() {
          return name;
     }
     public void setName(String name) {
          this.name = name;
     }    
     @Override
     public int describeContents() {
          return 0;
     }
     @Override
     public void writeToParcel(Parcel dest, int flags) {//把javanbean中的数据写到Parcel
          dest.writeInt(this.id);
          dest.writeString(this.name);
     }
     //添加一个静态成员,名为CREATOR,该对象实现了Parcelable.Creator接口
     public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>(){
          @Override
          public Person createFromParcel(Parcel source) {//从Parcel中读取数据,返回person对象
               return new Person(source.readInt(), source.readString());     //*按write入的顺序读取
          }
          @Override
          public Person[] newArray(int size) {
               return new Person[size];
          }
     };
}

2> 在自定义类型所在包下创建一个aidl文件对自定义类型进行声明,文件的名称与自定义类型同名。
package cn.itcast.domain;
parcelable Person;


3> 在接口aidl文件中使用自定义类型,需要使用import显式导入,本例在cn.itcast.aidl包下创建IPersonService.aidl文件,内容如下:
package cn.itcast.aidl;
import cn.itcast.domain.Person;
interface IPersonService {
      void save(in Person person);
}

4> 在实现aidl文件生成的接口(本例是IPersonService),但并非直接实现接口,而是通过继承接口的Stub来实现(Stub抽象类内部实现了aidl接口),并且实现接口方法的代码。内容如下:
public class ServiceBinder extends IPersonService.Stub {
       @Override
       public void save(Person person) throws RemoteException {
          Log.i("PersonService", person.getId()+"="+ person.getName());
       }         
}

5> 创建一个Service(服务),在服务的onBind(Intent intent)方法中返回实现了aidl接口的对象(本例是ServiceBinder)。内容如下:
public class PersonService extends Service {
     private ServiceBinder serviceBinder = new ServiceBinder();
     @Override
     public IBinder onBind(Intent intent) {
          return serviceBinder;
     }
public class ServiceBinder extends IPersonService.Stub {
       @Override
       public void save(Person person) throws RemoteException {
     Log.i("PersonService", person.getId()+"="+ person.getName());
       }
}
}
其他应用可以通过隐式意图访问服务,意图的动作可以自定义,AndroidManifest.xml配置代码如下:
<service android:name=".PersonService" >
     <intent-filter>
          <action android:name="cn.itcast.process.aidl.PersonService " />
     </intent-filter>
</service>

6> 把应用中的aidl文件和所在package一起拷贝到客户端应用的src目录下eclipse会自动在客户端应用的gen目录中为aidl文件同步生成IPersonService.java接口文件,接下来再把自定义类型文件和类型声明aidl文件及所在package一起拷贝到客户端应用的src目录下。
最后就可以在客户端应用中实现与远程服务的通信,代码如下:
public class ClientActivity extends Activity {
     private IPersonService personService;

     @Override
     public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.main);
          this.bindService(new Intent("cn.itcast.process.aidl.PersonService"), this.serviceConnection, BIND_AUTO_CREATE);//绑定到服务
     }

     @Override
     protected void onDestroy() {
          super.onDestroy();
          this.unbindService(serviceConnection);//解除服务
     }    
    
     private ServiceConnection serviceConnection = new ServiceConnection() {
          @Override
          public void onServiceConnected(ComponentName name, IBinder service) {
               personService = IPersonService.Stub.asInterface(service);
               try {
                    personService.save(new Person(56,"liming"));
               } catch (RemoteException e) {
                    Log.e("ClientActivity", e.toString());
               }
          }
          @Override
          public void onServiceDisconnected(ComponentName name) {
               personService = null;
          }
     };
}

 

 

 

 

 

 

 

 

1.5.3. 绑定远程服务和本地服务的区别:

 

1. 本地服务 直接定义IService.java的接口
   远程服务 定义一个IService.adil文件  更改扩展名 删除public

2. 本地服务 extend Binder implement IServcie
   远程服务 extend IService.Stub

3. 本地服务 绑定成功  直接强制类型转换 IServcie
   远程服务 绑定成功  IService.Stub.asInterface(service)

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值