前言:写了个aidl的例子写了一个多小时,Service恁是起不来,最后灵光一闪,手机预制的xx管家会限制自启动和关联启动,emmmm,关了就好了, 真心累。
demo地址:
Server:点击打开链接
Client: 点击打开链接
1. aidl介绍
抄下百度百科的介绍
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按钮后会报错了: