上一篇我们介绍了在Java层如何跨层访问在Native(C、C++)层注册的Binder service。这一篇我们打算反过来,在Native(C、C++)层跨层访问Java层注册的Binder service。如果对上一篇有初步了解的话,对这一篇的实现应该也不会有太多的问题,毕竟都是通过Binder调用。老样子,本篇的示例代码也是在没有添加selinux权限下进行的
Java层实现service
Java层在AIDL的帮助下,实现service会比较简单,所以接下来service的实现我们就不用AIDL的方式了,直接是自己写代码进行实现(嗯,比较作,偏不走简单的路,真是作死)
ISayHelloServiceInterface.java
1 2 3 4 5 6 7 8 9 |
package com.jimmy.sayhelloservice; import android.os.IInterface; public interface ISayHelloServiceInterface extends IInterface { void sayHello(String str) throws android.os.RemoteException; void displayDialog(String str) throws android.os.RemoteException; } |
和C、C++实现service有点类似哈,这里也是先继承IInterface,作为service服务端和客户端都需要实现的基类,下面看服务端的代码实现
SayHelloService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
package com.jimmy.sayhelloservice; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.os.Binder; import android.os.CountDownTimer; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Parcel; import android.os.RemoteException; import android.util.Log; import android.view.WindowManager; import android.widget.Button; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.util.Locale; import java.util.concurrent.TimeUnit; public class SayHelloService extends Binder implements ISayHelloServiceInterface { private static final String DESCRIPTOR = "com.jimmy.sayhelloservice.SayHelloService"; private Context mContext = null; private static final int DISPLAY_DIALOG = 0; public SayHelloService(Context mContext) { this.mContext = mContext; attachInterface(this, DESCRIPTOR); } @Override public void sayHello(String str) throws RemoteException { Log.e("JIMMY", "111sayHello str = " + str); } // 实现两个基类的方法 @Override public void displayDialog(String str) throws RemoteException { Message message = handler.obtainMessage(); message.what = DISPLAY_DIALOG; message.obj = str; handler.sendMessage(message); } @Override public IBinder asBinder() { return this; } // asInterface的作用是根据调用是否属于同进程而返回不同的实例对象 public static ISayHelloServiceInterface asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iInterface = obj.queryLocalInterface(DESCRIPTOR); if (((iInterface != null)&&(iInterface instanceof SayHelloService))) { return ((SayHelloService)iInterface); } return new SayHelloServiceManager(obj); } // 这里做一个弹框功能,弹框需要运行在Hanlder中 Handler handler = new Handler() { @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); switch (msg.what) { case DISPLAY_DIALOG: { String str = (String)msg.obj; AlertDialog.Builder builder = new AlertDialog.Builder(mContext); builder.setTitle("SayHelloService"); builder.setMessage("Message : " + str); builder.setIcon(R.mipmap.ic_launcher_round); builder.setCancelable(false); builder.setPositiveButton("YES", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Log.e("JIMMY", "SayHelloService displaydialog click yes!"); } }); builder.setNegativeButton("NO", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Log.e("JIMMY", "SayHelloServide displaydialog click no!"); } }); AlertDialog dialog = builder.create(); dialog.setOnShowListener(new DialogInterface.OnShowListener() { private static final int AUTO_DISMISS_MILIS = 15 * 1000; @Override public void onShow(final DialogInterface dialog) { Log.e("JIMMY", "SayHelloService dialog has been showed!"); final Button positiveButton = ((AlertDialog)dialog).getButton(AlertDialog.BUTTON_NEGATIVE); final CharSequence positiveButtonText = positiveButton.getText(); new CountDownTimer(AUTO_DISMISS_MILIS, 1000) { @Override public void onTick(long millisUntilFinished) { positiveButton.setText(String.format(Locale.getDefault(), "%s (%d)", positiveButtonText, TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) + 1)); } @Override public void onFinish() { if (((AlertDialog)dialog).isShowing()) { //dialog.dismiss(); } } }.start(); } }); dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { Log.e("JIMMY", "SayHelloService dialog has dismissed!"); } }); dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); Log.e("JIMMY", "111OnTransact dialog begin to show!"); dialog.show(); }; break; default: break; } } }; // onTransact方法,其实sayHello和displayDialog的具体代码也可以在这里实现,然后讲sayHello和displayDialog留空 @Override protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException { switch (code) { case TRANSACTION_sayhelloservice : { Log.e("JIMMY", "111onTransact TRANSACTION_sayhelloservice code!"); data.enforceInterface(DESCRIPTOR); String str = data.readString(); sayHello(str); } break; case TRANSACTION_displaydialog: { Log.e("JIMMY", "111onTransact TRANSACTION_displaydialog code!"); data.enforceInterface(DESCRIPTOR); String str = data.readString(); displayDialog(str); } break; default: break; } return super.onTransact(code, data, reply, flags); } // 定义两个Transact的定义值 static final int TRANSACTION_sayhelloservice = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_displaydialog = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } |
从这里看,其实自己使用Java手动实现一个binder service也不是特别复杂。接下来我们看客户端的代码。
SayHelloServiceManager.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
package com.jimmy.sayhelloservice; import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.util.Log; public class SayHelloServiceManager implements ISayHelloServiceInterface { private IBinder mRemote; private static final String DESCRIPTOR = "com.jimmy.sayhelloservice.SayHelloService"; SayHelloServiceManager(IBinder remote) { this.mRemote = remote; } @Override public void sayHello(String str) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(DESCRIPTOR); Log.e("JIMMY", "SayHelloServiceManager str = " + str); data.writeString(str); mRemote.transact(SayHelloService.TRANSACTION_sayhelloservice, data, reply, 0); reply.readException(); } finally { data.recycle(); reply.recycle(); } } @Override public void displayDialog(String str) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(DESCRIPTOR); data.writeString(str); mRemote.transact(SayHelloService.TRANSACTION_displaydialog, data, reply, 0); reply.readException(); } finally { data.recycle(); reply.recycle(); } } @Override public IBinder asBinder() { return mRemote; } } |
没什么特别的,需要注意的是要先将Token写到Parcel中,然后再讲需要发送的数据写到Parcel。否则可能会出现Service处理失败的情况。当然,如果只是想通过Java注册service的话,客户端的代码都可以不用实现的,不过博主是打算在APK里面进行一些简单的测试,所以客户端的代码也就一起实现了。最后是注册service和测试的代码了,博主统一写到MainActivity中了
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
package com.jimmy.sayhelloservice; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.view.View; import android.widget.Button; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class MainActivity extends AppCompatActivity { private Button btnSayHello; private Button btnShowDialog; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnSayHello = (Button) findViewById(R.id.sayhello); btnShowDialog = (Button) findViewById(R.id.dialog); Class<?> serviceManager = null; Method addServiceMethod = null; Method getServiceMethod = null; try { serviceManager = Class.forName("android.os.ServiceManager"); addServiceMethod = serviceManager.getDeclaredMethod("addService", String.class, IBinder.class); addServiceMethod.invoke(null, "sayHelloService", new SayHelloService(getApplicationContext())); } catch (Exception e) { e.printStackTrace(); } try { getServiceMethod = serviceManager.getDeclaredMethod("getService", String.class); IBinder proxy = (IBinder) getServiceMethod.invoke(null, "sayHelloService"); final SayHelloServiceManager manager = new SayHelloServiceManager(proxy); btnSayHello.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (manager != null) { try { manager.sayHello("hello, call manager.sayHello method"); } catch (RemoteException e) { e.printStackTrace(); } } } }); btnShowDialog.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (manager != null) { try { manager.displayDialog("show dialog"); } catch (RemoteException e) { e.printStackTrace(); } } } }); } catch (Exception e) { e.printStackTrace(); } } } |
布局有两个button,分别用来测试sayHello和displayDialog binder调用的。好的Java层的service 实现就到这里了,接下来就到了在native层怎么通过binder调用到这个Java service了。
Native(C、C++)调用service
ISayHelloService.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// // Created by jimmy on 2/29/20. // #ifndef ANDROID_ISAYHELLOSERVICE_H #define ANDROID_ISAYHELLOSERVICE_H #include <utils/Errors.h> // for status_t #include <utils/RefBase.h> #include <utils/String8.h> #include <binder/IInterface.h> #include <binder/Parcel.h> namespace android { class ISayHelloService : public IInterface { public: // 重要的宏定义,提供Service的asInterface方法和descriptor成员 DECLARE_META_INTERFACE(SayHelloService); // 实际工作的成员函数 virtual void sayHello(std::string /* str */) {}; virtual void displayDialog(std::string /* str */) {}; }; } #endif //ANDROID_ISAYHELLOSERVICE_H |
emmm,C、C++也是老样子的,定义一个公共头文件,继承IInterface,作为binder服务端和客户端都需要实现的公共类。因为我们是希望在Native中通过binder调用Java层的service,所以后面我们只需要实现客户端的代码即可
ISayHelloService.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
// // Created by jimmy on 2/29/20. // #define LOG_TAG "JIMMY" #include "include/ISayHelloService.h" #include <utils/Errors.h> #include <utils/String8.h> // namespace android{ enum { SAY_HELLO = IBinder::FIRST_CALL_TRANSACTION, DISPLAY_DIALOG }; class BpSayHelloService : public BpInterface<ISayHelloService> { public: BpSayHelloService(const sp<IBinder>& impl) : BpInterface<ISayHelloService>(impl) { } virtual void sayHello(std::string str) { ALOGD("sayHello() getInterfaceDescriptor = %s", String8(ISayHelloService::getInterfaceDescriptor()).string()); Parcel data, reply; data.writeInterfaceToken(String16(ISayHelloService::getInterfaceDescriptor())); data.writeString16(String16(str.c_str())); remote()->transact(SAY_HELLO, data, &reply); int code = reply.readExceptionCode(); ALOGD("sayHello() readExceptionCode = %d", code); return; } virtual void displayDialog(std::string str) { ALOGD("displayDialog() getInterfaceDescriptor = %s", String8(ISayHelloService::getInterfaceDescriptor()).string()); Parcel data, reply; data.writeInterfaceToken(String16(ISayHelloService::getInterfaceDescriptor())); data.writeString16(String16(str.c_str())); remote()->transact(DISPLAY_DIALOG, data, &reply); int code = reply.readExceptionCode(); ALOGD("displayDialog() readExceptionCode = %d", code); return; } }; // 关键的宏,完成DECLARE_META_INTERFACE宏中定义的方法 IMPLEMENT_META_INTERFACE(SayHelloService, "com.jimmy.sayhelloservice.SayHelloService"); } |
这个就是客户端的代码,文件名起得不好,记住这个是客户端的代码就行了。另外就是两个定义宏了DECLARE_META_INTERFACE和IMPLEMENT_META_INTERFACE宏了,接下来我们看看客户端的实现代码。
TestClient.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#define LOG_TAG "JIMMY" #include <fcntl.h> #include <sys/prctl.h> #include <sys/wait.h> #include <binder/IPCThreadState.h> #include <binder/ProcessState.h> #include <binder/IServiceManager.h> #include <cutils/properties.h> #include <utils/Log.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/socket.h> #include <utils/String16.h> #include "include/ISayHelloService.h" int main(void) { /* get service */ android::sp<android::IServiceManager> sm = android::defaultServiceManager(); android::sp<android::IBinder> binder = sm->getService(android::String16("sayHelloService")); if (binder == NULL) { ALOGD("client get service return NULL"); return -1; } android::sp<android::ISayHelloService> service = android::interface_cast<android::ISayHelloService>(binder); service->sayHello("Say hello from native!"); service->displayDialog("Display dialog from native!"); return 0; } |
常规流程,获取service,然后调用就对了。最后就是Android.mk了
Android.mk
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ ISayHelloService.cpp LOCAL_SHARED_LIBRARIES := \ libbinder \ libcutils \ liblog \ libutils \ LOCAL_C_INCLUDES := \ $(TOP)/frameworks/arithmetic/include \ $(TOP)/frameworks/native/include LOCAL_CLANG := true LOCAL_MODULE := libsayhelloservice include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ TestClient.cpp LOCAL_SHARED_LIBRARIES := \ libbinder \ libcutils \ liblog \ libutils \ libsayhelloservice \ LOCAL_C_INCLUDES := \ $(TOP)/frameworks/arithmetic/include \ $(TOP)/frameworks/native/include LOCAL_CLANG := true LOCAL_MODULE := test_sayhello_client include $(BUILD_EXECUTABLE) include $(call all-makefiles-under,$(LOCAL_PATH)) |
好了,最后push这个test_sayhello_client可执行程序push到system/bin 然后先运行servcie所在的apk,apk会先往system_server注册service,然后执行test_sayhello_client就可以实现native(C、C++)跨层访问Java注册的service了。OK这篇到这里,各位看官有什么问题可以留言一起交流交流。