Services服务

Service

1.1基本概念

Service只能在后台运行,适合那些不需要界面的操作,像是播放音乐或者监听动作等,
因为它 的名字就已经提示了:它就是一个服务。
Service同样也是运行在主线程中,所以不能用它来做耗时的请求或者动作,
否则就会阻塞住主线程。如果真的要这么做,可以跟Activity一样的做法:新开一个线程。

1.2启动方式

Service根据启动方式分为两类:Started和Bound。其中,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,在其他组件中调用startService方法

停止Service,调用stopService方法


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

创建Service,定义类继承Service,AndroidManifest.xml中定义

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

绑定Service,onBind()

解绑Service,onUnbind()

停止Service,调用stopService方法

混合开启服务

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

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

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

1.3 生命周期

Service

Service

常用的回调方法

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,为的就是通过绑定的IBinder的getService()方法来获得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)

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值