(二十) aidl的简单demo

前言:写了个aidl的例子写了一个多小时,Service恁是起不来,最后灵光一闪,手机预制的xx管家会限制自启动和关联启动,emmmm,关了就好了, 真心累。


demo地址:

Server:点击打开链接

Client: 点击打开链接


1. aidl介绍

抄下百度百科的介绍

Android系统中的进程之间不能 共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。
为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了 远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。我们知道4个Android应用程序组件中的3个(Activity、BroadcastReceiver和ContentProvider)都可以进行跨进程访问,另外一个Android应用程序组件Service同样可以。因此,可以将这种可以跨进程访问的服务称为AIDL(Android Interface Definition Language)服务。

2. 写个简单的例子

想用aidl实现跨进程调用,那么首先得有个服务器端和客户端,服务器端提供服务,客户端调取服务器端提供的服务进行逻辑处理。


2.1 创建server和client架子

这个简单,创建两个module,就好了。



2.2 创建aidl

我挺想把创建aidl文件夹和目录的过程截图截下来,但是发现一打开new-folder-aidl folder后就截不了图了,尴尬。

下面的图是创建好aidl后的样子,有个我不是很懂的注意点:server下的aidl文件要和client下的aidl文件包名要一样,client下的aidl文件就是从server下拷贝过去的。至于截图显示包名不一样是AS的bug,实际目录是一样的,都是com.example.demo_20_aidl_server。


aidl是长这样的:

// IMyAidlInterface.aidl
package com.example.demo_20_aidl_server;

// Declare any non-default types here with import statements

interface IMyAidlInterface {

    int add (int num1, int num2);
}

一个很简单的接口,只是文件以aidl结尾而已。

弄好以后将两个module都用build-make一下,得到如下的java文件

详情:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /home/jiatai/AndroidStudioProjects/demo_csdn/demo_20_aidl_client/src/main/aidl/com/example/demo_20_aidl_server/IMyAidlInterface.aidl
 */
package com.example.demo_20_aidl_server;
// Declare any non-default types here with import statements

public interface IMyAidlInterface extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.demo_20_aidl_server.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.example.demo_20_aidl_server.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.example.demo_20_aidl_server.IMyAidlInterface interface,
 * generating a proxy if needed.
 */
public static com.example.demo_20_aidl_server.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.demo_20_aidl_server.IMyAidlInterface))) {
return ((com.example.demo_20_aidl_server.IMyAidlInterface)iin);
}
return new com.example.demo_20_aidl_server.IMyAidlInterface.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_add:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.demo_20_aidl_server.IMyAidlInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public int add(int num1, int num2) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(num1);
_data.writeInt(num2);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public int add(int num1, int num2) throws android.os.RemoteException;
}


2.3 创建Server

server是以Service的形式提供服务的,具体如下:

2.3.1 创建Service

package com.example.demo_20_aidl_server;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class MyService extends Service {
    private static final String TAG = "aidlTest";
    public MyService() {
    }

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

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        return iBinder;
    }

    private IBinder iBinder = new IMyAidlInterface.Stub() {
        @Override
        public int add(int num1, int num2) throws RemoteException {
            return num1 + num2;
        }
    };
}

2.3.2 注册Service

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

    <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">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="aidl.test.action"/>
            </intent-filter>
        </service>
    </application>

</manifest>

2.3.3 关闭对应server的关联启动控制

PS:如果有的话。。。这样server就能被client调起来了


2.4 创建Client

布局:两个按钮,一个用来绑定/解绑Service,一个用来调用对应Server的功能,text用来显示结果。



activity:

package com.example.demo_20_aidl_client;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.example.demo_20_aidl_server.IMyAidlInterface;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    private static final String TAG = "aidlTest";
    Button btnBindService;
    Button btnAdd;
    TextView txResult;
    private IMyAidlInterface myAidlInterface;
    private boolean flag = false;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myAidlInterface = IMyAidlInterface.Stub.asInterface(service);
            Log.d(TAG, "onServiceConnected" + myAidlInterface);
        }

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnBindService = findViewById(R.id.button);
        btnAdd = findViewById(R.id.button2);
        txResult = findViewById(R.id.textView);
        btnBindService.setOnClickListener(this);
        btnAdd.setOnClickListener(this);
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button:
                if (!flag) {
                    bindMyService();
                    Toast.makeText(this, "bind service", Toast.LENGTH_SHORT).show();
                    flag = true;
                    btnBindService.setText("unbind");
                }else{
                    unbindMyService();
                    Toast.makeText(this, "unbind service", Toast.LENGTH_SHORT).show();
                    flag = false;
                    btnBindService.setText("bind");
                }
                break;
            case R.id.button2:
                try {
                    if (myAidlInterface != null) {
                        txResult.setText("the result is :" + myAidlInterface.add(1, 4));
                    }else {
                        txResult.setText("the service has unbind");
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            default:
                break;
        }
    }



    private void bindMyService() {
        Intent intent = new Intent();
        intent.setPackage("com.example.demo_20_aidl_server");
        intent.setAction("aidl.test.action");
        bindService(intent, connection, BIND_AUTO_CREATE);
    }

    private void unbindMyService() {
        unbindService(connection);
    }
}


2.5 unbindService部分失效?

突然发现unbindService不会调用onServiceDisconnected,网上找了解释:

参考:点击打开链接

首先要明确一点,unbindService()起作用了。

你之所以还是可以调用Service里的方法是因为你持有myBinder这个对象,所以Service的生命周期虽然结束了,但是它还没有被垃圾回收机制回收,这个Service对象还在内存中。

感觉说的不大对,问题关键在于Service已经调用了OnDestroy了,但是onServiceDisconnected没有被调用。

如上图所示,我加了个堆栈,onServiceConnected和onServiceDisconnected按道理来说应该是对应的,应该在一个类里处理。

ServiceDispatcher.java

public void doConnected(ComponentName name, IBinder service, boolean dead) {
            ServiceDispatcher.ConnectionInfo old;
            ServiceDispatcher.ConnectionInfo info;

            synchronized (this) {
                if (mForgotten) {
                    // We unbound before receiving the connection; ignore
                    // any connection received.
                    return;
                }
                old = mActiveConnections.get(name);
                if (old != null && old.binder == service) {
                    // Huh, already have this one.  Oh well!
                    return;
                }

                if (service != null) {
                    // A new service is being connected... set it all up.
                    info = new ConnectionInfo();
                    info.binder = service;
                    info.deathMonitor = new DeathMonitor(name, service);
                    try {
                        service.linkToDeath(info.deathMonitor, 0);
                        mActiveConnections.put(name, info);
                    } catch (RemoteException e) {
                        // This service was dead before we got it...  just
                        // don't do anything with it.
                        mActiveConnections.remove(name);
                        return;
                    }

                } else {
                    // The named service is being disconnected... clean up.
                    mActiveConnections.remove(name);
                }

                if (old != null) {
                    old.binder.unlinkToDeath(old.deathMonitor, 0);
                }
            }

            // If there was an old service, it is now disconnected.
            if (old != null) {
                mConnection.onServiceDisconnected(name);
            }
            if (dead) {
                mConnection.onBindingDied(name);
            }
            // If there is a new service, it is now connected.
            if (service != null) {
                mConnection.onServiceConnected(name, service);
            }
        }

然后看了下onServiceDisconnected只有两个地方会调用到,如下是最有可能的:

public void doDeath(ComponentName name, IBinder service) {
            synchronized (this) {
                ConnectionInfo old = mActiveConnections.get(name);
                if (old == null || old.binder != service) {
                    // Death for someone different than who we last
                    // reported...  just ignore it.
                    return;
                }
                mActiveConnections.remove(name);
                old.binder.unlinkToDeath(old.deathMonitor, 0);
            }

            mConnection.onServiceDisconnected(name);

        }



继而搜了一下大致何时调用的:

也就是说binder对象死了后onServiceConnected才会被调用到。

我试了下把server对应的apk禁用和卸载都不会起效,但是点击add按钮后会报错了:


猜你喜欢

转载自blog.csdn.net/sinat_20059415/article/details/79770615