浅析Android-AIDL的使用方式

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/Greathfs/article/details/102717116

前言

我们在说AIDL使用之前先说下Service.ServiceAndroid四大组件之一,它是不依赖于用户界面的,所以我们常常用于进行一些耗时的操作,比如:下载数据,更新等操作,我们下面就先简单走一遍这个Service

Service启动

启动方式有两种:startServicebindService

  • startService:主要用于启动一个服务执行后台任务,不进行通信。
    停止服务使用stopService
  • bindService:该方法启动的服务可以进行通信。
    停止服务使用unbindService

这里说明下:ServiceThread没有任何关系

  • ThreadThread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。

  • ServiceServiceandroid的一种机制,当它运行的时候如果是Local Service,那么对应的 Service是运行在主进程的 main线程上的。如:onCreateonStart这些函数在被系统调用的时候都是在主进程的main线程上运行的。如果是RemoteService,那么对应的 Service则是运行在独立进程的 main线程上。因此请不要把 Service理解成线程,它跟线程半毛钱的关系都没有!同样如果在Service中执行耗时操作,一样是需要开起线程,否则会引起ANR

首先我们先新建一个Service类,重写一下里面的方法

/**
 *
 * @author HuangFusheng
 * @date 2019-10-23
 * description 本地服务
 */
public class LocalService extends Service {

    private static final String TAG = "LocalService";

    public LocalService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate: --->");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand: --->");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: --->");
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

Service属于四大组件之一,所以还需要在清单文件中注册下

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.hfs.aidlsample">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <service
            android:name=".LocalService"
            android:enabled="true"
            android:exported="true"></service>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

Ok,然后我们在MainActivity中写两个方法启动服务和停止服务

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="startService"
        android:text="开启服务" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="stopService"
        android:text="停止服务" />

</LinearLayout>

我们先点击开启服务,看下log

10-24 20:35:46.324 1488-1488/? D/LocalService: onCreate: --->
10-24 20:35:46.324 1488-1488/? D/LocalService: onStartCommand: --->

我们多点击开启服务试试:

10-24 20:35:46.324 1488-1488/? D/LocalService: onCreate: --->
10-24 20:35:46.324 1488-1488/? D/LocalService: onStartCommand: --->
10-24 20:37:41.924 1488-1488/? D/LocalService: onStartCommand: --->
10-24 20:37:42.590 1488-1488/? D/LocalService: onStartCommand: --->
10-24 20:37:42.756 1488-1488/? D/LocalService: onStartCommand: --->
10-24 20:37:42.956 1488-1488/? D/LocalService: onStartCommand: --->
10-24 20:37:43.057 1488-1488/? D/LocalService: onStartCommand: --->
10-24 20:37:43.224 1488-1488/? D/LocalService: onStartCommand: --->

我们看到,后面即使多点几下这个开启服务,但是也只会调onStartCommand方法,onCreate方法并不会重复调用,因为我们之后每次点击的时候,由于该Service已经存在,所以并不会重新创建,所以onCreate方法只会调用一次。

我们点击停止服务,看下log

10-24 20:39:17.624 1488-1488/? D/LocalService: onDestroy: --->

服务此时已经销毁了

我们第一种启动方式已经完成了,这种启动方式呢我们不能和这个Service进行一些交互,我们只能控制它的启动和销毁,这时我们就得说下Service的另外一种启动方式了,我们先在MainActivity中写好绑定服务和解绑服务这两个方法,供我们下面使用

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="startService"
        android:text="开启服务" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="stopService"
        android:text="停止服务" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="bindService"
        android:text="绑定服务" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="unBindService"
        android:text="解绑服务" />

</LinearLayout>

我们首先看下bindService这个方法

 public boolean bindService(Intent service, ServiceConnection conn, int flags) {
        ...
}

可以看见这个方法需要三个参数:

  • 第一个,Intent传的值
  • 第二个就是service连接对象,类似操作数据库时的链接对象
  • 第三个就是我们的flags这样的一个参数,一般是BIND_AUTO_CREATE

然后我们在看下unbindService这个方法

public void unbindService(ServiceConnection conn) {
...
}

主要就是需要一个ServiceConnection对象,这个和我们bindService保持一致就行,所以接下来我们先建一个ServiceConnection

 private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

这里会回调两个方法,这两个方法分别会在ActivityService建立关联和解除关联的时候调用

刚才使用startService方式启动Service的时候,我们Service中有个onBind方法我们并没有去管它,但是用bindService这种方式启动我们就需要重写这个方法了,它返回的是一个IBinder,这个是一个接口,它有一个实现类Binder,所以我们写一个类继承这个Binder,同时写一个getName方法一个回调给Activity

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

    public class SimpleBind extends Binder {
        public String getName() {
            return "我是: Greathfs";
        }
    }

那么Activity中怎么调用这个方法呢,这时就用到了咱们之前写好的mServiceConnection中的onServiceConnected,这个方法会返回一个IBinder对象,这个对象就是我们之前在Service中写的SimpleBind,所以我们只可以强转为SimpleBind对象,也就可以调用getName方法了

 private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.d(TAG, "onServiceConnected: --->");
            LocalService.SimpleBind bind = (LocalService.SimpleBind) iBinder;
            Log.d(TAG, "onServiceConnected: " + bind.getName());
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.d(TAG, "onServiceDisconnected: --->");
        }
    };

OK,我们来运行下我们代码,先执行bindService,看下log

10-24 21:13:36.115 2087-2087/com.hfs.aidlsample D/LocalService: onCreate: --->
10-24 21:13:36.124 2087-2087/com.hfs.aidlsample D/LocalService: onServiceConnected: --->
10-24 21:13:36.124 2087-2087/com.hfs.aidlsample D/LocalService: onServiceConnected: 我是: Greathfs

我们这里发现并没有执行onStartCommand方法,我们在多点击几次看看

10-24 21:13:36.115 2087-2087/com.hfs.aidlsample D/LocalService: onCreate: --->
10-24 21:13:36.124 2087-2087/com.hfs.aidlsample D/LocalService: onServiceConnected: --->
10-24 21:13:36.124 2087-2087/com.hfs.aidlsample D/LocalService: onServiceConnected: 我是: Greathfs

发现并没有什么变化

我们调用unbindService方法,看下log

10-24 21:15:51.198 2087-2087/com.hfs.aidlsample D/LocalService: onDestroy: --->

服务销毁,这里有些同学肯定会问为啥onServiceDisconnected没调用呢?
因为onServiceDisconnected()方法在连接正常关闭的情况下是不会被调用的, 该方法只在Service被破坏了或者被杀死的时候调用.例如, 系统资源不足, 要关闭一些Services, 刚好连接绑定的 Service 是被关闭者之一, 这个时候onServiceDisconnected()就会被调用。

远程服务 AIDL

AIDL(Android Interface Definition Language)Android接口定义语言的意思,它可以用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。
实际上实现跨进程之间通信的有很多,比如广播Content Provider,但是AIDL的优势在于速度快(系统底层直接是共享内存),性能稳,效率高,一般进程间通信就用它。
跨进程肯定有两个端,我们这里模拟一下,在工程中再建一个模块server,整体的项目结构就是下面这样

在这里插入图片描述

服务端

我们在服务端下建立一个SimpleAIDLService.aidl文件,直接使用AS建即可,自动编译
在这里插入图片描述
然后,我们在SimpleAIDLService下增加一个获取名字的方法。

interface SimpleAIDLService {

       String getName();
}

然后build一下,然后会在build - >generated ->source ->aidl->debug下会生成一个aidl文件,那说明AIDL文件已经编译成功。

这个文件创建好之后,我们还需要创建一个Service进行通信,和上面我们讲Service启动方式的第二种很像,主要就是onBind方法那里有些不同

public class MyService extends Service {
    public MyService() {
    }

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

    public class MyBind extends com.hfs.server.SimpleAIDLService.Stub {

        @Override
        public String getName() throws RemoteException {
            return "我是: AIDL里的Greathfs";
        }
    }
}

代码看起来是不是很熟悉,唯一不一样的就是原来在本地服务的时候内部类继承的是Binder,而现在继承的是SimpleAIDLService.Stub,继承的是我们刚刚建立的aidl文件,然后实现我们刚刚的定义的getName()方法

这样服务端的代码基本上就写完了,接下来我们写一下客户端

客户端

首先将刚刚在服务端创建的SimpleAIDLService原封不动的复制到客户端来。(注意:路径要一模一样),直接复制文件夹
在这里插入图片描述
在这里插入图片描述
这里为了区分,我们新建一个AIDLActivity

public class AIDLActivity extends AppCompatActivity {
    private static final String TAG = "AIDLActivity";

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            SimpleAIDLService simpleAIDLService = SimpleAIDLService.Stub.asInterface(iBinder);
            try {
                String name = simpleAIDLService.getName();
                Log.d(TAG, "onServiceConnected: " + name);
            } catch (RemoteException e) {
                e.printStackTrace();
            }

        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };


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

    public void bindService(View view) {
        Intent intent = new Intent();
        intent.setAction("com.hfs.server.MyService");
        //从 Android 5.0开始 隐式Intent绑定服务的方式已不能使用,所以这里需要设置Service所在服务端的包名
        intent.setPackage("com.hfs.server");
        bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
    }

    public void unBindService(View view) {
        unbindService(mServiceConnection);
    }
}

这里基本上和连接本地服务的代码差不多,只需要注意两个地方,
一个是绑定服务的时候,因为从 Android 5.0开始 隐式Intent绑定服务的方式已不能使用,所以这里需要设置Service所在服务端的包名
另一个需要注意的就是获取SimpleAIDLService对象是通过SimpleAIDLService.Stub.asInterface(iBinder);获取的,同时基本上只能传递Java的基本数据类型、字符串、List或Map等。如果想传递一个自定义的类就必须要让这个类去实现Parcelable接口,并且要给这个类也定义一个同名的AIDL文件

接下来我们运行下代码,看看我们的log能不能打印出来,我们分别运行app模块和server模块

10-24 22:09:44.957 2426-2426/com.hfs.aidlsample D/AIDLActivity: onServiceConnected: 我是: AIDL里的Greathfs

可以看到,日志已经出来了,这样的话整体流程也就全部走完了

源码地址

猜你喜欢

转载自blog.csdn.net/Greathfs/article/details/102717116