当前位置: 首页 > news >正文

手写一个Java Android Binder服务及源码分析

手写一个Java Android Binder服务及源码分析

  • 前言
  • 一、Java语言编写自己的Binder服务Demo
    • 1. binder服务demo功能介绍
    • 2. binder服务demo代码结构图
    • 3. binder服务demo代码实现
      • 3.1 IHelloService.aidl
      • 3.2 IHelloService.java(自动生成)
      • 3.3 HelloService.java
      • 3.4 TestServer.java
      • 3.5 TestClient.java
      • 3.6 编译binder服务的Android.mk文件
  • 二、C++编写的Binder服务Demo源码解析
    • 1. client端发送数据的实现机制
      • 1.1 添加服务源码分析
      • 1.2 获取服务源码分析
      • 1.3 client端执行服务函数源码分析
      • 1.4 ServiceManagerProxy中mRemote的构造源码分析
      • 1.5 IHelloService.Stub.Proxy中mRemote的构造源码分析
      • 1.6 已知mRemote是一个java BinderProxy对象,那么再分析下mRemote.transact
    • 2. server端读取数据/执行服务的实现机制
      • 2.1 app_process为服务创建binder线程,实现读取数据
      • 2.3 在某个线程的IPCThreadState的joinThreadPool函数中的循环里读取数据
      • 2.4 cookie中的数据什么时候存储的?
  • 后记

前言

上一篇文章手写一个C++ Android Binder服务及源码分析,我们详细讲解了如何用C++编写自己的Binder服务,并且对C++编写的binder服务源码进行分析,相信通过上篇文章我们已经可以很轻松的使用c++编写自己的binder服务,并对其实现机制有了深入了解。

本篇在讲解如何使用java语言编写自己的binder服务,并通过对代码的分析,深入了解java binder服务的实现机制。

其实java编写的binder服务本质是对C++ binder接口的封装,而C++编写的binder服务实质是对C语言binder接口的封装,而C编写的binder服务是对linux内核中binder驱动的调用,都是一环套一环,编程语言越高级,封装就越深,开发人员使用就越便捷。因此,建议在阅读本篇文章之前,先阅读手写一个C++ Android Binder服务及源码分析,这有利于你对binder服务的深入理解。

如果不满足于仅仅会使用java语言和C++语言编写自己的binder服务,而是想深入内核理解android binder,推荐继续阅读以下系列文章:
深入内核讲明白Android Binder【一】
深入内核讲明白Android Binder【二】
深入内核讲明白Android Binder【三】

android binder是android中各个服务进程进行沟通的桥梁,如果想要阅读android源码,binder是不可或缺的一个技术点,掌握binder技术原理,对于我们android源码的阅读和理解至关重要,这也是我断断续续花半年的时间,也一定要啃下binder源码的原因。后续我会输出更多android系统源码的文章,让我们一起成长为一个不仅仅停留在写android APP层面的程序员,而是成为一名可以审视/优化google源码的工程师,这条路很艰辛,但我们一起加油!

下面开始介绍binder系列文章的最后一篇文章。

一、Java语言编写自己的Binder服务Demo

1. binder服务demo功能介绍

我们继续采用上篇文章手写一个C++ Android Binder服务及源码分析中C++语言实现的binder服务功能,Java实现的binder服务也提供sayhello和sayhello_to两个函数供客户端调用。

2. binder服务demo代码结构图

  • IHelloService.aidl文件用于声明服务接口
  • IHelloService.java由IHelloService.aidl自动生成,其内容自动生成了服务端的代理类IHelloService.Stub.Proxy,客户端只需要通过Proxy类即可完成对服务端的跨进程调用
  • HelloService.java继承IHelloService.Stub类,完成服务的本地实现
  • TestServer.java 服务端的代码实现
  • TestClient.java客户端的代码实现
    在这里插入图片描述

3. binder服务demo代码实现

3.1 IHelloService.aidl

/** {@hide} */
interface IHelloService
{void sayhello();int sayhello_to(String name);
}

系统如何由aidl文件自动生成java类?

  1. aidl文件放在源代码目录:https://github.com/CyanogenMod/android_frameworks_base/tree/cm-14.1/core/java/android/os
  2. 在源代码目录添加aidl文件的路径:>https://github.com/CyanogenMod/android_frameworks_base/blob/cm-14.1/Android.mk
  3. mmm frameworks/bash

3.2 IHelloService.java(自动生成)

自动生成的java代码包含以下三个核心内容:

  1. IHelloService接口定义了sayhello,sayhello_to两个服务函数
  2. IHelloService.Stub抽象类实现了IHelloService接口,但并没有实现sayhello,sayhello_to这两个服务函数,因此这两个服务函数交由IHelloService.Stub的派生类HelloService实现,java的HelloService类的作用等同于C++中的BnHelloServie.cpp的功能
  3. IHelloService.Stub.Proxy类作为服务端的代理类,完全自动生成,用于被客户端直接调用,Proxy类实现了IHelloService接口的sayhello,sayhello_to两个函数,Proxy类等同于C++中的BpHelloService.cpp的功能
    在这里插入图片描述
/** This file is auto-generated.  DO NOT MODIFY.* Original file: frameworks/base/core/java/android/os/IHelloService.aidl*/
/** {@hide} */
public interface IHelloService extends android.os.IInterface
{/** Local-side IPC implementation stub class. *///抽象类Stub并没有实现IHelloService接口中的sayhello及sayhello_to函数,交给Stub的派生类去实现,派生类等同于C++中的BnHelloServie.cpp的功能public static abstract class Stub extends android.os.Binder implements IHelloService{private static final java.lang.String DESCRIPTOR = "IHelloService";/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR);}/*** Cast an IBinder object into an IHelloService interface,* generating a proxy if needed.*/public static IHelloService asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof IHelloService))) {return ((IHelloService)iin);}return new IHelloService.Stub.Proxy(obj);}@Override public android.os.IBinder asBinder(){return this;}//服务端根据不同的code,调用不同的服务函数@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_sayhello:{data.enforceInterface(DESCRIPTOR);this.sayhello();reply.writeNoException();return true;}case TRANSACTION_sayhello_to:{data.enforceInterface(DESCRIPTOR);java.lang.String _arg0;_arg0 = data.readString();int _result = this.sayhello_to(_arg0);reply.writeNoException();reply.writeInt(_result);return true;}}return super.onTransact(code, data, reply, flags);}//代理类Proxy,用于被客户端直接调用,等同于C++中的BpHelloService.cpp的功能private static class Proxy implements IHelloService{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 void sayhello() throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_sayhello, _data, _reply, 0);_reply.readException();}finally {_reply.recycle();_data.recycle();}}@Override public int sayhello_to(java.lang.String name) 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.writeString(name);mRemote.transact(Stub.TRANSACTION_sayhello_to, _data, _reply, 0);_reply.readException();_result = _reply.readInt();}finally {_reply.recycle();_data.recycle();}return _result;}}static final int TRANSACTION_sayhello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_sayhello_to = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);}public void sayhello() throws android.os.RemoteException;public int sayhello_to(java.lang.String name) throws android.os.RemoteException;

3.3 HelloService.java

继承自抽象类IHelloService.Stub,用于完成服务所提供函数的具体实现

import android.util.Slog;public class HelloService extends IHelloService.Stub {private static final String TAG = "HelloService";private int cnt1 = 0;private int cnt2 = 0;public void sayhello() throws android.os.RemoteException {cnt1++;Slog.i(TAG, "sayhello : cnt = "+cnt1);}public int sayhello_to(java.lang.String name) throws android.os.RemoteException {cnt2++;Slog.i(TAG, "sayhello_to "+name+" : cnt = "+cnt2);return cnt2;}
}

3.4 TestServer.java

binder服务端代码实现,本质过程还是添加服务,循环等待客户端数据,只是java中循环等待的过程交给了app_process启动的binder子线程完成。

import android.util.Slog;
import android.os.ServiceManager;/* 1. addService* 2. while(true) { read data, parse data, call function, reply }*/public class TestServer {private static final String TAG = "TestServer";public static void main(String args[]){/* add Service */Slog.i(TAG, "add hello service");ServiceManager.addService("hello", new HelloService());//只需要让主线程活着就可以了。//循环读数据,解析数据,调用函数的过程交由app_process启动程序时,创建的两个binder子线程完成while (true){try {Thread.sleep(100);} catch (Exception e){}}}
}

3.5 TestClient.java

binder客户端代码实现,本质过程就是获取服务,调用服务函数。

mport android.util.Slog;
import android.os.ServiceManager;
import android.os.IBinder;/* 1. getService* 2. 调用服务的sayhello,sayhello_to**//* test_client <hello|goodbye> [name] */public class TestClient {private static final String TAG = "TestClient";public static void main(String args[]){if (args.length == 0){System.out.println("Usage: need parameter: <hello|goodbye> [name]");return;}if (args[0].equals("hello")){/* 1. getService */IBinder binder = ServiceManager.getService("hello");if (binder == null){System.out.println("can not get hello service");Slog.i(TAG, "can not get hello service");return;}IHelloService svr = IHelloService.Stub.asInterface(binder);if (args.length == 1){try {svr.sayhello();System.out.println("call sayhello");Slog.i(TAG, "call sayhello");} catch (Exception e) {}}else{try {int cnt = svr.sayhello_to(args[1]);System.out.println("call sayhello_to "+args[1]+" : cnt = "+cnt);Slog.i(TAG, "call sayhello_to "+args[1]+" : cnt = "+cnt);} catch (Exception e) {System.out.println("call sayhello_to , err :"+e);Slog.i(TAG, "call sayhello_to , err : "+e);}}}}
}

3.6 编译binder服务的Android.mk文件

# Copyright 2008 The Android Open Source Project
#
LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS)
LOCAL_SRC_FILES := HelloService.java IHelloService.java TestServer.java
LOCAL_MODULE := TestServer
include $(BUILD_JAVA_LIBRARY)include $(CLEAR_VARS)
LOCAL_SRC_FILES := HelloService.java IHelloService.java TestClient.java
LOCAL_MODULE := TestClient
include $(BUILD_JAVA_LIBRARY)

二、C++编写的Binder服务Demo源码解析

上面我们讲解了如何用java语言写自己的android binder服务,下面我们深入分析java实现的binder的服务的内部逻辑,我们知道java最终会调用到C++的封装,而C++的binder实现我们上一篇文章手写一个C++ Android Binder服务及源码分析,已经分析过了,所以,这里的源码解析就解析到C++层面。

1. client端发送数据的实现机制

1.1 添加服务源码分析

添加服务addService是通过ServiceManagerProxy对象中的addService函数实现的,而最终是通过mRemote.transact向servicemanager发送数据的。

public class TestServer {private static final String TAG = "TestServer";public static void main(String args[]){/* add Service */Slog.i(TAG, "add hello service");ServiceManager.addService("hello", new HelloService());//只需要让主线程活着就可以了。//循环读数据,解析数据,调用函数的过程交由app_process启动程序时,创建的两个binder子线程完成while (true){try {Thread.sleep(100);} catch (Exception e){}}}
}public static void addService(String name, IBinder service) {try {getIServiceManager().addService(name, service, false);} catch (RemoteException e) {Log.e(TAG, "error in addService", e);}
}private static IServiceManager getIServiceManager() {if (sServiceManager != null) {return sServiceManager;}// Find the service managersServiceManager = ServiceManagerNative.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));return sServiceManager;
}/*** Cast a Binder object into a service manager interface, generating* a proxy if needed.*/
static public IServiceManager asInterface(IBinder obj)
{if (obj == null) {return null;}IServiceManager in =(IServiceManager)obj.queryLocalInterface(descriptor);if (in != null) {return in;}return new ServiceManagerProxy(obj);
} // addService是通过ServiceManagerProxy对象中的addService函数实现的
//源码地址:https://github.com/CyanogenMod/android_frameworks_base/blob/cm-14.1/core/java/android/os/ServiceManagerNative.java#L109C7-L109C26
public void addService(String name, IBinder service, boolean allowIsolated)throws RemoteException {Parcel data = Parcel.obtain();Parcel reply = Parcel.obtain();data.writeInterfaceToken(IServiceManager.descriptor);data.writeString(name);data.writeStrongBinder(service);data.writeInt(allowIsolated ? 1 : 0);mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);reply.recycle();data.recycle();
}

1.2 获取服务源码分析

客户端获取服务getService是通过ServiceManagerProxy对象中的getService函数实现的,而最终也是通过mRemote.transact向servicemanager发送数据的。

public class TestClient {private static final String TAG = "TestClient";public static void main(String args[]){if (args.length == 0){System.out.println("Usage: need parameter: <hello|goodbye> [name]");return;}if (args[0].equals("hello")){/* 1. getService */IBinder binder = ServiceManager.getService("hello");if (binder == null){System.out.println("can not get hello service");Slog.i(TAG, "can not get hello service");return;}IHelloService svr = IHelloService.Stub.asInterface(binder);if (args.length == 1){try {svr.sayhello();System.out.println("call sayhello");Slog.i(TAG, "call sayhello");} catch (Exception e) {}}else{try {int cnt = svr.sayhello_to(args[1]);System.out.println("call sayhello_to "+args[1]+" : cnt = "+cnt);Slog.i(TAG, "call sayhello_to "+args[1]+" : cnt = "+cnt);} catch (Exception e) {System.out.println("call sayhello_to , err :"+e);Slog.i(TAG, "call sayhello_to , err : "+e);}}}}
}/*** Returns a reference to a service with the given name.** @param name the name of the service to get* @return a reference to the service, or <code>null</code> if the service doesn't exist* @hide*/
@UnsupportedAppUsage
public static IBinder getService(String name) {try {IBinder service = sCache.get(name);if (service != null) {return service;} else {return Binder.allowBlocking(rawGetService(name));}} catch (RemoteException e) {Log.e(TAG, "error in getService", e);}return null;
}private static IBinder rawGetService(String name) throws RemoteException {final long start = sStatLogger.getTime();final IBinder binder = getIServiceManager().getService(name);......return binder;}
}private static IServiceManager getIServiceManager() {if (sServiceManager != null) {return sServiceManager;}// Find the service managersServiceManager = ServiceManagerNative.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));return sServiceManager;
}/*** Cast a Binder object into a service manager interface, generating* a proxy if needed.*/
static public IServiceManager asInterface(IBinder obj)
{if (obj == null) {return null;}IServiceManager in =(IServiceManager)obj.queryLocalInterface(descriptor);if (in != null) {return in;}return new ServiceManagerProxy(obj);
}   // getService是通过ServiceManagerProxy对象中的getService函数实现的
//源码地址:https://github.com/CyanogenMod/android_frameworks_base/blob/cm-14.1/core/java/android/os/ServiceManagerNative.java#L109C7-L109C26
public IBinder getService(String name) throws RemoteException {Parcel data = Parcel.obtain();Parcel reply = Parcel.obtain();data.writeInterfaceToken(IServiceManager.descriptor);data.writeString(name);mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);IBinder binder = reply.readStrongBinder();reply.recycle();data.recycle();return binder;
}

1.3 client端执行服务函数源码分析

client端使用服务是通过调用IHelloService.Stub.Proxy类中方法实现的,而最终也是通过mRemote.transact向服务端发送数据的。

public class TestClient {private static final String TAG = "TestClient";public static void main(String args[]){if (args.length == 0){System.out.println("Usage: need parameter: <hello|goodbye> [name]");return;}if (args[0].equals("hello")){/* 1. getService */IBinder binder = ServiceManager.getService("hello");if (binder == null){System.out.println("can not get hello service");Slog.i(TAG, "can not get hello service");return;}// 获得服务的代理类,IHelloService.Stub.Proxy对象IHelloService svr = IHelloService.Stub.asInterface(binder);if (args.length == 1){try {// 执行服务函数svr.sayhello();System.out.println("call sayhello");Slog.i(TAG, "call sayhello");} catch (Exception e) {}}else{try {// 执行服务函数int cnt = svr.sayhello_to(args[1]);System.out.println("call sayhello_to "+args[1]+" : cnt = "+cnt);Slog.i(TAG, "call sayhello_to "+args[1]+" : cnt = "+cnt);} catch (Exception e) {System.out.println("call sayhello_to , err :"+e);Slog.i(TAG, "call sayhello_to , err : "+e);}}}}
}@Override public void sayhello() throws android.os.RemoteException
{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);// 向服务端发送数据mRemote.transact(Stub.TRANSACTION_sayhello, _data, _reply, 0);_reply.readException();}finally {_reply.recycle();_data.recycle();}
}
@Override public int sayhello_to(java.lang.String name) 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.writeString(name);// 向服务端发送数据mRemote.transact(Stub.TRANSACTION_sayhello_to, _data, _reply, 0);_reply.readException();_result = _reply.readInt();}finally {_reply.recycle();_data.recycle();}return _result;
}

由上面的分析可以得知java端跨进程发送数据都是通过代理类中的mRemote.transact函数实现,那么servicemanager的代理ServiceManagerProxy中mRemote和HelloService的代理类IHelloService.Stub.Proxy中mRemote到底是什么呢?我们下面接着分析。
在这里插入图片描述

1.4 ServiceManagerProxy中mRemote的构造源码分析

  • ServiceManager.addService添加服务,构造ServiceManagerProxy对象时传入的参数就是mRemote
  • 该参数由BinderInternal.getContextObject()方法获得,该方法是一个native方法,即通过JNI才可以调用的由C++实现的方法,该C++函数最终得到一个Java BinderProxy对象,其中mObject指向new BpBinder(0),BpBinder中的mHandle属性等于0,代表目的进程,即servicemanager进程。
    因此,mRemote是一个由C++程序创建的Java BinderProxy对象,其中mObject指向new BpBinder(0)
 public class TestServer {private static final String TAG = "TestServer";public static void main(String args[]){/* add Service */Slog.i(TAG, "add hello service");ServiceManager.addService("hello", new HelloService());while (true){try {Thread.sleep(100);} catch (Exception e){}}}
}public static void addService(String name, IBinder service) {try {getIServiceManager().addService(name, service, false);} catch (RemoteException e) {Log.e(TAG, "error in addService", e);}
}private static IServiceManager getIServiceManager() {if (sServiceManager != null) {return sServiceManager;}// Find the service manager//BinderInternal.getContextObject()就是mRemotesServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());return sServiceManager;
}static public IServiceManager asInterface(IBinder obj)
{if (obj == null) {return null;}IServiceManager in =(IServiceManager)obj.queryLocalInterface(descriptor);if (in != null) {return in;}return new ServiceManagerProxy(obj);
}public ServiceManagerProxy(IBinder remote) {mRemote = remote;
}/*** Return the global "context object" of the system.  This is usually* an implementation of IServiceManager, which you can use to find* other services.*/
// C或C++函数,通过JNI调用。具体的实现代码,在source insight工具中可以通过ctrl + /进行搜索,找到这个JNI定义的地方
//该函数最终得到一个Java BinderProxy对象,其中mObject指向new BpBinder(0)
public static final native IBinder getContextObject();//android_util_Binder.cpp
static const JNINativeMethod gBinderInternalMethods[] = {/* name, signature, funcPtr */{ "getContextObject", "()Landroid/os/IBinder;", (void*)android_os_BinderInternal_getContextObject },{ "joinThreadPool", "()V", (void*)android_os_BinderInternal_joinThreadPool },{ "disableBackgroundScheduling", "(Z)V", (void*)android_os_BinderInternal_disableBackgroundScheduling },{ "setMaxThreads", "(I)V", (void*)android_os_BinderInternal_setMaxThreads },{ "handleGc", "()V", (void*)android_os_BinderInternal_handleGc }
};static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{sp<IBinder> b = ProcessState::self()->getContextObject(NULL);return javaObjectForIBinder(env, b); //b = new BpBinder(0)
}//到这儿,就和C++的实现接上了
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{return getStrongProxyForHandle(0);
}sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{sp<IBinder> result;AutoMutex _l(mLock);handle_entry* e = lookupHandleLocked(handle);if (e != NULL) {// We need to create a new BpBinder if there isn't currently one, OR we// are unable to acquire a weak reference on this current one.  See comment// in getWeakProxyForHandle() for more info about this.IBinder* b = e->binder;if (b == NULL || !e->refs->attemptIncWeak(this)) {if (handle == 0) {// Special case for context manager...// The context manager is the only object for which we create// a BpBinder proxy without already holding a reference.// Perform a dummy transaction to ensure the context manager// is registered before we create the first local reference// to it (which will occur when creating the BpBinder).// If a local reference is created for the BpBinder when the// context manager is not present, the driver will fail to// provide a reference to the context manager, but the// driver API does not return status.//// Note that this is not race-free if the context manager// dies while this code runs.//// TODO: add a driver API to wait for context manager, or// stop special casing handle 0 for context manager and add// a driver API to get a handle to the context manager with// proper reference counting.Parcel data;status_t status = IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0);if (status == DEAD_OBJECT)return NULL;}b = new BpBinder(handle); //mHandle = 0e->binder = b;if (b) e->refs = b->getWeakRefs();result = b;} else {// This little bit of nastyness is to allow us to add a primary// reference to the remote proxy when this team doesn't have one// but another team is sending the handle to us.result.force_set(b);e->refs->decWeak(this);}}return result;
}jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{if (val == NULL) return NULL;if (val->checkSubclass(&gBinderOffsets)) {// One of our own!jobject object = static_cast<JavaBBinder*>(val.get())->object();LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);return object;}// For the rest of the function we will hold this lock, to serialize// looking/creation/destruction of Java proxies for native Binder proxies.AutoMutex _l(mProxyLock);// Someone else's...  do we know about it?jobject object = (jobject)val->findObject(&gBinderProxyOffsets);if (object != NULL) {jobject res = jniGetReferent(env, object);if (res != NULL) {ALOGV("objectForBinder %p: found existing %p!\n", val.get(), res);return res;}LOGDEATH("Proxy object %p of IBinder %p no longer in working set!!!", object, val.get());android_atomic_dec(&gNumProxyRefs);val->detachObject(&gBinderProxyOffsets);env->DeleteGlobalRef(object);}//使用C++代码来创建java BinderProxy对象object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);if (object != NULL) {LOGDEATH("objectForBinder %p: created new proxy %p !\n", val.get(), object);// The proxy holds a reference to the native object.//设置BinderProxy对象中的object = val.get = new BpBinder(0)env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());val->incStrong((void*)javaObjectForIBinder);// The native object needs to hold a weak reference back to the// proxy, so we can retrieve the same proxy if it is still active.jobject refObject = env->NewGlobalRef(env->GetObjectField(object, gBinderProxyOffsets.mSelf));val->attachObject(&gBinderProxyOffsets, refObject,jnienv_to_javavm(env), proxy_cleanup);// Also remember the death recipients registered on this proxysp<DeathRecipientList> drl = new DeathRecipientList;drl->incStrong((void*)javaObjectForIBinder);env->SetLongField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jlong>(drl.get()));// Note that a new object reference has been created.android_atomic_inc(&gNumProxyRefs);incRefsCreated(env);}return object;
}const char* const kBinderProxyPathName = "android/os/BinderProxy";static int int_register_android_os_BinderProxy(JNIEnv* env)
{jclass clazz = FindClassOrDie(env, "java/lang/Error");gErrorOffsets.mClass = MakeGlobalRefOrDie(env, clazz);clazz = FindClassOrDie(env, kBinderProxyPathName);gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);//构造的就是"android/os/BinderProxy"的对象gBinderProxyOffsets.mConstructor = GetMethodIDOrDie(env, clazz, "<init>", "()V");......
}

1.5 IHelloService.Stub.Proxy中mRemote的构造源码分析

  • 客户端获取使用服务时先调用IHelloService.Stub.asInterface(binder)获得IHelloService.Stub.Proxy对象
  • 而传入Proxy构造函数的参数binder就是mRemote,即mRemote就是 ServiceManager.getService(“hello”)拿到的IBinder
  • 而ServiceManager.getService最终使用JNI调用C++实现的nativeReadStrongBinder函数,该函数会创建一个java BinderProxy对象
  • 而BinderProxy对象中的mObject = parcel->readStrongBinder()
  • 而parcel->readStrongBinder()就是服务端的handle值。

因此,mRemote是一个由C++程序创建的Java BinderProxy对象,其中mObject指向new BpBinder(handle),handle是从service_manager中返回的对应hello服务的值。

public class TestClient {private static final String TAG = "TestClient";public static void main(String args[]){if (args.length == 0){System.out.println("Usage: need parameter: <hello|goodbye> [name]");return;}if (args[0].equals("hello")){/* 1. getService */IBinder binder = ServiceManager.getService("hello");if (binder == null){System.out.println("can not get hello service");Slog.i(TAG, "can not get hello service");return;}IHelloService svr = IHelloService.Stub.asInterface(binder);if (args.length == 1){try {svr.sayhello();System.out.println("call sayhello");Slog.i(TAG, "call sayhello");} catch (Exception e) {}}else{try {int cnt = svr.sayhello_to(args[1]);System.out.println("call sayhello_to "+args[1]+" : cnt = "+cnt);Slog.i(TAG, "call sayhello_to "+args[1]+" : cnt = "+cnt);} catch (Exception e) {System.out.println("call sayhello_to , err :"+e);Slog.i(TAG, "call sayhello_to , err : "+e);}}}}
}/*** Cast an IBinder object into an IHelloService interface,* generating a proxy if needed.*/
public static IHelloService asInterface(android.os.IBinder obj)
{if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof IHelloService))) {return ((IHelloService)iin);}return new IHelloService.Stub.Proxy(obj);
}private static class Proxy implements IHelloService
{private android.os.IBinder mRemote;Proxy(android.os.IBinder remote){//mRemote就是getService("hello")拿到的IBindermRemote = remote;}......}// 由上面的2可知,IBinder binder = ServiceManager.getService("hello");
// 是通过ServiceManagerProxy对象中的getService函数实现的
// 源码地址:https://github.com/CyanogenMod/android_frameworks_base/blob/cm-14.1/core/java/android/os/ServiceManagerNative.java#L109C7-L109C26
public IBinder getService(String name) throws RemoteException {Parcel data = Parcel.obtain();Parcel reply = Parcel.obtain();data.writeInterfaceToken(IServiceManager.descriptor);data.writeString(name);mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);IBinder binder = reply.readStrongBinder();//这个就是IHelloService.Stub.Proxy中mRemotereply.recycle();data.recycle();return binder;
}/*** Read an object from the parcel at the current dataPosition().*/
public final IBinder readStrongBinder() {return nativeReadStrongBinder(mNativePtr);
}private static native IBinder nativeReadStrongBinder(long nativePtr);//android_os_Parcel.cpp
static const JNINativeMethod gParcelMethods[] = {
......
{"nativeReadStrongBinder",    "(J)Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder}
......
}static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{//把java Parcel对象转换为C++ Parcel对象/*client程序向service_manager发出getService请求,得到一个回复reply,它里面含有flat_binder_object,然后flat_binder_object再此处被封装成一个C++的Parcel对象*/Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);if (parcel != NULL) {//上面5已经分析过这个函数。//它会创建一个java BinderProxy对象,而BinderProxy对象中的mObject = parcel->readStrongBinder()return javaObjectForIBinder(env, parcel->readStrongBinder());}return NULL;
}sp<IBinder> Parcel::readStrongBinder() const
{sp<IBinder> val;readStrongBinder(&val);return val;
}status_t Parcel::readStrongBinder(sp<IBinder>* val) const
{   //这个函数在C++的Binder实现中也分析过return unflatten_binder(ProcessState::self(), *this, val);
}status_t unflatten_binder(const sp<ProcessState>& proc,const Parcel& in, sp<IBinder>* out)
{const flat_binder_object* flat = in.readObject(false);if (flat) {switch (flat->type) {case BINDER_TYPE_BINDER:*out = reinterpret_cast<IBinder*>(flat->cookie);return finish_unflatten_binder(NULL, *flat, in);case BINDER_TYPE_HANDLE:// 通过handle创建BpBinder对象*out = proc->getStrongProxyForHandle(flat->handle);return finish_unflatten_binder(static_cast<BpBinder*>(out->get()), *flat, in);}}return BAD_TYPE;
}sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{sp<IBinder> result;AutoMutex _l(mLock);handle_entry* e = lookupHandleLocked(handle);if (e != NULL) {// We need to create a new BpBinder if there isn't currently one, OR we// are unable to acquire a weak reference on this current one.  See comment// in getWeakProxyForHandle() for more info about this.IBinder* b = e->binder;if (b == NULL || !e->refs->attemptIncWeak(this)) {if (handle == 0) {// Special case for context manager...// The context manager is the only object for which we create// a BpBinder proxy without already holding a reference.// Perform a dummy transaction to ensure the context manager// is registered before we create the first local reference// to it (which will occur when creating the BpBinder).// If a local reference is created for the BpBinder when the// context manager is not present, the driver will fail to// provide a reference to the context manager, but the// driver API does not return status.//// Note that this is not race-free if the context manager// dies while this code runs.//// TODO: add a driver API to wait for context manager, or// stop special casing handle 0 for context manager and add// a driver API to get a handle to the context manager with// proper reference counting.Parcel data;status_t status = IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0);if (status == DEAD_OBJECT)return NULL;}// 创建BpBinder对象b = new BpBinder(handle); e->binder = b;if (b) e->refs = b->getWeakRefs();result = b;} else {// This little bit of nastyness is to allow us to add a primary// reference to the remote proxy when this team doesn't have one// but another team is sending the handle to us.result.force_set(b);e->refs->decWeak(this);}}return result;
}

1.6 已知mRemote是一个java BinderProxy对象,那么再分析下mRemote.transact

最终调用到C++对象BpBinder的transact函数,到这就和C++的Binder实现接上了,BpBinder的transact函数在手写一个C++ Android Binder服务及源码分析中已经详细解释,这里不再赘述。

//Binder.javapublic boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");if (Binder.isTracingEnabled()) { Binder.getTransactionTracker().addTrace(); }return transactNative(code, data, reply, flags);
}public native boolean transactNative(int code, Parcel data, Parcel reply,int flags) throws RemoteException;//android_util_Binder.cpp 
static const JNINativeMethod gBinderProxyMethods[] = {/* name, signature, funcPtr */{"pingBinder",          "()Z", (void*)android_os_BinderProxy_pingBinder},{"isBinderAlive",       "()Z", (void*)android_os_BinderProxy_isBinderAlive},{"getInterfaceDescriptor", "()Ljava/lang/String;", (void*)android_os_BinderProxy_getInterfaceDescriptor},{"transactNative",      "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},{"linkToDeath",         "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},{"unlinkToDeath",       "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},{"destroy",             "()V", (void*)android_os_BinderProxy_destroy},
};static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{if (dataObj == NULL) {jniThrowNullPointerException(env, NULL);return JNI_FALSE;}Parcel* data = parcelForJavaObject(env, dataObj);if (data == NULL) {return JNI_FALSE;}Parcel* reply = parcelForJavaObject(env, replyObj);if (reply == NULL && replyObj != NULL) {return JNI_FALSE;}//从java BinderProxy对象中取出mObject,它就是一个BpBinder对象IBinder* target = (IBinder*)env->GetLongField(obj, gBinderProxyOffsets.mObject);if (target == NULL) {jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");return JNI_FALSE;}ALOGV("Java code calling transact on %p in Java object %p with code %" PRId32 "\n",target, obj, code);bool time_binder_calls;int64_t start_millis;if (kEnableBinderSample) {// Only log the binder call duration for things on the Java-level main thread.// But if we don'ttime_binder_calls = should_time_binder_calls();if (time_binder_calls) {start_millis = uptimeMillis();}}//printf("Transact from Java code to %p sending: ", target); data->print();//到这就和C++的Binder实现接上了status_t err = target->transact(code, *data, reply, flags);//if (reply) printf("Transact from Java code to %p received: ", target); reply->print();if (kEnableBinderSample) {if (time_binder_calls) {conditionally_log_binder_call(start_millis, target, code);}}if (err == NO_ERROR) {return JNI_TRUE;} else if (err == UNKNOWN_TRANSACTION) {return JNI_FALSE;}signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/, data->dataSize());return JNI_FALSE;
}

2. server端读取数据/执行服务的实现机制

public class TestServer {private static final String TAG = "TestServer";public static void main(String args[]){/* add Service */Slog.i(TAG, "add hello service");ServiceManager.addService("hello", new HelloService());//只需要让主线程活着就可以了。//循环读数据,解析数据,调用函数的过程交由app_process启动程序时,创建的两个binder子线程完成while (true){try {Thread.sleep(100);} catch (Exception e){}}}
}

2.1 app_process为服务创建binder线程,实现读取数据

app_process源代码地址:app_process/app_main.cpp

  • app_main运行TestServer服务后,它最终会导致app_main.cpp中AppRuntime类的onStarted函数被调用
  • onStarted函数中通过ProcessState.startThreadPool创建子线程
  • 最终在PoolThread中的threadLoop函数中调用IPCThreadState::self()->joinThreadPool(mIsMain)来读取/处理客户端数据
  • IPCThreadState::self()->joinThreadPool在手写一个C++ Android Binder服务及源码分析中已经详细解释,这里不再赘述。
int main(int argc, char* const argv[])
{if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {// Older kernels don't understand PR_SET_NO_NEW_PRIVS and return// EINVAL. Don't die on such kernels.if (errno != EINVAL) {LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));return 12;}}AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));// Process command line arguments// ignore argv[0]argc--;argv++;// Everything up to '--' or first non '-' arg goes to the vm.//// The first argument after the VM args is the "parent dir", which// is currently unused.//// After the parent dir, we expect one or more the following internal// arguments ://// --zygote : Start in zygote mode// --start-system-server : Start the system server.// --application : Start in application (stand alone, non zygote) mode.// --nice-name : The nice name for this process.//// For non zygote starts, these arguments will be followed by// the main class name. All remaining arguments are passed to// the main method of this class.//// For zygote starts, all remaining arguments are passed to the zygote.// main function.//// Note that we must copy argument string values since we will rewrite the// entire argument block when we apply the nice name to argv0.int i;for (i = 0; i < argc; i++) {if (argv[i][0] != '-') {break;}if (argv[i][1] == '-' && argv[i][2] == 0) {++i; // Skip --.break;}runtime.addOption(strdup(argv[i]));}// Parse runtime arguments.  Stop at first unrecognized option.bool zygote = false;bool startSystemServer = false;bool application = false;String8 niceName;String8 className;++i;  // Skip unused "parent dir" argument.while (i < argc) {const char* arg = argv[i++];if (strcmp(arg, "--zygote") == 0) {zygote = true;niceName = ZYGOTE_NICE_NAME;} else if (strcmp(arg, "--start-system-server") == 0) {startSystemServer = true;} else if (strcmp(arg, "--application") == 0) {application = true;} else if (strncmp(arg, "--nice-name=", 12) == 0) {niceName.setTo(arg + 12);} else if (strncmp(arg, "--", 2) != 0) {className.setTo(arg);break;} else {--i;break;}}Vector<String8> args;if (!className.isEmpty()) {// We're not in zygote mode, the only argument we need to pass// to RuntimeInit is the application argument.//// The Remainder of args get passed to startup class main(). Make// copies of them before we overwrite them with the process name.args.add(application ? String8("application") : String8("tool"));runtime.setClassNameAndArgs(className, argc - i, argv + i);} else {// We're in zygote mode.maybeCreateDalvikCache();if (startSystemServer) {args.add(String8("start-system-server"));}char prop[PROP_VALUE_MAX];if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",ABI_LIST_PROPERTY);return 11;}String8 abiFlag("--abi-list=");abiFlag.append(prop);args.add(abiFlag);// In zygote mode, pass all remaining arguments to the zygote// main() method.for (; i < argc; ++i) {args.add(String8(argv[i]));}}if (!niceName.isEmpty()) {runtime.setArgv0(niceName.string());set_process_name(niceName.string());}if (zygote) {runtime.start("com.android.internal.os.ZygoteInit", args, zygote);} else if (className) {runtime.start("com.android.internal.os.RuntimeInit", args, zygote);} else {fprintf(stderr, "Error: no class name or --zygote supplied.\n");app_usage();LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");return 10;}
}//我们暂且不分析app_main的细节,
//已知app_main运行TestServer服务后,它最终会导致AppRuntime类中的onStarted函数被调用virtual void onStarted()
{sp<ProcessState> proc = ProcessState::self();ALOGV("App process: starting thread pool.\n");//创建子线程proc->startThreadPool();AndroidRuntime* ar = AndroidRuntime::getRuntime();//调用TestServer里面的main函数,启动TestServer程序ar->callMain(mClassName, mClass, mArgs);IPCThreadState::self()->stopProcess();
}void ProcessState::startThreadPool()
{AutoMutex _l(mLock);if (!mThreadPoolStarted) {mThreadPoolStarted = true;spawnPooledThread(true);}
}void ProcessState::spawnPooledThread(bool isMain)
{if (mThreadPoolStarted) {String8 name = makeBinderThreadName();ALOGV("Spawning new pooled thread, name=%s\n", name.string());// 创建子线程sp<Thread> t = new PoolThread(isMain);// 运行子线程t->run(name.string());}
}status_t Thread::run(const char* name, int32_t priority, size_t stack)
{if (name == nullptr) {ALOGW("Thread name not provided to Thread::run");name = 0;}Mutex::Autolock _l(mLock);if (mRunning) {// thread already startedreturn INVALID_OPERATION;}// reset status and exitPending to their default value, so we can// try again after an error happened (either below, or in readyToRun())mStatus = NO_ERROR;mExitPending = false;mThread = thread_id_t(-1);// hold a strong reference on ourselfmHoldSelf = this;mRunning = true;bool res;if (mCanCallJava) {res = createThreadEtc(_threadLoop,this, name, priority, stack, &mThread);} else {//执行PoolThread中的threadLoop函数res = androidCreateRawThreadEtc(_threadLoop,this, name, priority, stack, &mThread);}if (res == false) {mStatus = UNKNOWN_ERROR;   // something happened!mRunning = false;mThread = thread_id_t(-1);mHoldSelf.clear();  // "this" may have gone away after this.return UNKNOWN_ERROR;}// Do not refer to mStatus here: The thread is already running (may, in fact// already have exited with a valid mStatus result). The NO_ERROR indication// here merely indicates successfully starting the thread and does not// imply successful termination/execution.return NO_ERROR;// Exiting scope of mLock is a memory barrier and allows new thread to run
}int Thread::_threadLoop(void* user)
{Thread* const self = static_cast<Thread*>(user);sp<Thread> strong(self->mHoldSelf);wp<Thread> weak(strong);self->mHoldSelf.clear();#if defined(__ANDROID__)// this is very useful for debugging with gdbself->mTid = gettid();
#endifbool first = true;do {bool result;if (first) {first = false;self->mStatus = self->readyToRun();result = (self->mStatus == NO_ERROR);if (result && !self->exitPending()) {// Binder threads (and maybe others) rely on threadLoop// running at least once after a successful ::readyToRun()// (unless, of course, the thread has already been asked to exit// at that point).// This is because threads are essentially used like this://   (new ThreadSubclass())->run();// The caller therefore does not retain a strong reference to// the thread and the thread would simply disappear after the// successful ::readyToRun() call instead of entering the// threadLoop at least once.result = self->threadLoop();}} else {result = self->threadLoop();}// establish a scope for mLock{Mutex::Autolock _l(self->mLock);if (result == false || self->mExitPending) {self->mExitPending = true;self->mRunning = false;// clear thread ID so that requestExitAndWait() does not exit if// called by a new thread using the same thread ID as this one.self->mThread = thread_id_t(-1);// note that interested observers blocked in requestExitAndWait are// awoken by broadcast, but blocked on mLock until break exits scopeself->mThreadExitedCondition.broadcast();break;}}// Release our strong reference, to let a chance to the thread// to die a peaceful death.strong.clear();// And immediately, re-acquire a strong reference for the next loopstrong = weak.promote();} while(strong != 0);return 0;
}class PoolThread : public Thread
{
public:PoolThread(bool isMain): mIsMain(isMain){}protected:virtual bool threadLoop(){//到这儿就和C++实现的Binder接起来了IPCThreadState::self()->joinThreadPool(mIsMain);return false;}const bool mIsMain;
};

2.3 在某个线程的IPCThreadState的joinThreadPool函数中的循环里读取数据

在某个线程的IPCThreadState的joinThreadPool函数中的循环里读取数据

  1. 读到的数据中含有.prt/.cookie
  2. 把cookie转换成BBinder对象
  3. 调用BBinder派生类JavaBBinder的transact函数
  4. C++调用Binder.java中的execTransact函数
  5. 调用IHelloService.java中的onTransact函数
  6. 调用服务端的本地函数
void IPCThreadState::joinThreadPool(bool isMain)
{LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid());mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);// This thread may have been spawned by a thread that was in the background// scheduling group, so first we will make sure it is in the foreground// one to avoid performing an initial transaction in the background.set_sched_policy(mMyThreadId, SP_FOREGROUND);status_t result;do {processPendingDerefs();// now get the next command to be processed, waiting if necessaryresult = getAndExecuteCommand();if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {ALOGE("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting",mProcess->mDriverFD, result);abort();}// Let this thread exit the thread pool if it is no longer// needed and it is not the main process thread.if(result == TIMED_OUT && !isMain) {break;}} while (result != -ECONNREFUSED && result != -EBADF);LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n",(void*)pthread_self(), getpid(), (void*)result);mOut.writeInt32(BC_EXIT_LOOPER);talkWithDriver(false);
}status_t IPCThreadState::getAndExecuteCommand()
{status_t result;int32_t cmd;//读取数据result = talkWithDriver();if (result >= NO_ERROR) {size_t IN = mIn.dataAvail();if (IN < sizeof(int32_t)) return result;cmd = mIn.readInt32();IF_LOG_COMMANDS() {alog << "Processing top-level Command: "<< getReturnString(cmd) << endl;}pthread_mutex_lock(&mProcess->mThreadCountLock);mProcess->mExecutingThreadsCount++;if (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads &&mProcess->mStarvationStartTimeMs == 0) {mProcess->mStarvationStartTimeMs = uptimeMillis();}pthread_mutex_unlock(&mProcess->mThreadCountLock);//分析数据result = executeCommand(cmd);pthread_mutex_lock(&mProcess->mThreadCountLock);mProcess->mExecutingThreadsCount--;if (mProcess->mExecutingThreadsCount < mProcess->mMaxThreads &&mProcess->mStarvationStartTimeMs != 0) {int64_t starvationTimeMs = uptimeMillis() - mProcess->mStarvationStartTimeMs;if (starvationTimeMs > 100) {ALOGE("binder thread pool (%zu threads) starved for %" PRId64 " ms",mProcess->mMaxThreads, starvationTimeMs);}mProcess->mStarvationStartTimeMs = 0;}pthread_cond_broadcast(&mProcess->mThreadCountDecrement);pthread_mutex_unlock(&mProcess->mThreadCountLock);// After executing the command, ensure that the thread is returned to the// foreground cgroup before rejoining the pool.  The driver takes care of// restoring the priority, but doesn't do anything with cgroups so we// need to take care of that here in userspace.  Note that we do make// sure to go in the foreground after executing a transaction, but// there are other callbacks into user code that could have changed// our group so we want to make absolutely sure it is put back.set_sched_policy(mMyThreadId, SP_FOREGROUND);}return result;
}status_t IPCThreadState::executeCommand(int32_t cmd)
{BBinder* obj;RefBase::weakref_type* refs;status_t result = NO_ERROR;switch ((uint32_t)cmd) {......// 处理数据case BR_TRANSACTION:{binder_transaction_data tr;result = mIn.read(&tr, sizeof(tr));ALOG_ASSERT(result == NO_ERROR,"Not enough command data for brTRANSACTION");if (result != NO_ERROR) break;Parcel buffer;buffer.ipcSetDataReference(reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),tr.data_size,reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);const pid_t origPid = mCallingPid;const uid_t origUid = mCallingUid;const int32_t origStrictModePolicy = mStrictModePolicy;const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags;mCallingPid = tr.sender_pid;mCallingUid = tr.sender_euid;mLastTransactionBinderFlags = tr.flags;int curPrio = getpriority(PRIO_PROCESS, mMyThreadId);if (gDisableBackgroundScheduling) {if (curPrio > ANDROID_PRIORITY_NORMAL) {// We have inherited a reduced priority from the caller, but do not// want to run in that state in this process.  The driver set our// priority already (though not our scheduling class), so bounce// it back to the default before invoking the transaction.setpriority(PRIO_PROCESS, mMyThreadId, ANDROID_PRIORITY_NORMAL);}} else {if (curPrio >= ANDROID_PRIORITY_BACKGROUND) {// We want to use the inherited priority from the caller.// Ensure this thread is in the background scheduling class,// since the driver won't modify scheduling classes for us.// The scheduling group is reset to default by the caller// once this method returns after the transaction is complete.set_sched_policy(mMyThreadId, SP_BACKGROUND);}}//ALOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid);Parcel reply;status_t error;IF_LOG_TRANSACTIONS() {TextOutput::Bundle _b(alog);alog << "BR_TRANSACTION thr " << (void*)pthread_self()<< " / obj " << tr.target.ptr << " / code "<< TypeCode(tr.code) << ": " << indent << buffer<< dedent << endl<< "Data addr = "<< reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer)<< ", offsets addr="<< reinterpret_cast<const size_t*>(tr.data.ptr.offsets) << endl;}if (tr.target.ptr) {// We only have a weak reference on the target object, so we must first try to// safely acquire a strong reference before doing anything else with it.if (reinterpret_cast<RefBase::weakref_type*>(tr.target.ptr)->attemptIncStrong(this)) {//将读取到的cookie转为BBinder,这个BBinder是派生类JavaBBinder,//执行BBinder的transact函数error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,&reply, tr.flags);reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);} else {error = UNKNOWN_TRANSACTION;}} else {error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);}//ALOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n",//     mCallingPid, origPid, origUid);if ((tr.flags & TF_ONE_WAY) == 0) {LOG_ONEWAY("Sending reply to %d!", mCallingPid);if (error < NO_ERROR) reply.setError(error);sendReply(reply, 0);} else {LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);}mCallingPid = origPid;mCallingUid = origUid;mStrictModePolicy = origStrictModePolicy;mLastTransactionBinderFlags = origTransactionBinderFlags;IF_LOG_TRANSACTIONS() {TextOutput::Bundle _b(alog);alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj "<< tr.target.ptr << ": " << indent << reply << dedent << endl;}}break;.......default:printf("*** BAD COMMAND %d received from Binder driver\n", cmd);result = UNKNOWN_ERROR;break;}if (result != NO_ERROR) {mLastError = result;}return result;
}status_t BBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{data.setDataPosition(0);status_t err = NO_ERROR;switch (code) {case PING_TRANSACTION:reply->writeInt32(pingBinder());break;default:err = onTransact(code, data, reply, flags);break;}if (reply != NULL) {reply->setDataPosition(0);}return err;
}//class JavaBBinder
class JavaBBinder : public BBinder
{
public:JavaBBinder(JNIEnv* env, jobject object): mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)){ALOGV("Creating JavaBBinder %p\n", this);android_atomic_inc(&gNumLocalRefs);incRefsCreated(env);}bool    checkSubclass(const void* subclassID) const{return subclassID == &gBinderOffsets;}jobject object() const{return mObject;}protected:virtual ~JavaBBinder(){ALOGV("Destroying JavaBBinder %p\n", this);android_atomic_dec(&gNumLocalRefs);JNIEnv* env = javavm_to_jnienv(mVM);env->DeleteGlobalRef(mObject);}virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0){JNIEnv* env = javavm_to_jnienv(mVM);ALOGV("onTransact() on %p calling object %p in env %p vm %p\n", this, mObject, env, mVM);IPCThreadState* thread_state = IPCThreadState::self();const int32_t strict_policy_before = thread_state->getStrictModePolicy();//printf("Transact from %p to Java code sending: ", this);//data.print();//printf("\n");//mObject指向Java的HelloService对象jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);if (env->ExceptionCheck()) {jthrowable excep = env->ExceptionOccurred();report_exception(env, excep,"*** Uncaught remote exception!  ""(Exceptions are not yet supported across processes.)");res = JNI_FALSE;/* clean up JNI local ref -- we don't return to Java code */env->DeleteLocalRef(excep);}// Check if the strict mode state changed while processing the// call.  The Binder state will be restored by the underlying// Binder system in IPCThreadState, however we need to take care// of the parallel Java state as well.if (thread_state->getStrictModePolicy() != strict_policy_before) {set_dalvik_blockguard_policy(env, strict_policy_before);}if (env->ExceptionCheck()) {jthrowable excep = env->ExceptionOccurred();report_exception(env, excep,"*** Uncaught exception in onBinderStrictModePolicyChange");/* clean up JNI local ref -- we don't return to Java code */env->DeleteLocalRef(excep);}// Need to always call through the native implementation of// SYSPROPS_TRANSACTION.if (code == SYSPROPS_TRANSACTION) {BBinder::onTransact(code, data, reply, flags);}//aout << "onTransact to Java code; result=" << res << endl//    << "Transact from " << this << " to Java code returning "//    << reply << ": " << *reply << endl;return res != JNI_FALSE ? NO_ERROR : UNKNOWN_TRANSACTION;}virtual status_t dump(int fd, const Vector<String16>& args){return 0;}private:JavaVM* const   mVM;jobject const   mObject;
};//gBinderOffsets.mExecTransac指向android/os/Binder java类中的execTransact方法
//而这个android/os/Binder java类就是它的派生类HelloService对象
gBinderOffsets.mExecTransact = GetMethodIDOrDie(env, clazz, "execTransact", "(IJJI)Z");
const char* const kBinderPathName = "android/os/Binder";
jclass clazz = FindClassOrDie(env, kBinderPathName);//Binder.java
private boolean execTransact(int code, long dataObj, long replyObj,int flags) {Parcel data = Parcel.obtain(dataObj);Parcel reply = Parcel.obtain(replyObj);// theoretically, we should call transact, which will call onTransact,// but all that does is rewind it, and we just got these from an IPC,// so we'll just call it directly.boolean res;// Log any exceptions as warnings, don't silently suppress them.// If the call was FLAG_ONEWAY then these exceptions disappear into the ether.try {//这个onTransact方法就是HelloService对象实现的方法(由其父类实现)res = onTransact(code, data, reply, flags);} catch (RemoteException|RuntimeException e) {if (LOG_RUNTIME_EXCEPTION) {Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);}if ((flags & FLAG_ONEWAY) != 0) {if (e instanceof RemoteException) {Log.w(TAG, "Binder call failed.", e);} else {Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);}} else {reply.setDataPosition(0);reply.writeException(e);}res = true;} catch (OutOfMemoryError e) {// Unconditionally log this, since this is generally unrecoverable.Log.e(TAG, "Caught an OutOfMemoryError from the binder stub implementation.", e);RuntimeException re = new RuntimeException("Out of memory", e);reply.setDataPosition(0);reply.writeException(re);res = true;}checkParcel(this, code, reply, "Unreasonably large binder reply buffer");reply.recycle();data.recycle();// Just in case -- we are done with the IPC, so there should be no more strict// mode violations that have gathered for this thread.  Either they have been// parceled and are now in transport off to the caller, or we are returning back// to the main transaction loop to wait for another incoming transaction.  Either// way, strict mode begone!StrictMode.clearGatheredViolations();return res;}
}//IHelloService.java
@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_sayhello:{data.enforceInterface(DESCRIPTOR);this.sayhello();reply.writeNoException();return true;}case TRANSACTION_sayhello_to:{data.enforceInterface(DESCRIPTOR);java.lang.String _arg0;_arg0 = data.readString();int _result = this.sayhello_to(_arg0);reply.writeNoException();reply.writeInt(_result);return true;}}return super.onTransact(code, data, reply, flags);
}public class HelloService extends IHelloService.Stub {private static final String TAG = "HelloService";private int cnt1 = 0;private int cnt2 = 0;public void sayhello() throws android.os.RemoteException {cnt1++;Slog.i(TAG, "sayhello : cnt = "+cnt1);}public int sayhello_to(java.lang.String name) throws android.os.RemoteException {cnt2++;Slog.i(TAG, "sayhello_to "+name+" : cnt = "+cnt2);return cnt2;}
}

2.4 cookie中的数据什么时候存储的?

猜测:

  1. new HelloService()是java对象
  2. 处理数据时把.cookie转换成BBinder对象,它是C++对象
  3. 所以,addService中肯定会把JAVA对象转换成一个BBinder派生类对象,存在.cookie中

结论:

  1. addService会通过JNI调用C++函数,创建一个BBinder派生类JavaBBinder对象,其中的mObject指向JAVA对象new HelloService(),它含有onTransact函数,然后把这个JavaBBinder对象存入flat_binder_object对象的cookie中(最终存入binder驱动中该服务对应的binder_node.cookie中)
  2. server进程从驱动中读到cookie,转为BBinder对象,调用它的transact函数,然后transact函数最终调用到BBinder的派生类即JavaBBinder中定义的onTransact函数(C++实现)
  3. 调用IHelloService中定义的onTranscat函数(JAVA实现),分析数据,调用sayhello/sayhello_to

代码验证:

public class TestServer {private static final String TAG = "TestServer";public static void main(String args[]){/* add Service */Slog.i(TAG, "add hello service");ServiceManager.addService("hello", new HelloService());//只需要让主线程活着就可以了。//循环读数据,解析数据,调用函数的过程交由app_process启动程序时,创建的两个binder子线程完成while (true){try {Thread.sleep(100);} catch (Exception e){}}}
}// 之前分析过,ServiceManager.addService,最终会调用ServiceManagerProxy类中的addService
public void addService(String name, IBinder service, boolean allowIsolated)throws RemoteException {Parcel data = Parcel.obtain();Parcel reply = Parcel.obtain();data.writeInterfaceToken(IServiceManager.descriptor);data.writeString(name);data.writeStrongBinder(service);data.writeInt(allowIsolated ? 1 : 0);mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);reply.recycle();data.recycle();
}/*** Write an object into the parcel at the current dataPosition(),* growing dataCapacity() if needed.*/
public final void writeStrongBinder(IBinder val) {nativeWriteStrongBinder(mNativePtr, val); // val = service = new HelloService()
}//android_os_Parcel.cpp
{"nativeWriteStrongBinder",   "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder},/*
该方法会构造一个JavaBBinder对象(C++对象),它的mObject=new HelloService()对象(java对象)
然后让.cookie=JavaBBinder
*/
static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{//把java的Parcel对象转换为C++的Parcel对象Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);if (parcel != NULL) {//ibinderForJavaObject是把一个java对象转为IBinderconst status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object)); //object = new HelloService()if (err != NO_ERROR) {signalExceptionF orError(env, clazz, err);}}
}
//把转换为IBinder的java对象写入cookie
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{return flatten_binder(ProcessState::self(), val, this);
}status_t flatten_binder(const sp<ProcessState>& /*proc*/,const sp<IBinder>& binder, Parcel* out)
{flat_binder_object obj;obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;if (binder != NULL) {IBinder *local = binder->localBinder();if (!local) {BpBinder *proxy = binder->remoteBinder();if (proxy == NULL) {ALOGE("null proxy");}const int32_t handle = proxy ? proxy->handle() : 0;obj.type = BINDER_TYPE_HANDLE;obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */obj.handle = handle;obj.cookie = 0;} else {obj.type = BINDER_TYPE_BINDER;obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());obj.cookie = reinterpret_cast<uintptr_t>(local);}} else {obj.type = BINDER_TYPE_BINDER;obj.binder = 0;obj.cookie = 0;}return finish_flatten_binder(binder, obj, out);
}sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj) //obj = new HelloService()
{if (obj == NULL) return NULL;if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {JavaBBinderHolder* jbh = (JavaBBinderHolder*)env->GetLongField(obj, gBinderOffsets.mObject);return jbh != NULL ? jbh->get(env, obj) : NULL;}if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {return (IBinder*)env->GetLongField(obj, gBinderProxyOffsets.mObject);}ALOGW("ibinderForJavaObject: %p is not a Binder object", obj);return NULL;
}class JavaBBinderHolder : public RefBase
{
public:sp<JavaBBinder> get(JNIEnv* env, jobject obj){AutoMutex _l(mLock);sp<JavaBBinder> b = mBinder.promote();if (b == NULL) {b = new JavaBBinder(env, obj); //obj = new HelloService()mBinder = b;ALOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%" PRId32 "\n",b.get(), b->getWeakRefs(), obj, b->getWeakRefs()->getWeakCount());}return b;}sp<JavaBBinder> getExisting(){AutoMutex _l(mLock);return mBinder.promote();}private:Mutex           mLock;wp<JavaBBinder> mBinder;
};JavaBBinder(JNIEnv* env, jobject object): mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)) //mObject指向了HelloService的java
{ALOGV("Creating JavaBBinder %p\n", this);android_atomic_inc(&gNumLocalRefs);incRefsCreated(env);
}

后记

如前言所说,android binder是android中各个服务进程进行沟通的桥梁,如果想要阅读android源码,binder是不可或缺的一个技术点,掌握binder技术原理,对于我们android源码的阅读和理解至关重要。
历经大半年的时间终于把android binder啃下来了,加上之前的四篇文章,android binder系列一共5篇文章,如果能够认真阅读完,相信会对各位博友有一定的帮助。
深入内核讲明白Android Binder【一】
深入内核讲明白Android Binder【二】
深入内核讲明白Android Binder【三】
手写一个C++ Android Binder服务及源码分析

再次感谢:
韦东山老师的Binder课程
说实话,binder不容易理解,老师的视频我应该看了5遍,再结合自己的源码分析,才有了一定的自我理解。

相关文章:

手写一个Java Android Binder服务及源码分析

手写一个Java Android Binder服务及源码分析 前言一、Java语言编写自己的Binder服务Demo1. binder服务demo功能介绍2. binder服务demo代码结构图3. binder服务demo代码实现3.1 IHelloService.aidl3.2 IHelloService.java&#xff08;自动生成&#xff09;3.3 HelloService.java…...

考研操作系统----操作系统的概念定义功能和目标(仅仅作为王道哔站课程讲义作用)

目录 操作系统的概念定义功能和目标 操作系统的四个特征 操作系统的分类 ​编辑 操作系统的运行机制 系统调用 操作系统体系结构 操作系统引导 虚拟机 操作系统的概念定义功能和目标 什么是操作系统&#xff1a; 操作系统是指控制和管理整个计算机系统的软硬件资源&…...

java.lang.IllegalArgumentException: 在请求目标中找到无效字符。有效字符在RFC 7230和RFC 3986中定义

Tomcat 屏蔽错误信息。java.lang.IllegalArgumentException: 在请求目标中找到无效字符。有效字符在RFC 7230和RFC 3986中定义 <h1>HTTP状态 400 - 错误的请求</h1><hr class"line" /><p><b>类型</b> 异常报告</p><p&…...

ros:ur机械臂初识

这是用来可视化的launch文件 比如&#xff0c;我运行 roslaunch ur_description view_ur3.launch ur3模型 ur3e模型 ur5模型 ur5e模型 ur10模型 ur20模型 ur30模型 后来我搜了一下 UR5 和 UR10 都是由 Universal Robots&#xff08;简称 UR&#xff09;生产的协作机器人&…...

计算机视觉-局部特征

一、局部特征 1.1全景拼接 先用RANSAC估计出变换&#xff0c;就可以拼接两张图片 ①提取特征 ②匹配特征 ③拼接图像 1.2 点的特征 怎么找到对应点&#xff1f;&#xff08;才能做点对应关系RANSAC&#xff09; &#xff1a;特征检测 我们希望找到的点具有的特征有什么特…...

LabVIEW袜品压力测试系统

开发了一种基于LabVIEW开发的袜品压力测试系统。该系统利用LabVIEW并结合灵敏的传感器和高精度的处理模块&#xff0c;实现了对袜品压力的精确测量和分析。系统不同于传统的服装压力测试方法&#xff0c;为研究和评价袜子的舒适性提供了新的测试手段。 ​ 项目背景 该系统的…...

Node.js 工具模块

Node.js 工具模块 引言 Node.js 是一个开源的、基于 Chrome V8 引擎的 JavaScript 运行时环境。它允许开发者使用 JavaScript 编写服务器端代码,从而构建快速、可扩展的网络应用。在 Node.js 开发过程中,工具模块扮演着至关重要的角色。本文将详细介绍 Node.js 中常用的工具…...

【Redis】下载安装Redis和Redis图形化界面工具教程(2024最新版本,史上最详细)

目录 一、Redis简介 二、Redis下载和安装 2.1、下载 2.2、安装 2.3、环境变量配置&#xff08;可省略&#xff09; 三、Redis启动验证 3.1、点击键盘上的WinR键&#xff0c;在跳出的运行界面中输入cmd并确定 3.2、输入redis-cli -v 查看redis的版本号 3.3、接着我们再…...

企业SSL 证书管理指南

文章从以下几个部分展开 SSL证书的用途和使用场景SSL证书的申请类型和实现方式SSL证书的管理SSL证书的续签 一、SSL 证书的用途和使用场景 1.1 为什么要使用 SSL 证书&#xff1f; 1. 数据安全 &#x1f6e1;️- 在 HTTP 传输中&#xff0c;TCP 包可以被截获&#xff0c;攻…...

JDK1.8新特性面试题

lambda表达式 Lambda表达式极大地简化了匿名内部类的创建&#xff0c;促进了函数式编程风格。开发者可以更简洁地定义只有一行代码的函数对象&#xff0c;并将其作为参数传递给方法或者赋值给变量。 三要素&#xff1a; 形式参数、箭头、代码块 &#xff08;形式参数&#xf…...

floodfill算法系列一>太平洋大西洋水流问题

目录 题目方法解析&#xff1a;代码设计&#xff1a;代码呈现&#xff1a; 题目方法解析&#xff1a; 代码设计&#xff1a; 代码呈现&#xff1a; class Solution {int m,n;int[] dx {0,0,-1,1};int[] dy {-1,1,0,0};public List<List<Integer>> pacificAtlant…...

【信息学奥赛一本通 C++题解】1258:【例9.2】数字金字塔

信息学奥赛一本通&#xff08;C版&#xff09;在线评测系统 基础算法 第一节 动态规划的基本模型 1258&#xff1a;【例9.2】数字金字塔 小学生的课堂讲解 一、解题思路 同学们&#xff0c;今天我们要解决的是数字金字塔找最大路径和的问题。想象一下&#xff0c;数字金字塔就…...

初始c语言(指针和结构体)

前言&#xff1a; 内容&#xff1a; 昨天学的指针&#xff0c;今天复习指针&#xff0c;然后学习结构体 复习&#xff1a; 什么是指针&#xff0c;指针就是地址&#xff0c; int* p &a; p就是指针变量&#xff0c;但是口语一般成为指针 int 说明p指向的对象是in…...

C#(Winform)通过添加AForge添加并使用系统摄像机

先展示效果 AForge介绍 AForge是一个专门为开发者和研究者基于C#框架设计的, 也是NET平台下的开源计算机视觉和人工智能库 它提供了许多常用的图像处理和视频处理算法、机器学习和神经网络模型&#xff0c;并且具有高效、易用、稳定等特点。 AForge主要包括: 计算机视觉与人…...

Ubuntu安装geteck/jetlinks实战:源码启动

这个还是很复杂的&#xff0c;建议使用docker即可。 参考 使用源码启动JetLinks | JetLinks 物联网基础平台 安装Ubuntu虚拟机&#xff08;略&#xff09;安装JDK8编译Redis安装mysql ubuntu安装MySqL server-CSDN博客 初次使用&#xff0c;不要安装ElasticSearch下载源码…...

探索ELK 的魅力

在大数据时代&#xff0c;海量日志和数据的收集、存储、处理与可视化分析变得越来越重要。而 ELK 堆栈&#xff0c;由 Elasticsearch、Logstash、Beats 和 Kibana 组成&#xff0c;正是一个强大的开源解决方案&#xff0c;帮助开发者和运维人员高效管理和分析日志数据。本文将详…...

137,【4】 buuctf web [SCTF2019]Flag Shop

进入靶场 都点击看看 发现点击work会增加&#xffe5; 但肯定不能一直点下去 抓包看看 这看起来是一个 JWT&#xff08;JSON Web Token&#xff09;字符串。JWT 通常由三部分组成&#xff0c;通过点&#xff08;.&#xff09;分隔&#xff0c;分别是头部&#xff08;Header&…...

变相提高大模型上下文长度-RAG文档压缩-2.带早停机制的map-refine

我试过用map-refine方法来精炼上下文&#xff0c;由于它是线性的&#xff0c;运行时间随着文档数量线性增长。所以可以考虑通过判断上下文是否可以满足QA来提前结束过程。 import os import json from langchain_core.documents import Documentdata [] file_path ./data/da…...

C++ 虚表(Vtable)和虚基表(Vbtale)与 虚函数 和 虚继承

C的虚表&#xff08;Vtable&#xff09;和虚基表&#xff08;Vbtale&#xff09;是与 虚函数 和 虚继承 密切相关的概念。它们都是用于支持多态&#xff08;特别是动态绑定&#xff09;和虚拟继承的机制&#xff0c;但它们的作用和实现方式有所不同。我们将逐步探讨虚表、虚基表…...

2021年全国研究生数学建模竞赛华为杯E题信号干扰下的超宽带(UWB)精确定位问题求解全过程文档及程序

2021年全国研究生数学建模竞赛华为杯 E题 信号干扰下的超宽带(UWB)精确定位问题 原题再现&#xff1a; 一、背景   UWB&#xff08;Ultra-Wideband&#xff09;技术也被称之为“超宽带”&#xff0c;又称之为脉冲无线电技术。这是一种无需任何载波&#xff0c;通过发送纳秒…...

SpringCould+vue3项目的后台用户管理的CURD【Taurus教育平台】

文章目录 一.SpringCouldvue3项目的后台用户管理的CURD【Taurus教育平台】 1.1 背景 二.用户列表&#xff08;分页查询&#xff09; 2.1 前端Vue3 &#xff08;Vue3-Element-Admin&#xff09;2.2 后端SpringCould 处理 三. 用户信息删除 3.1 前端Vue3 &#xff08;Vue3-Eleme…...

草图绘制技巧

1、点击菜单栏文件–》新建–》左下角高级新手切换–》零件&#xff1b; 2、槽口&#xff1a;直槽口&#xff0c;中心点槽口&#xff0c;三点源槽口&#xff0c;中心点圆弧槽口&#xff1b; 3、草图的约束&#xff1a;需要按住ctrl键&#xff0c;选中两个草图&#xff0c;然后…...

机器学习-1:线性回归

常用的线性回归模型主要有以下这些 简单线性回归多元线性回归多项式回归岭回归套索回归弹性网络回归逐步回归 一.简单的一元线性回归 1.导入必备的库 #导入必备的库 import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.model_selection …...

android 的抓包工具

charles 抓包工具 官网地址 nullCharles Web Debugging Proxy - Official Sitehttps://www.charlesproxy.com/使用手册一定记得看官网 SSL Certificates • Charles Web Debugging Proxy http请求&#xff1a; 1.启动代理&#xff1a; 2.设置设备端口 3.手机连接当前代理 …...

AJAX 与 ASP 的深入探讨

AJAX 与 ASP 的深入探讨 引言 随着互联网技术的飞速发展,Web应用程序的交互性和性能要求越来越高。AJAX(Asynchronous JavaScript and XML)和ASP(Active Server Pages)作为两种重要的Web开发技术,在提高Web应用程序性能和用户体验方面发挥着重要作用。本文将深入探讨AJ…...

Qt开发①Qt的概念+发展+优点+应用+使用

目录 1. Qt的概念和发展 1.1 Qt的概念 1.2 Qt 的发展史&#xff1a; 1.3 Qt 的版本 2. Qt 的优点和应用 2.1 Qt 的优点&#xff1a; 2.2 Qt 的应用场景 2.3 Qt 的应用案例 3. 搭建 Qt 开发环境 3.1 Qt 的开发工具 3.2 Qt SDK 的下载和安装 3.3 Qt 环境变量配置和使…...

函数调用过程的详细解析

目录 一、C语言示例代码 二、汇编代码分步解析&#xff08;x86架构&#xff09; 1. 调用前&#xff1a;参数压栈&#xff08;从右向左&#xff09; 2. 进入被调函数&#xff1a;保存栈帧 3. 执行函数逻辑 4. 恢复栈帧并返回 三、内存布局图示&#xff08;调用过程中栈的变…...

教师管理系统在职校中的应用与优势

随着信息技术的不断发展&#xff0c;教师管理系统在职校中的应用越来越广泛。这一系统通过集成教师信息、教学资源和日程安排等功能&#xff0c;为职校管理带来了诸多便利和优势。 教师管理系统能够显著提高管理效率。传统的人工管理方式往往繁琐且易出错&#xff0c;而教师管理…...

【系统架构设计师】虚拟机体系结构风格

目录 1. 说明2. 解释器体系结构风格3. 规则系统体系结构风格4. 例题4.1 例题1 1. 说明 1.p263。2.虚拟机体系结构风格的基本思想是人为构建一个运行环境&#xff0c;在这个环境之上&#xff0c;可以解析与运行自定义的一些语言&#xff0c;这样来增加架构的灵活性。3.虚拟机体…...

UE C++ UObject 功能的初步总结

一. Uboject的 1.垃圾回收:上篇文章介绍过 2.引用更新 1. 反射:之前的文章描述过的CDO&#xff0c;还有就是C与蓝图相互调用UFUCTION,UPROPERTY 2.序列化&#xff1a;编辑器的资产序列化到磁盘上&#xff0c;变为.uasset等格式的资产文件。所有的东西存在编辑器里&#xff…...

Django 美化使用ModelForm的输入框

在初次使用ModelForm时&#xff0c;我的html文件代码如下&#xff0c;主要内容是显示一个卡片式表单&#xff0c;通过循环遍历 form 对象动态生成表单字段 {% extends layout.html %}{% block content %} <div class"container"><div class"c1"&g…...

SQL在云计算中的新角色:重新定义数据分析

文章目录 1. 云计算与数据分析的融合2. SQL在云计算中的新角色3. 分布式SQL查询引擎4. SQL-on-Hadoop解决方案5. SQL与其他数据分析工具的集成6. 实时数据分析与SQL7. SQL在云数据仓库中的角色8. 安全性与隐私保护9. SQL的未来展望《SQL数据分析实战&#xff08;第2版&#xff…...

使用Redis实现分布式锁,基于原本单体系统进行业务改造

一、单体系统下&#xff0c;使用锁机制实现秒杀功能&#xff0c;并限制一人一单功能 1.流程图&#xff1a; 2.代码实现&#xff1a; Service public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderSe…...

用Python实现线性回归:从数学原理到代码实战

一、前言&#xff1a;为什么线性回归是AI必修课&#xff1f; 作为机器学习领域的"Hello World"&#xff0c;线性回归算法具有三大核心价值&#xff1a; 1️⃣ 理解监督学习的底层逻辑&#xff08;特征工程→模型训练→预测输出&#xff09; 2️⃣ 掌握梯度下降等优化…...

JS 链表

文章目录 链表题的一些总结两种链表定义set存储链表节点&#xff0c;存的是整个空间同时处理长短不一的两个链表处理方法 while(l1 || l2)处理方法 while(l1 & l2) dummyhead的使用 链表题的一些总结 两种链表定义 class class ListNode {val;next null;constructor(va…...

AI时代:架构师的困境与救赎

在GitHub Copilot生成完整函数、ChatGPT编写业务逻辑的今天&#xff0c;编程正经历着前所未有的范式变革。某在线教育平台的技术负责人曾向我展示&#xff1a;团队使用AI工具3个月后&#xff0c;年轻工程师在架构评审会上对Kafka消息队列的消费机制支支吾吾&#xff0c;却在IDE…...

1-10 github注册仓库

如何在github中注册一个仓库&#xff1f; 1.0 注册仓库 1-1 GitHub的账号注册教程_github注册-CSDN博客 2.0 删除仓库 1-2 从github中删除创建的仓库_github删除仓库-CSDN博客 3.0 创建仓库 1-3 【教程】GitHub新建仓库新手教程_github仓库-CSDN博客 4.0 github操作 1-4 1-9 克…...

JavaScript作用域与闭包

一 作用域 在JavaScript中&#xff0c;作用域&#xff08;Scope&#xff09;指的是变量和函数的可访问性范围。在JavaScript中&#xff0c;作用域有全局作用域和局部作用域之分。 全局作用域&#xff08;Global Scope&#xff09;&#xff1a;全局作用域指的是在代码中任何位置…...

docker容器部署jar应用导入文件时候报缺少字体错误解决

如题&#xff0c;在导入文件时候报错如下&#xff1a; Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: Could not initialize class sun.awt.X11FontManager 经查是缺少对应字体&#xff0c;解决办法有两张&#xff1a; 第一种&#xff1a;…...

lean4安装

目录 lean4安装windows 证明等比数列和函数函数 lean4安装windows lean4 windows一键安装(全网最简单的安装流程)_lean4安装-CSDN博客 证明等比数列和函数函数 import Mathlib.Data.Real.Basic -- 导入实数基础库 import Mathlib.Tactic.Simps.Basic -- 导入简化策略 im…...

HTML的入门

一、HTML HTML&#xff08;HyperText Markup Language&#xff0c;超文本标记语言&#xff09;是一种用来告知浏览器如何组织页面的标记语言。 超文本&#xff1a;就是超越了文本&#xff1b;HTML不仅仅可以用来显示文本(字符串、数字之类)&#xff0c;还可以显示视频、音频等…...

Jenkins 部署 之 Mac 一

Jenkins 部署 之 Mac 一 一.Jenkins 部署依赖 JDK 环境 查看 Mac JDK 环境&#xff0c;如果没有安装&#xff0c;先安装 打开终端输入命令:java -version Mac安装配置 JDK 二. 检查 HomeBrew 安装 检查 HomeBrew 是否安装&#xff0c;终端输入命令:brew -v Mac安装HomeB…...

matlab平面波展开法计算的二维声子晶体带隙

平面波展开法计算的二维声子晶体带隙&#xff0c;分别是正方与圆形散射体形成正方格子声子晶体&#xff0c;最后输出了能带图的数据&#xff0c;需要自己用画图软件画出来。 列表 平面波展开法计算二维声子晶体带隙/a2.m , 15823 平面波展开法计算二维声子晶体带隙/a4.m , 942…...

爬虫实战:利用代理ip爬取推特网站数据

引言 亮数据-网络IP代理及全网数据一站式服务商屡获殊荣的代理网络、强大的数据挖掘工具和现成可用的数据集。亮数据&#xff1a;网络数据平台领航者https://www.bright.cn/?promoRESIYEAR50/?utm_sourcebrand&utm_campaignbrnd-mkt_cn_csdn_yingjie202502 在跨境电商、社…...

【kafka系列】生产者

目录 发送流程 1. 流程逻辑分析 阶段一&#xff1a;主线程处理 阶段二&#xff1a;Sender 线程异步发送 核心设计思想 2. 流程 关键点总结 重要参数 一、核心必填参数 二、可靠性相关参数 三、性能优化参数 四、高级配置 五、安全性配置&#xff08;可选&#xff0…...

Kafka日志数据深度解析:从基础查看到高级操作全攻略

#作者&#xff1a;孙德新 文章目录 查看log日志文件(kafka-dump-log.sh)1、查看Log文件基本数据信息2、index文件健康性检查(--index-sanity-check)3、转储文件(--max-message-size)4、偏移量解码(--offsets-decoder)5、日志数据解析(--transaction-log-decoder)6、查询Log文件…...

单例模式、构造函数、左值右值

拷贝构造函数 简单的说就是——用一个对象构造另外一个对象 class Myclass {public:int d0;Myclass(int d_){d d_}; //常用的构造函数Myclass(Myclass c) //拷贝构造函数{d c.d;} }; //对比 class Myclass {public:int d0;Myclass(int d_){d d_}; //常用的构造函数Myclass…...

DeepSeek+即梦 做AI视频

DeepSeek做AI视频 制作流程第一步&#xff1a;DeepSeek 生成视频脚本和分镜 第二步&#xff1a;生成分镜图片绘画提示词第三步&#xff1a;生成分镜图片第四步&#xff1a;使用可灵 AI 工具&#xff0c;将生成的图片转成视频。第五步&#xff1a;剪映成短视频 DeepSeek 真的强&…...

「软件设计模式」建造者模式(Builder)

深入解析建造者模式&#xff1a;用C打造灵活对象构建流水线 引言&#xff1a;当对象构建遇上排列组合 在开发复杂业务系统时&#xff0c;你是否经常面对这样的类&#xff1a;它有20个成员变量&#xff0c;其中5个是必填项&#xff0c;15个是可选项。当用户需要创建豪华套餐A&…...

Android设备 网络安全检测

八、网络与安全机制 6.1 网络框架对比 volley&#xff1a; 功能 基于HttpUrlConnection;封装了UIL图片加载框架&#xff0c;支持图片加载;网络请求的排序、优先级处理缓存;多级别取消请求;Activity和生命周期的联动&#xff08;Activity结束生命周期同时取消所有网络请求 …...