Android进阶路上的AIDL

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/QQ243223991/article/details/52709160

前言

在初学Android时,只是打着幌子去看了看AIDL,到后面连AIDL的全拼都不知道了,到了今天,在项目中总算遇到了实践的地方,才去初略的了解了AIDL的用法,那么什么是AIDL,我们到底该怎样使用,以及我们需要注意一些什么?接下来我们就进入AIDL的学习。
 
 
说明:学习Android尽量结合源码,不要一味的只看别人的博客,相信很多人已经发现,百度里面的东西,大家都是抄过去抄过来的,有些人甚至都没动脑子就copy一份,这样的学习方式毫无意义,永远都不属于自己。

准备工作

服务端(Server):即AIDL声明的地方,比如进程名为:remote,一定要区分。
客户端(Client):即使用AIDL服务的地方,他和服务端不在一个进程,比如进程名为:local,默认不用写。
注:,在清淡配置文件中加上process=":xxx",一定不能少了“:”,xxx为你自定义进程名称,在运行程序是log日志打印是选择进程的话是以【包名+xxx】形式显示的。
如:
<activity android:name=".app.HelloWorld" android:label="@string/activity_hello_world" android:process=":remote">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.SAMPLE_CODE" />
            </intent-filter>
        </activity>

定义

ADIL:Android Interface Definition Language,硬生生的翻译过来就叫【Android接口定义语言】

部分源码解析

先来看看Android samples里面的一个写法,定义了一个IRemoteService的aidl文件。
package com.example.android.apis.app;

import com.example.android.apis.app.IRemoteServiceCallback;

/**
 * Example of defining an interface for calling on to a remote service
 * (running in another process).
 */
interface IRemoteService {
    /**
     * Often you want to allow a service to call back to its clients.
     * This shows how to do so, by registering a callback interface with
     * the service.
     */
    void registerCallback(IRemoteServiceCallback cb);
    
    /**
     * Remove a previously registered callback interface.
     */
    void unregisterCallback(IRemoteServiceCallback cb);
}


观察子代码可知,其声明格式和我们接口的声明格式是相同的,但有一点不同的是,Android中的Interface可以有修饰符来进行修饰,比如public,但是AIDL是不能用这样类似的修饰符来进行修饰的。接下来继续,在这个aidl文件中,他预先定义了两个方法,参数都为IRemoteServiceCallback,这个参数是什么呢?看看源码。
package com.example.android.apis.app;

/**
 * Example of a callback interface used by IRemoteService to send
 * synchronous notifications back to its clients.  Note that this is a
 * one-way interface so the server does not block waiting for the client.
 */
oneway interface IRemoteServiceCallback {
    /**
     * Called when the service has a new value for you.
     */
    void valueChanged(int value);


其中注释说明了,这是一个接口回调,用于客户端AIDL回调,因此AIDL的接口回调其实和Android相同,AIDL回调必须要声明回调的aidl文件,其次在接口中声明了一个方法valueChanged(int),这个方法的参数类型是一个int类型。请先记住这里,为什么是int类型?
注:这里的oneway代表请求对应的接口回调时不需要等待时间,没有时间上的延迟,直接返回。其可以用来声明Interface和方法。
AIDL的官方demo的声明和定义主要就这些,接下来我们看看具体使用方法。

使用步骤

①、在服务端中创建一个AIDL文件,文件名为IRemoteService.aidl,AIDL文件的包名必须与class的包名一致,为了防止后面编译找不到AIDL文件错误,建议在工程中通过右键【new->AIDL->AIDL文件】步骤创建(Android Studio),其会自动创建一个与class同级别的包名。代码:
interface IRemoteService{
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}
在声明一个AIDL文件后,Android Studio会给我们默认创建一个方法basicTypes(int,long,boolean,float,double,Stirng),再看看其注释说明,大概意思是这些类型可以用来作为参数类型和返回类型,初次之外,还支持List,Map,类型,那么在实际生活中,我们AIDL通信时不可能只需要这几种类型,而是需要更多的数据类型,即需要自定义数据类型,那么这时候一个自定义数据类型怎么才能作为AIDL方法中的参数类型呢?在后面我会继续说明的,到这里就验证了上面提出的一个问题,AIDL方法中的参数类型为什么为int,因为默认的方法中只支持int、long、String、List、Map等类型,不能为封装类型。
定义个自定义类型作为方法参数必须要进行序列化,即实现Parcelable,然而Serrializable不行的,接着需要创建一个与自定义类型相同名字的AIDL文件,其中需要作这样一句处理:parcelable xxx,xxx为自定义类型,如:
public class Student implements Parcelable {
    public Student(){

    }
    private String name;

    protected Student(Parcel in) {
        name = in.readString();
    }

    public static final Creator<Student> CREATOR = new Creator<Student>() {
        @Override
        public Student createFromParcel(Parcel in) {
            return new Student(in);
        }

        @Override
        public Student[] newArray(int size) {
            return new Student[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
    }
}

其次创建一个Student.aidl文件,内容为:
<pre name="code" class="java">
parcelable Student;

 这样一个操作,就可以支持自定义数据类型了,到这里,我们可能会觉得上面的方法很麻烦,我们可不可以用另一种方法来实现呢?答案是肯定的,不管是什么样的数据类型(目前枚举没试过),最终我们都可以将其转换成String,我们照样用String来作为入参或者返回类型,在我们调用aidl或者得到返回数据时可以在对String进行转换成我们需要的数据类型即可。这样就很简单了。 
  


②、声明方法,可以有参,可以无参,根据自己需求,我们这里直接声明一个接口回调作为入参。在上面已经讲解了接口回调也需要声明一个AIDL文件,因此需要先创建一个接口回调的AIDL文件,名字为ICallback.aidl
package com.android.app.myapplication;
import com.android.app.myapplication.ICallback;// 切记需要导入包,这里需要手动编写,默认的几种类型不要导包,只有非默认的才需要。

interface IRemoteService {
   void request(ICallback callback);
}
ICallback.aidl

package com.android.app.myapplication;


interface ICallback {
   void callback(String content);
}
③、gradle编译。编译的目的是可以防止自己哪里有写错的地方,其次可以在使用的地方就能自动提示代码了,Android编译可以自动将aidl文件编译成.java文件,如图中就可以看到。
AIDL

④、创建Service。
public class RemoteService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return remoteService;
    }

    private final IRemoteService.Stub remoteService=new IRemoteService.Stub() {
        @Override
        public void request(ICallback callback) throws RemoteException {
            // do something
            callback.callback("进程"+Process.myPid()+"给您传值了");
        }
    };
}
并且在清淡配置文件中配置
<service android:name=".service.RemoteService" android:process=":remote">
            <intent-filter>
                <action android:name="com.android.app.REMOTE"/>
            </intent-filter>
        </service>
process="remote",这个名字可以自己定义。
⑤、实现ServiceConnection。

/**
 * aidl管理
 */

public class RemoteManager implements ServiceConnection {
    private  final String TAG=this.getClass().getName();
    private IRemoteService remoteService;
    private RemoteManager(){}
    private static class Single{
        static RemoteManager instance=new RemoteManager();
    }

    /**
     * 启动服务
     * @param context
     */
    public void bind(Context context){
        try {
            Intent intent = new Intent("com.android.app.remote");
            intent.setPackage("com.android.app.myapplication");// 包名,android5.0以上需要显示调用
            context.bindService(intent,this,Context.BIND_AUTO_CREATE);
        }catch (Exception e){
            Log.e(TAG,e.toString());
        }
    }
    public static RemoteManager getInstance(){
        return Single.instance;
    }

    /**
     * 获取之前进程的信息
     */
    public void getRemote(){
        try {
              remoteService.request(new ICallback.Stub() {
                  @Override
                  public void callback(String content) {
                      Log.d(TAG,content);
                  }
              });
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }


    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.d(TAG,"onServiceConnected");
        remoteService=IRemoteService.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        Log.d(TAG,"onServiceDisconnected");
        remoteService=null;
    }
}

⑥、调用

public class MainActivity extends Activity {
    private Button btnGet;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initData();
        initListener();
    }

    private void initData() {
        RemoteManager.getInstance().bind(MainActivity.this);// 建议在application中进行绑定
    }

    private void initListener() {
        btnGet.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               RemoteManager.getInstance().getRemote();
            }
        });
    }
    private void initView(){
        btnGet= (Button) findViewById(R.id.btn_get);
    }
}

最后查看结果得:

这就是一个简单的aidl通信例子,

注意事项

☆、不需要任何修饰符
☆、AIDL文件名和接口名必须一致
☆、AIDL文件必须和class的包名一致
☆、AIDL默认只支持数据类型为基本数据类型和String以及List<T>、Map<T>,T也如此。
☆、aidl在绑定service的类需要在清淡配置文件中声明进程

总结

aidl是一个轻量级的跨进程通信,我们知道四大组件也可以进行跨进程通信,比如我们广播(除了本地广播外),他们有着一些区别,广播可以在不同app间进行通信,而aidl只能在一个app中,即相同包名下进行不同进程之间的通信,aidl能够事实准确的传递消息。
到这里,aidl通信的讲解就结束了,不懂的地方可以联系我的商务QQ。
如果有错误的地方请大家指正,谢谢!



猜你喜欢

转载自blog.csdn.net/QQ243223991/article/details/52709160
今日推荐