Android基础第十天

Android基础第十天

Service

1.    Serivce的基本概念

Android 官方文档对Service 的解释如下:

A Service is an application component that can perform long-running operations in the background and does not provide a user interface. Another application component can start a service and it will continue to run in the background even if the user switches to another application. Additionally, a component can bind to a service to interact with it and even perform interprocess communication (IPC). For example, a service might handle network transactions, play music, perform file I/O, or interact with a content provider, all from the background.

 
 

 

 

 

 

 

 

 


翻译:

Service 是应用组件之一,用来执行需要长时间运行的操作,Service 运行在后台并且跟用户没有交互界面。第三方应用的组件(指四大组件的任意一种)可以启动一个Service,一旦启动起来,该Service 就一直在后台运行,就算用户已经将该应用置为后台进程。另外,组件也可以通过绑定的形式跟一个Service

进行交互,甚至完成进程间通信。比如:Service 可以在后台处理网络传输、播放音乐、进行I/O 流的读写或者跟内容提供者进行交互。

Service 的特点如下

l  Service 在Android 中是一种长生命周期的组件,它不实现任何用户界面,是一个没有界面的Activity

l  Service 长期在后台运行,执行不关乎界面的一些操作比如:网易新闻服务,每隔1 分钟去服务查看是否有最新新闻

l  Service 和Thread 有点相似,但是使用Thread 不安全,不严谨

l  Service 和其他组件一样,都是运行在主线程中,因此不能用它来做耗时的操作

2.    Android中的进程

在Android 中进程优先级由高到低,依次分为Foreground process、Visible process、Service process、Background process、Empty process。

Android 官方文档的解释如下:

1)    Foreground process 前台进程

 

翻译:

进程是用户执行当前任务所需要的。下面任何一个条件满足了,那么当前进程就视为前台进程:

1. 拥有一个正在和用户交互的Activity(也就是说Activity的onResume()方法被执行了)。

2. 拥有一个被用户的正在交互的Activity绑定的Service。

3. 拥有一个以“前台模式”运行的Service--该Service 已经被调用了startForeground()方法

4. 拥有一个正在执行生命周期方法的Service(onCreate()、onStart()、onDestory())

5. 拥有一个BroadCastReceiver并且正在执行onReceive()方法

通常情况下,在任何时候系统只存在一小部分前台进程。这些进程只会作为最后的手段才会被杀死-当内存不足以继续运行他们的时候。

通常情况下,在这个时刻,设备已经达到内存分页状态(当系统达到内存分页状态时只能通过虚拟地址访问内存,是不是很不好理解?那大家就理解为达到这个状态时系统已经无法继续分配新的内存空间即可),因此杀死一些前台进程(释放内存空间)以保证应用能够继续响应用户的交互是必要的手段。

2)    Visible process 可视进程,可以看见,但不可以交互

 

 

翻译:

一个不拥有任何前台组件的进程,但是依然可以影响用户在屏幕上看见的控件。如下任何条件之一成立则认为是可视进程:

1. 拥有一个不是前台的Activity(onPause 方法被调用),但是对用户依然可见。这种情况(处于暂停状态的Activity)发生,比如,如果前台Activity 打开了一个dialog,该dialog 下面依然可见Activity。

2. 拥有一个被可见或者前台Activity 绑定的Service。

 

一个可视进程被认为是极其重要的并且一般不会被系统杀死,除非为了保证所有的前台进程去运行不得已为之。

3)    Service process 服务进程

翻译:

一个拥有正在运行的Service,并且该Service 是被startService()方法启动起来的进程,并且该进程没有被归类到前面的两种(前置进程和可视进程)类型,那么该进程就是服务进程。尽管服务进程没有与用户可见的控件直接绑定,但是这些进程干的工作依然是用户关心的(比如在后台播放音乐或者从网络上下载数据),因此系统保留这些进程一直运行除非系统没有足够的内存去运行前台进程和可视进程。

4) Background process 后台进程

翻译:

一个拥有对用户不可见的Activity(该Activity 已经被执行了onStop()方法)进程叫做后台进程。后台进程对用户体验没有直接的影响,并且系统会在任何需要为前台进程,可视进程,或服务进程申请内存的时候杀死后台进程。

通常系统中运行着大量的后台进程,这些后台进程保存在一个LRU(最少最近使用的)列表中,使用LRU 规则是为了保证让最近被用户使用的Activity 进程最后被杀死(就是谁最近被使用了,谁最后再被杀死)。

如果一个Activity 正确实现了它的生命周期方法,并且保存了它的状态(通常这个状态是系统自动保存的),那么当系统杀死它的进程的时候是对用户的体验没有看得见的影响的,因为当用户导航到之前的Activity 的时候,这个Activity 会自动恢复之前保存的视图状态。查看Activity 文档去获取更多关于Activity状态的保存和恢复信息。

5)    Empty process 空进程(当程序退出时, 进程没有被销毁, 而是变成了空进程)

翻译:

不拥有任何系统组件(指四大组件)的进程叫空进程。保持空进程存活的唯一理由是为了缓存,这样可以提高下次启动组件的打开速度。当系统需要维持缓存进程和底层内核缓存的资源均衡的时候系统经常会(随时会)杀死该类进程。

3.    进程的回收机制

Android 系统有一套内存回收机制,会根据优先级进行回收。Android系统会尽可能的维持程序的进程,但是终究还是需要回收一些旧的进程节省内存提供给新的或者重要的进程使用。

进程的回收顺序是:从低到高

1. 当系统内存不够用时, 会把空进程一个一个回收掉

2. 当系统回收所有的完空进程不够用时, 继续向上回收后台进程, 依次类推

3. 但是当回收服务, 可视, 前台这三种进程时, 系统非必要情况下不会轻易回收, 如果需要回收掉这三种进程, 那么在系统内存够用时, 会再给重新启动进程;但是服务进程如果用户手动的关闭服务, 这时服务不会再重启了。

4. 为什么用服务而不是线程

服务是运行在后台的进程,跟我们熟悉的Thread 很像,Android 官方文档给出Service 和Thread 的区别如下:

翻译:

Service 是Android 中的(四大)组件之一,即使用户跟你的应用(不可见)没交互也依然运行在系统后台。因此,如果你真的需要这样的服务时才需要创建一个Service。

如果你不需要在主线程中做一些工作,而是仅仅当应用跟用户交互的时候做一些工作,那么你就可以创建一个子线程而不是开启一个服务。比如,如果你想播放音乐,但是仅仅想当你的Activity 处于运行状态时播放,那么你就可以在onCreate()方法中创建个线程对象,然后在onStart()方法中启动该线程,最后在

onStop()方法中停止(终止该线程)播放。当然也可以考虑使用AsyncTask 和HandlerThread 来替代传统的Thread。可以查看Processes and Threading 文档获取更多关于线程的内容。

在使用Service 时一定要注意,Service默认运行在应用的主线程中,因此如果你需要在Service 中执行大量的或者阻塞(比如网络访问、IO 等)的任务,那么依然需要在Service 中开启一个Thread 来完成这些工作。

追加:

其实使用Service 并不影响我们使用Thread,而且很多情形下,我们都是在Service 中开启子Thread的混合使用方式。

案例-电话窃听器

需求分析

开启一个服务,在服务中监听用户接到的电话(注意是接收到来电而不是拨出去电话),当电话被接通时开始录音,电话挂断时停止录音。将录音数据保存在sdcard 上。监听电话需要在后台实现,因此需要将监听电话的代码逻辑在Service 中运行。

代码实现

在该案例中用到两个java 类,MainActivity和自定义的Service 类。

1.    自定义Service

创建一个后台的服务PhoneService,当服务开启的时候去注册一个电话状态的监听。当电话接通的时候开始录音,当电话挂断的时候停止录音。

注意在服务销毁的时候去注销掉电话状态的监听,节省内存资源。

/**

 * 后台的服务 ,用来窃听电话

 */

publicclass PhoneService extends Service {

 

    private TelephonyManager tm;

    private MyPhoneListener listener;

    private MediaRecorder mRecorder;

   

    @Override

    public IBinder onBind(Intent intent) {

       returnnull;

    }

 

    @Override

    publicvoid onCreate() {

       //获取电话管理器

       tm = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);

       listener = new MyPhoneListener();

       //注册电话状态的监听器

       tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);

    }

 

    privateclass MyPhoneListener extends PhoneStateListener {

 

       @Override

       publicvoid onCallStateChanged(int state, String incomingNumber) {

           super.onCallStateChanged(state,incomingNumber);

           switch (state) {

           // 空闲状态,代表当前没有电话

           case TelephonyManager.CALL_STATE_IDLE:

              if(mRecorder!=null){

                  System.out.println("停止录音");

                  stopRecording();

              }

              break;

           // 响铃状态

           case TelephonyManager.CALL_STATE_RINGING:

              break;

           // 通话状态

           case TelephonyManager.CALL_STATE_OFFHOOK:

              System.out.println("通话状态,开启录音机,录音.");

              startRecording();

              break;

           }

       }

    }

 

    @Override

    publicvoid onDestroy() {

       //注销电话状态的监听

       tm.listen(listener, PhoneStateListener.LISTEN_NONE);

       super.onDestroy();

    }

   

    /**

     * 开始录音

     */

     privatevoid startRecording() {

            mRecorder = new MediaRecorder();

            //设置声音来源 从麦克风录音

        mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);

//设置输出格式为3gp mRecorder.setOutputFormat(MediaRecorder.OutputFormat

.THREE_GPP);

            //设置输出到的文件

         mRecorder.setOutputFile("/mnt/sdcard/"+SystemClock

.uptimeMillis()+".3gp");

            //设置输出的音频编码

        mRecorder.setAudioEncoder(MediaRecorder.

AudioEncoder.AMR_NB);

            try {

                mRecorder.prepare();

            } catch (Exception e) {

               System.out.println("prepare() failed");

            }

            mRecorder.start();

        }

 

     /**

      * 停止录音

      */

        privatevoid stopRecording() {

            mRecorder.stop();

            //释放录音器资源

            mRecorder.release();

            mRecorder = null;

        }

 

}

2.    启动Service

Service 的显示意图(其实并没有这个说法,Service 的启动分为startService 和bindService 两种启动方式,只不过第一种启动方式类似Activity 的显示意图启动)启动,跟显示意图启动Activity 几乎一模一样的,唯一的差别是方法名不一样。

publicclass MainActivity extends Activity {

    @Override

    protectedvoid onCreate(Bundle savedInstanceState){

       super.onCreate(savedInstanceState);

       setContentView(R.layout.activity_main);

    }

    publicvoid start(View view){

       //创建一个Intent 对象

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

       //启动服务

       startService(intent);

    }

}      

3.    在AndroidManifest.xml中注册

在该案例中保存录音文件到sdcard、监听电话状态、录音都需要添加权限。

在AndroidManifest.xml 中声明权限:

    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <uses-permission android:name="android.permission.RECORD_AUDIO"/>

注册Service:

<service android:name="com.itheima.phonelistener.PhoneService">

</service>

总结:

关于MediaRecorder 的代码大家可以不用去死记硬背,记住个大概过程即可,官方文档已经给出了详细的使用说明和例子,只要我们用的时候(更何况在企业用的概率也不是很大)能够查看文档即可。

除此之外官方给出的实例代码如下:

官方给出的MediaRecorder状态图:

 

Service的生命周期

关于Service 的生命周期在Android官方文档中有非常详细的说明,甚至比如何一本书籍都要好,因此这里建议英文水平好的直接看官方文档。

自己在学习Service 以及写关于Service 的文章时其实大量参考了官方的文档,因为那才是最权威的学习资料。

跟Activity 一样,Service 也是有生命周期的,不一样的是Service 的生命周期,从它被创建开始,到它被销毁为止,可以有两条不同的路径:标准开启模式和绑定模式。

A started service(标准开启模式)

被开启的service 通过其他组件调用startService()被创建。这种service 可以无限地运行下去,必须调用stopSelf()方法或者其他组件调用stopService()方法来停止它。当service 被停止时,系统会销毁它。

A bound service(绑定模式)

被绑定的service 是当其他组件(一个客户)调用bindService()来创建的。客户可以通过一个IBinder接口和service 进行通信。客户可以通过unbindService()方法来关闭这种连接。一个service 可以同时和多个客户绑定,当多个客户都解除绑定之后,系统会销毁service。

也就是说,你可以和一个已经调用了startService()而被开启的service 进行绑定。比如,一个后台音乐service 可能因调用startService()方法而被开启了,稍后,可能用户想要控制播放器或者得到一些当前歌曲的信息,可以通过bindService()将一个activity 和service 绑定。这种情况下,stopService()或stopSelf()实际

上并不能停止这个service,除非所有的客户都解除绑定。

Service 的生命周期回调函数

和Activity 一样,service 也有一系列的生命周期回调函数,你可以实现它们来监测service 状态的变化,并且在适当的时候执行适当的工作。

下面的service 展示了每一个生命周期的方法,也是官网给出的demo:

 

注意:不同于Activity 的生命周期回调方法,Service 不须调用父类的生命周期回调方法。

Service的生命周期图

官方给出的Service 生命周期图如下所示。该图左侧展示的是用startService()方法启动的Service的生命周期,右侧展示的是用bindService()方法启动的Service 的生命周期。

这个图说明了Service 典型的回调方法,尽管这个图中将开启的Service 和绑定的Service 分开,但是我们需要记住,任何Service 都潜在地允许被绑定。所以,一个被开启的Service 仍然可能被绑定。实现这些方法,可以看到两层嵌套的Service 的生命周期。

整体生命周期(The entire lifetime)

Service 整体的生命时间是从onCreate()被调用开始,到onDestroy()方法返回为止。和Activity一样,Service 在onCreate()中进行它的初始化工作,在onDestroy()中释放残留的资源。比如,一个音乐播放service 可以在onCreate()中创建播放音乐的线程,在onDestory()中停止这个线程。

onCreate() 和onDestroy()会被所有的Service 调用,不论Service 是通过startService()还是bindService()建立的。

 
 

The music will be played in onCreate(), then stop the thread in onDestroy().

The onCreate() and onDestroy() methods are called for all services, whether they're created by startService() or bindService().

 

 
 

积极活动的生命周期(The active lifetime)

Service 积极活动的生命周期(activelifetime)是从onStartCommand() 或onBind()被调用开始,它们各自处理由startService()或bindService()方法传过来的Intent 对象。如果service 是被开启的,那么它的活动生命周期和整个生命周期一同结束。如果service 是被绑定的,它们它的活动生命周期是在onUnbind()方法返回后结束。

注意:

尽管一个被开启的service 是通过调用stopSelf() 或stopService()来停止的,没有一个对应的回调函数与之对应,即没有onStop()回调方法。所以,当调用了停止的方法,除非这个service 和客户组件绑定,否则系统将会直接销毁它,onDestory()方法会被调用,并且是这个时候唯一会被调用的回调方法。

案例-音乐播放器

需求分析

通过该案例,我们可以更加直观地感受BoundService 的使用方法。

         主界面就是一个输入音乐的具体路径的EditText输入框,下方有三个按钮分别是播放,暂停,停止。如下图:

         当进入主界面的时候就去绑定服务,通过得到服务的中间代理人对象去控制多媒体进行音乐的播放暂停和停止。

         注意:我们在主界面输入的音乐路径不能通过static修饰符的方式来给其他类进行使用,为了减少oom内存泄漏,static尽量少用。所以这里如何传递路径给Service进行初始化mediaPlayer是个问题?我们可以巧妙的借助中间代理人对象来进行设置。

代码实现

1.    主界面MainActivity布局XML

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical"

    android:paddingBottom="@dimen/activity_vertical_margin"

    android:paddingLeft="@dimen/activity_horizontal_margin"

    android:paddingRight="@dimen/activity_horizontal_margin"

    android:paddingTop="@dimen/activity_vertical_margin"

    tools:context=".MainActivity">

 

    <EditText

        android:text="/mnt/sdcard/meet.mp3"

        android:id="@+id/et_path"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:hint="请输入要播放的音乐文件的路径" />

 

    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:orientation="horizontal">

 

        <Button

            android:id="@+id/play"

            android:layout_width="0dip"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text=">"/>

 

        <Button

            android:id="@+id/pause"

            android:layout_width="0dip"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="||"/>

 

        <Button

            android:id="@+id/stop"

            android:layout_width="0dip"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="□"/>

    </LinearLayout>

</LinearLayout>

2.    创建Iservice接口代理人

 publicinterface IService {

    /**

     * 播放音乐

     */

    publicvoid playInService();

    /**

     * 暂停音乐

     */

    publicvoid pauseInService();

    /**

     * 停止播放

     */

    publicvoid stopInService();

    /**

     * 初始化媒体播放器

     * @param path

     */

    publicvoid init(String path);

}

除了播放,暂停,停止以外,多设置了一个方法初始化init是为了让MainActivity在绑定Service的时候可以通过调用init方法进行路径paht的设置和MediaPlayer的初始化。

3.    创建MusicService

/**

 * 音乐播放的服务代码

 */

publicclass MusicService extends Service {

    //多媒体的播放器

    private MediaPlayer mediaPlayer ;

    privateclass MyBinder extends Binder implements IService{

       @Override

       publicvoid playInService() {

           play();

       }

 

       @Override

       publicvoid pauseInService() {

           pause();

       }

 

       @Override

       publicvoid stopInService() {

           stop();

       }

 

       @Override

       publicvoid init(String path) {

           initMediaPlayer(path);

       }

    }

    @Override

    public IBinder onBind(Intent intent) {

       returnnew MyBinder();

    }

    @Override

    publicvoid onCreate() {

       super.onCreate();

    }

    /**

     * 初始化我们的媒体播放器

     * @param path

     */

    privatevoid initMediaPlayer(String path) {

       try {

           mediaPlayer = new MediaPlayer();

           mediaPlayer.setDataSource(path);

           mediaPlayer.setLooping(false);

           mediaPlayer.setOnCompletionListener(new OnCompletionListener() {

              publicvoid onCompletion(MediaPlayer mp) {

                  Toast.makeText(MusicService.this, "播放完毕了", 0).show();

                  mediaPlayer.seekTo(0);

                  mediaPlayer.start();

              }

           });

           mediaPlayer.prepare();//准备开始播放音乐

       } catch (Exception e) {

           e.printStackTrace();

       }

    }

   

    @Override

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

       returnsuper.onStartCommand(intent, flags,startId);

    }

    @Override

    publicvoid onDestroy() {

       if(mediaPlayer!=null){

           mediaPlayer.stop();

           mediaPlayer.release();//释放播放器.

       }

       super.onDestroy();

    }

    publicvoid play(){

       if(mediaPlayer!=null &&!mediaPlayer.isPlaying()){

           mediaPlayer.start();

       }

    }

    publicvoid pause(){

       if(mediaPlayer!=null && mediaPlayer.isPlaying()){

           mediaPlayer.pause();

       }

    }

    publicvoid stop(){

       if(mediaPlayer!=null){

           mediaPlayer.stop();

           //停止掉播放器之后立刻的重新准备好.

           try {

              mediaPlayer.prepare();

              mediaPlayer.seekTo(0);

           } catch (Exception e) {

              e.printStackTrace();

           }

       }

    }

}

4.    MainActivity代码实现

 publicclass MainActivity extends Activity implements OnClickListener {

    private Button bt_play;

    private Button bt_pause;

    private Button bt_stop;

    private EditText et_path;

    private IService iService;

    private MyConn myConn;

    /**

     * 绑定服务是否成功

     */

    privatebooleanbind_success;

 

    @Override

    protectedvoid onCreate(Bundle savedInstanceState){

       super.onCreate(savedInstanceState);

       setContentView(R.layout.activity_main);

       bt_play = (Button) findViewById(R.id.play);

       bt_pause = (Button) findViewById(R.id.pause);

       bt_stop = (Button) findViewById(R.id.stop);

       bt_pause.setOnClickListener(this);

       bt_stop.setOnClickListener(this);

       bt_play.setOnClickListener(this);

       et_path = (EditText) findViewById(R.id.et_path);

 

       // 绑定服务

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

       myConn = new MyConn();

       bindService(intent, myConn, BIND_AUTO_CREATE);

    }

 

    privateclass MyConn implements ServiceConnection {

       @Override

       publicvoid onServiceConnected(ComponentNamename, IBinder service) {

           System.out.println("绑定服务成功");

           iService = (IService) service;

//         iService.init(path);

           bind_success = true;

       }

       @Override

       publicvoid onServiceDisconnected(ComponentNamename) {

       }

    }

 

    publicvoid onClick(View v) {

       String path = et_path.getText().toString().trim();

       if (TextUtils.isEmpty(path)) {

           Toast.makeText(this, "路径不能为空", 0).show();

           return;

       }

       File file = new File(path);

       if (!file.exists()) {

           Toast.makeText(this, "文件不存在", 0).show();

           return;

       }

       if (bind_success) {

           // 初始化媒播放器(只需要初始化一次即可)

           iService.init(path);

           bind_success = false;

       }

 

       switch (v.getId()) {

       case R.id.pause:// 暂停

           iService.pauseInService();

           break;

       case R.id.stop:// 停止

           iService.stopInService();

           break;

       case R.id.play:// 播放

           iService.playInService();

           break;

       }

    }

 

    @Override

    publicvoid onBackPressed() {

       AlertDialog.Builder builder = new Builder(this);

       builder.setTitle("提醒:");

       builder.setMessage("是否在后台继续播放音乐?");

       builder.setPositiveButton("继续播放",

              new DialogInterface.OnClickListener() {

                  publicvoid onClick(DialogInterface dialog, int which) {

                     //finish();

                     Toast.makeText(MainActivity.this, "此功能在只绑定服务的条件下不能实现!", 0).show();

                     dialog.dismiss();

                  }

              });

       builder.setNegativeButton("停止播放",

              new DialogInterface.OnClickListener(){

                  publicvoid onClick(DialogInterface dialog, int which) {

                     finish();

                  }

              });

       builder.show();

    }

   

    @Override

    protectedvoid onDestroy() {

       unbindService(myConn);

       super.onDestroy();

    }

}

注意:

绑定服务成功后不能立马去初始化媒体播放器,因为这个时候我们可能还没有输入具体的音乐路径,所以这个时候我们可以用一个boolean的变量值来进行标记,在三个按钮的点击事件里面进行初始化,但是切记只能初始化一次。

5.    清单文件配置服务

<service android:name="com.itheima.mp3player.MusicService">

</service>

 

AIDL 实现进程间通信

         在Android 平台中,各个组件运行在自己的进程中,他们之间是不能相互访问的,但是在程序之间是不可避免的要传递一些对象,在进程之间相互通信。为了实现进程之间的相互通信,Android 采用了一种轻量级的实现方式RPC(Remote Procedure Call 远程进程调用)来完成进程之间的通信,并且Android通过接

口定义语言(Android Interface Definition Language ,AIDL)来生成两个进程之间相互访问的代码,例如,你在Activity 里的代码需要访问Service 中的一个方法,那么就可以通过这种方式来实现了。

AIDL 是Android 的一种接口描述语言。编译器可以通过aidl 文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程的目的. 如果需要在一个Activity 中, 访问另一个Service 中的某个对象,需要先将对象转化成AIDL 可识别的参数(可能是多个参数), 然后使用AIDL 来传递这些参数, 在消息的接

收端, 使用这些参数组装成自己需要的对象。

如果想让我们的Service 可以提供远程服务,那么就必须定义一个.aidl 文件,该文件使用的是java 语法,类似java 的接口。然后将该文件在客户端和服务端的src 目录下各自保存一份,这样编译器就会根据aidl 文件自动生成一个java 类,也就说在客户端和服务端都拥有了相同的类文件了。

aidl 的使用分为如下三个步骤(见官方文档):

1.       远程服务定义的接口文件不能是.java文件,需要改成.aidl文件,里面的修饰符都不需要。

比如:Iservice.aidl文件

package com.itheima.remote;

 

 interfaceIService {

     void callMethodInService();

}

 

2.       程序会自动在gen目录下生成Iservice.java文件

会有一个抽象的静态类Stub继承了Binder对象并实现了IService接口。

3.       创建服务,在服务中创建中间代理人对象,在onBind()方法中返回出去给绑定该服务的对象使用。

 publicclass RemoteService extends Service {

    /**中间代理人*/

    privateclass MyBinder extends IService.Stub{

       @Override

       publicvoid callMethodInService() {

           //调用服务里面的方法

           methodInService();

       }

    }

    @Override

    public IBinder onBind(Intent intent) {

       //返回中间代理人

       returnnew MyBinder();

    }

   

    @Override

    publicvoid onCreate() {

       System.out.println("远程服务被创建了...");

       super.onCreate();

    }

   

    @Override

    publicvoid onDestroy() {

       System.out.println("远程服务被销毁了...");

       super.onDestroy();

    }

    /**

     * 服务里面的方法

     */

    publicvoid methodInService(){

       System.out.println("我是远程服务的方法,我被调用了....");

    }

}

 

 

通过以上三步即可创建一个远程服务给其他应用进行调用:

在调用之前将远程服务的aidl文件拷贝到自己工程的目录下,并且一定要创建aidl文件中对应的包名,将aidl文件放在对应的包下即可:

调用者代码:

publicclass MainActivity extends Activity {

    private MyConn conn;

    private IService iService;

 

    @Override

    protectedvoid onCreate(Bundle savedInstanceState){

       super.onCreate(savedInstanceState);

       setContentView(R.layout.activity_main);

    }

 

    publicvoid bind(View view){

       Intent intent = new Intent();

       //调用远程服务:隐式意图

       intent.setAction("com.itheima.remote");

       conn = new MyConn();

       bindService(intent, conn, BIND_AUTO_CREATE);

    }

   

    privateclass MyConn implements ServiceConnection{

 

       @Override

       publicvoid onServiceConnected(ComponentNamename, IBinder service) {

           //得到远程服务的代理对象

           iService = IService.Stub.asInterface(service);

       }

       @Override

       publicvoid onServiceDisconnected(ComponentNamename) {

       }

    }

    publicvoid call(View view){

       try {

           //通过代理对象调用远程服务的方法

           iService.callMethodInService();

       } catch (RemoteException e) {

           e.printStackTrace();

       }

    }

}

 

总结:

AIDL可以实现android下的进程间通信,远程服务的绑定和方法调用,创建一个远程服务的写法固定三个步骤,官网有明确的说明和示例代码,大家不用死记硬背,而且以后在实际的企业开发中也几乎不会让我们自己去写一个远程服务给别人调用;一般是我们去调用别人写好的远程服务来方便我们自己开发的应用,比如调用支付宝服务。所以如何去调用别人的远程服务是来实现我们需要的功能是我们需要必须掌握的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值