Telephony中ITelephony的AIDL调用关系

以Android14.0源码讲解

ITelephony来自framework下的com.android.internal.telephony包下

frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl

这个接口用于与Phone交互的界面,主要由TelephonyManager类使用,一些地方仍在直接使用它,如果可能的话,请清理它们,并使用TelephonyManager。意思就是不让你直接使用ITelephony 接口,要用里面的方法的话就使用TelephonyManager来获取。

 /**
94   * Interface used to interact with the phone.  Mostly this is used by the
95   * TelephonyManager class.  A few places are still using this directly.
96   * Please clean them up if possible and use TelephonyManager instead.
97   *
98   * {@hide}
99   */
100  interface ITelephony {

里面包含了384个方法,我们找一些常见的方法来学习。

1.通话相关

 /**/*拨一个号码。这不会打电话。它显示拨号程序屏幕。*/
108      @UnsupportedAppUsage
109      void dial(String number);
110  
111      /**拨打指定号码。*/
116      @UnsupportedAppUsage
117      void call(String callingPackage, String number);
350  
351      /**返回特定Subscription的Call State。*/
354      int getCallStateForSubscription(int subId, String callingPackage, String featureId);

之前Android9.0还能 endCall(),answerRingingCall(),isRinging(String callingPackage)等可以控制通话的这些方法已经被移除了,只能获取通话的相关状态了。

在 Android 14 中,ITelephony 接口中的一些与呼叫控制相关的方法,如 endCall() 和 answerRingingCall(),被移除的原因主要是为了增强用户隐私和安全性,并遵循更严格的权限管理政策。以下是移除这些方法背后的主要原因:

  1. 用户隐私和安全
    Android 操作系统近年来越来越重视用户隐私和安全性。方法如 endCall() 和 answerRingingCall(),允许应用程序以编程方式挂断电话或接听来电,这涉及对电话呼叫进行直接控制。虽然这些方法过去在某些场景下(例如自动应答、自动挂断电话)很有用,但它们也存在潜在的隐私风险:

滥用风险:应用可以在用户不知情或未授权的情况下控制呼叫,导致隐私问题。例如,某些恶意应用可以滥用这些API来挂断重要的电话或在不需要用户参与的情况下接听电话。
侵犯用户选择:这些方法绕过了用户的决定,让应用能够控制电话呼叫流程,而不经过用户的同意或操作。
为了解决这些问题,Google 在 Android 14 中移除了这些容易被滥用的方法,进一步保护用户的隐私。

  1. 权限管理的演变
    随着 Android 的发展,Google 越来越强调权限的精细化管理,以确保用户对应用行为的更大控制。过去,某些方法可能只需要少量的权限(例如 READ_PHONE_STATE),但实际上,它们赋予应用极高的权力来控制用户的电话。

为了解决这种过度授权的问题,Google 逐步将与电话呼叫相关的操作限制给了系统应用或经过特别授权的应用,而普通第三方应用程序则无法再获得这些权限。

  1. 推动使用更安全的API
    Google 提供了更安全且受限的替代方案来处理呼叫控制功能。例如,通过官方的 TelecomManager API,应用程序可以注册为默认电话应用来控制电话呼叫。要成为默认电话应用,用户必须显式授予权限,这样能够更好地避免未经授权的操作。这种设计确保了只有用户明确选择的应用程序才能执行类似的操作,减少滥用的可能性。

例如:
TelecomManager API:可以用来控制电话呼叫,但仅限于注册为默认电话应用的程序,并且权限控制更严格。

TelecomManager telecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
telecomManager.endCall();  // 需要应用是默认电话应用
CallScreeningService:用于筛选和监控来电,同样受限于特定的系统权限。
  1. 限制低层次的接口调用
    ITelephony 是 Android 系统中的一个底层接口,通常不面向普通应用程序开发者,而是为系统应用或有特殊权限的应用设计的。过去,一些开发者通过反射等手段调用这些内部API,而这些API绕过了系统层级的限制和权限检查。在 Android 14 中,Google 通过移除这些方法,加强了对底层接口的封装,减少了未授权调用的风险。

  2. 提高系统稳定性
    随着 Android 系统的演变,Google 也希望简化并优化底层系统接口,以提高系统的稳定性和一致性。减少对底层 ITelephony API 的依赖,可以避免未来版本中出现的不兼容性问题,并减少系统维护的复杂度。

2.网络模式相关

553      /*返回subId的数据网络类型*/
559      int getDataNetworkTypeForSubscriber(int subId, String callingPackage,String callingFeatureId);561      /** 返回subId的语音网络类型*/
569      int getVoiceNetworkTypeForSubscriber(int subId, String callingPackage,String callingFeatureId);902       /*将网络选择模式设置为自动。*/
905      void setNetworkSelectionModeAutomatic(int subId);//让radio连接到输入网络,并将选择模式更改为手动。boolean setNetworkSelectionModeManual(int subId, in OperatorInfo operatorInfo, boolean persisSelection);//执行radio扫描并返回可用网络列表。也就是 获取搜网后的结果CellNetworkScanResult getCellNetworkScanResults(int subId, String callingPackage,String callingFeatureId);//发起搜网请求
931      int requestNetworkScan(int subId, in boolean renounceFineLocationAccess,
932              in NetworkScanRequest request, in Messenger messenger, in IBinder binder,
933  	    in String callingPackage, String callingFeatureId);//停止搜网
941      void stopNetworkScan(int subId, int scanId);

设置网络模式(即,4G/3G/2G, 3G/2G, 2G only等) setPreferredNetworkType(int subId, int networkType);方法也移除了,普通应用不再能够直接更改这些设置。目前,只有具有 系统级权限 或 运营商特权 的应用程序才可能具有修改网络模式的权限。

3.手机相关

/*getDeviceId(String callingPackage);方法已经废弃使用getDeviceIdWithFeature代替,
返回Phone的唯一设备ID,例如IMEI GSM 和 CDMA phone的MEID。如果设备ID不可用,则返回null。*/
1317      String getDeviceIdWithFeature(String callingPackage, String callingFeatureId);//通过slot 获取IMEI
1328      String getImeiForSlot(int slotIndex, String callingPackage, String callingFeatureId);//返回给定插槽的MEID。
1356      String getMeidForSlot(int slotIndex, String callingPackage, String callingFeatureId);// 获取设备软件版本号
1374      String getDeviceSoftwareVersionForSlot(int slotIndex, String callingPackage,
1375              String callingFeatureId);

IMEI:用于 GSM 和 LTE 网络的 15 位数字唯一标识符,广泛应用于全球。
MEID:主要用于 CDMA 网络的 14 位十六进制标识符。常见于美国和一些其他地区的移动通信网络。CDMA 网络由运营商如 Verizon(美国)或 KDDI(日本)使用。

在现代智能手机上,特别是双模设备(支持 GSM 和 CDMA 网络)的手机,可能同时有 IMEI 和 MEID 号码,分别用于不同的网络协议。随着 LTE 网络的发展,CDMA 技术的使用逐渐减少,因此 IMEI 逐渐成为全球统一的设备标识方法。

4.SIM卡相关

//提供一个pin来解锁特定subId的SIM卡。
163      boolean supplyPinForSubscriber(int subId, String pin);//提供puk以解锁SIM卡,并将SIM卡pin设置为新pin。
173      boolean supplyPukForSubscriber(int subId, String puk, String pin);// 判断是否插入了Sim卡
575      @UnsupportedAppUsage
576      boolean hasIccCard();//获取默认eUICC卡的卡ID。如果没有eUICC,则返回{@link#INVALID_CARD_ID}。
1732      int getCardIdForDefaultEuicc(int subId, String callingPackage);

接下来看一下这些方法是如何实现的?

查看源码,PhoneInterfaceManager类继承了接口ITelephony.Stub ,相当于实现了ITelephony的所有方法。
在这里插入图片描述
先了解一下AIDL机制:

那为什么源码中PhoneInterfaceManager 可以 extends ITelephony.Stub 接口?在 Java 和 Android 中,PhoneInterfaceManager 类通过 extends 继承 ITelephony.Stub 的情况,其实并不是简单的继承接口。这里涉及到了AIDL(Android Interface Definition Language)的特殊机制。1. AIDL(Android Interface Definition Language)
在 Android 中,AIDL 是一种允许不同进程之间通信的机制,常用于实现跨进程调用(IPC)。当您定义一个 AIDL 接口时,系统会自动为您生成一个 Stub 类。这些生成的代码通常会继承 android.os.Binder 类,并实现您定义的接口方法。例如,假设 ITelephony.aidl 是一个定义了远程接口的 AIDL 文件:interface ITelephony {void setPreferredNetworkType(int type);
}
在编译时,Android SDK 会自动生成一个 ITelephony.Stub 类,这个类是 Binder 的子类,并且它实现了 ITelephony 接口。Stub 类有助于在服务器端处理来自客户端的远程方法调用。2. ITelephony.Stub 是什么?
ITelephony.Stub 是 Android 自动生成的一个 抽象类,它既实现了 ITelephony 接口的所有抽象方法,又继承了 Binder 类。ITelephony 接口:定义了远程接口的行为(如 setPreferredNetworkType())。
ITelephony.Stub 类:这个抽象类实现了 IBinder 接口,并负责处理客户端的远程调用请求。它还提供了方法用于将远程请求分发给实际的 PhoneInterfaceManager 实现。
简而言之,Stub 类是 Binder 的子类,封装了 AIDL 中的绑定逻辑,用来处理跨进程调用。3. 为什么 PhoneInterfaceManager 可以 extends ITelephony.Stub?
由于 ITelephony.Stub 是一个抽象类,而不是一个接口,Java 中允许类继承另一个类。因此,PhoneInterfaceManager 继承 ITelephony.Stub 是合理的:java
public class PhoneInterfaceManager extends ITelephony.Stub {@Overridepublic void setPreferredNetworkType(int type) {// 具体的实现逻辑}
}
4. ITelephony.Stub 和 IPC 通信
PhoneInterfaceManager 继承 ITelephony.Stub 是为了处理来自其他进程的远程调用。由于 Android 系统中的很多服务(如电话、消息等)运行在单独的进程中,它们需要通过 AIDL 机制提供远程调用接口给其他进程使用。通过继承 ITelephony.Stub,PhoneInterfaceManager 类能够作为服务端,处理来自客户端(如应用层)发起的远程调用请求。5. 总结
PhoneInterfaceManager 继承 ITelephony.Stub,是因为 Stub 实际上是一个 AIDL 自动生成的 抽象类,它既实现了接口,又继承了 Binder 类,负责管理远程调用。
AIDL 机制允许跨进程调用,Stub 类帮助处理这些调用,因此 PhoneInterfaceManager 继承了 Stub,从而可以提供远程方法调用的服务。
这不是普通的类继承接口的情况,而是 AIDL 机制下的 自动生成类的继承。

看完小插曲,回到PhoneInterfaceManager类的学习,通过查看这个所在路径packages/services/Telephony/src/com/android/phone/PhoneInterfaceManager.java,发现它是运行在phone进程下的,它是PhoneGlobals在onCreate方法中初始化的 phoneMgr = PhoneInterfaceManager.init(this),并且这个只能初始化一次。

2439      /**
2440       * Initialize the singleton PhoneInterfaceManager instance.
2441       * This is only done once, at startup, from PhoneApp.onCreate().
2442       */
2443      /* package */ static PhoneInterfaceManager init(PhoneGlobals app) {
2444          synchronized (PhoneInterfaceManager.class) {
2445              if (sInstance == null) {
2446                  sInstance = new PhoneInterfaceManager(app);
2447              } else {
2448                  Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
2449              }
2450              return sInstance;
2451          }
2452      }
2453  
2454      /** Private constructor; @see init() */
2455      private PhoneInterfaceManager(PhoneGlobals app) {
2456          mApp = app; //得到PhoneGlobals的引用
2457          mCM = PhoneGlobals.getInstance().mCM; //CallManager引用
2458          mImsResolver = ImsResolver.getInstance(); //获取ImsResolver实例对象
2459          mSatelliteController = SatelliteController.getInstance();//获取SatelliteController实例对象
2460          mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
2461          mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
2462          mMainThreadHandler = new MainThreadHandler(); //获取MainThreadHandler对象
2463          mTelephonySharedPreferences = PreferenceManager.getDefaultSharedPreferences(mApp);
2464          mNetworkScanRequestTracker = new NetworkScanRequestTracker();
2465          mPhoneConfigurationManager = PhoneConfigurationManager.getInstance();
2466          mRadioInterfaceCapabilities = RadioInterfaceCapabilityController.getInstance();
2467          mNotifyUserActivity = new AtomicBoolean(false);
2468          PropertyInvalidatedCache.invalidateCache(TelephonyManager.CACHE_KEY_PHONE_ACCOUNT_TO_SUBID);
2469          publish();
2470          CarrierAllowListInfo.loadInstance(mApp);
2471      }7      private void publish() {
2488          if (DBG) log("publish: " + this);
2489  //将当前对象注册为 Telephony 服务,使其可以被系统访问和使用。ServiceManager.addService("phone", this);
2490          TelephonyFrameworkInitializer
2491                  .getTelephonyServiceManager()
2492                  .getTelephonyServiceRegisterer()
2493                  .register(this);
2494      }

先看getCallStateForSubscription方法是如何实现的?

3219      @Override
3220      public int getCallStateForSubscription(int subId, String callingPackage, String featureId) {//判断权限
3221          if (CompatChanges.isChangeEnabled(
3222                  TelecomManager.ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION,
3223                  Binder.getCallingUid())) {
3224              // Check READ_PHONE_STATE for API version 31+
3225              if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage,
3226                      featureId, "getCallStateForSubscription")) {
3227                  throw new SecurityException("getCallState requires READ_PHONE_STATE for apps "
3228                          + "targeting API level 31+.");
3229              }
3230          }
3231          final long identity = Binder.clearCallingIdentity();
3232          try {
3233              Phone phone = getPhone(subId);	//返回CALL_STATE状态
3234              return phone == null ? TelephonyManager.CALL_STATE_IDLE :
3235                      PhoneConstantConversions.convertCallState(phone.getState());
3236          } finally {
3237              Binder.restoreCallingIdentity(identity);
3238          }
3239      }

把TelephonyManager.CALL_STATE_*转成PhoneConstants.State

frameworks/base/telephony/java/com/android/internal/telephony/PhoneConstantConversions.java39      /**
40       * Convert the TelephonyManager.CALL_STATE_* constants into the
41       * {@link PhoneConstants.State} enum for the public API.
42       */
43      public static PhoneConstants.State convertCallState(int state) {
44          switch (state) {
45              case TelephonyManager.CALL_STATE_RINGING:
46                  return PhoneConstants.State.RINGING;
47              case TelephonyManager.CALL_STATE_OFFHOOK:
48                  return PhoneConstants.State.OFFHOOK;
49              default:
50                  return PhoneConstants.State.IDLE;
51          }
52      }
53  

再来看一个getDataNetworkTypeForSubscriber方法

5361      @Override
5362      public int getDataNetworkTypeForSubscriber(int subId, String callingPackage,
5363              String callingFeatureId) {
5364          String functionName = "getDataNetworkTypeForSubscriber";
5365          if (!TelephonyPermissions.checkCallingOrSelfReadNonDangerousPhoneStateNoThrow(
5366                  mApp, functionName)) {
5367              if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
5368                      mApp, subId, callingPackage, callingFeatureId, functionName)) {
5369                  return TelephonyManager.NETWORK_TYPE_UNKNOWN;
5370              }
5371          }
5372  
5373          final long identity = Binder.clearCallingIdentity();
5374          try {
5375              final Phone phone = getPhone(subId);//也是通过phone对象来获取DataNetworkType
5376              if (phone != null) {
5377                  return phone.getServiceState().getDataNetworkType();
5378              } else {
5379                  return TelephonyManager.NETWORK_TYPE_UNKNOWN;
5380              }
5381          } finally {
5382              Binder.restoreCallingIdentity(identity);
5383          }
5384      }

看了两个方法,可以发现在PhoneInterfaceManager类中,就是通过获取phone对象并调用其方法来实现具体功能的。

既然PhoneInterfaceManager类是作为服务端的,那么客户端是怎么来获取这里面的方法呢,App的开发过程中是没办法获取到ITelephony接口的。可以通过 frameworks/base/telephony/java/android/telephony/TelephonyManager.java类来获取响应的方法,在TelephonyManager类中,ITelephony通过ITelephony.Stub.asInterface获取ITelephony类。
在这里插入图片描述

然后在telephony对象中获取getDataNetworkTypeForSubscriber方法,相当于远程调用了PhoneInterfaceManager中的getDataNetworkTypeForSubscriber()方法

6      public int getDataNetworkType(int subId) {
3147          try{//调用getITelephony()方法得到ITelephony的实例对象
3148              ITelephony telephony = getITelephony();
3149              if (telephony != null) {// 调用ITelephony实例对象的getDataNetworkTypeForSubscriber()方法// 实际上是远程调用了PhoneInterfaceManager中的getDataNetworkTypeForSubscriber()方法
3150                  return telephony.getDataNetworkTypeForSubscriber(subId, getOpPackageName(),
3151                          getAttributionTag());
3152              } else {
3153                  // This can happen when the ITelephony interface is not up yet.
3154                  return NETWORK_TYPE_UNKNOWN;
3155              }
3156          } catch(RemoteException ex) {
3157              // This shouldn't happen in the normal case
3158              return NETWORK_TYPE_UNKNOWN;
3159          } catch (NullPointerException ex) {
3160              // This could happen before phone restarts due to crashing
3161              return NETWORK_TYPE_UNKNOWN;
3162          }
3163      }

至此就完成了,但是getDataNetworkTypeForSubscriber方法中返回的这个数据phone.getServiceState().getDataNetworkType(),其实要深究的话,就要到ServiceState类了

/frameworks/base/telephony/java/android/telephony/ServiceState.java1770      public @NetworkType int getDataNetworkType() {
1771          final NetworkRegistrationInfo iwlanRegInfo = getNetworkRegistrationInfo(
1772                  NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
1773          final NetworkRegistrationInfo wwanRegInfo = getNetworkRegistrationInfo(
1774                  NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
1775  
1776          // For legacy mode device, or AP-assisted mode device but IWLAN is out of service, use
1777          // the RAT from cellular.
1778          if (iwlanRegInfo == null || !iwlanRegInfo.isInService()) {
1779              return (wwanRegInfo != null) ? wwanRegInfo.getAccessNetworkTechnology()
1780                      : TelephonyManager.NETWORK_TYPE_UNKNOWN;
1781          }
1782  
1783          // At this point, it must be an AP-assisted mode device and IWLAN is in service. We should
1784          // use the RAT from IWLAN service is cellular is out of service, or when both are in service
1785          // and any APN type of data is preferred on IWLAN.
1786          if (!wwanRegInfo.isInService() || mIsIwlanPreferred) {
1787              return iwlanRegInfo.getAccessNetworkTechnology();
1788          }
1789  
1790          // If both cellular and IWLAN are in service, but no APN is preferred on IWLAN, still use
1791          // the RAT from cellular.
1792          return wwanRegInfo.getAccessNetworkTechnology();
1793      }

它们的调用关系:
1> App获取TelephonyManger的实例对象
2>然后调用TelephonyManger的getDataNetworkType()
3>在方法中通过getITelephony()获取ITelephony 实例
4>ITelephony 跨进程 调用PhoneInterfaceManager类中的getDataNetworkTypeForSubscriber
5>而getDataNetworkTypeForSubscriber方法中又通过phone对象调用ServiceState返回DataNetworkType

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/457789.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

多元线性回归【正规方程/sklearn】

多元线性回归【正规方程/sklearn】 1. 基本概念1.1 线性回归1.2 一元简单线性回归1.3 最优解1.4 多元线性回归 2. 正规方程求最优解2.1 线性回归的损失函数(最小二乘法)2.2 推导正规方程2.3 正规方程练习2.4 使用sklearn计算多元线性方程2.5 凸函数 3. 线…

InternVL-1.1: Enhance Chinese and OCR Capabilities

Blog:https://internvl.github.io/blog/2024-01-24-InternVL-1.1/ 指南:https://internvl.readthedocs.io/en/latest/internvl1.1/introduction.html InternVL-Chat-V1-1 结构类似于 LLaVA,包括一个 ViT、一个 MLP 投影器和一个 LLM。如上图所示,我们通过一个简单的 MLP …

JAVA篇之类和对象

目录 一. 面向对象 1.1 面向对象和面向过程 二. 类的定义和使用 2.1 什么是类 2.2 类的定义格式 三. 类的实例化 四. this引用 4.1 this引用的作用 五. 构造方法 5.1 构造方法重载 5.2 通过this调用其他构造方法 5.3 默认初始化 结语 一. 面向对象 Java 是一门面向对…

面向对象与设计模式第二节:设计模式实战

第三章:面向对象与设计模式 第二节:设计模式实战 设计模式是软件工程中的一项重要实践,它为解决常见的设计问题提供了经过验证的解决方案。本课将深入探讨几种常见的设计模式,并通过实际案例分析其在项目中的应用。 1. 每种设计…

JavaEE初阶---文件IO总结

文章目录 1.文件初识2.java针对于文件的操作2.1文件系统的操作---file类2.2文件内容的操作---流对象的分类2.4字符流的操作》文本文件2.4.1异常的说明2.4.2第一种文件内容的读取方式2.4.3第二种读取方式2.4.4close的方法的介绍2.4.5close的使用优化操作2.4.6内容的写入 2.3字节…

无需依赖闭源模型!司南CompassJudger为AI评测带来新选择

前沿科技速递🚀 近期,司南OpenCompass团队发布了一款开源的全能评价模型——CompassJudger。这是全球首个全能开源的 All-in-one Judge Model,不仅支持主流的双向对比(pair-wise)和单向评分(point-wise&…

软件工程--需求分析与用例模型

面向对象分析(ObjectOrientedAnalysis,简称OOA) 分析和理解问题域,找出描述问题域所需的类和对象,分析它们的内部构成和外部关系,建立独立于实现的OOA模型,暂时忽略与系统实现有关的问题。 主要使用UML中的以下几种图…

全球知名度最高的华人起名大师颜廷利:世界顶级思想哲学教育家

全国给孩子起名最好的大师颜廷利教授在其最新的哲学探索中,提出了《升命学说》这一前沿理论观点,该理论不仅深刻地回应了古今中外众多哲学流派和思想体系的精髓,还巧妙地融合了实用主义、理想主义以及经验主义的核心理念。通过这一独特的视角…

我准备写一份Stable Diffusion入门指南-part1

我准备写个SD自学指南,当然也是第一次写,可能有点凌乱,后续我会持续更新不断优化,我是生产队的驴,欢迎监督。 Stable Diffusion WebUI 入门指南 Stable Diffusion WebUI 是一款基于 Stable Diffusion 模型的用户界面…

力扣 中等 740.删除并获得点数

文章目录 题目介绍题解 题目介绍 题解 由题意可知,在选择了数组中元素 a 后,该元素以及所有等于 a−1 和 a1 的元素都会从数组中删去,并获得 a 的点数。若还有多个值为 a的元素,由于所有等于 a−1 或 a1 的元素已经被删除&#x…

三种材料的金相图及金相图解析材料

3. 二.不同温度下三种材料(铸铁,铝,低碳钢)的低温脆性,相关材料,文献引用 三.三种材料在汽车制造中可能的应用 (如捷豹用铝合金降低车身重量).三种材料哪个材…

Linux: Shell编程入门

Shell 编程入门 1 ) Shell 概念 shell 是 在英语中 壳, 外壳的意思可以把它想象成嵌入在linux这样的操作系统里面的一个微型的编程语言不像C语言, C 或 Java 等编程语言那么完整,它可以帮我们完成很多自动化任务例如保存数据监测系统的负载等等,我们同样…

AI博士人手10篇顶会,遭质疑。。。

B站:啥都会一点的研究生公众号:啥都会一点的研究生 AI科技圈又发生了啥新鲜事? “稚晖君”灵犀X1全球开源,推动人形机器人技术共享 智元机器人宣布其人形机器人灵犀X1正式面向全球开源,提供了超过1.2GB的软硬件全套…

【LeetCode】11.盛最多水的容器

思路: 利用双指针法进行移动,一个在头一个在尾,此时宽度最宽,当宽度缩小时,高度发生变化,从而可以找到最大值。 代码: int maxArea(int* height, int heightSize) {int* left height;int* …

android——渐变色

1、xml的方式实现渐变色 效果图&#xff1a; xml的代码&#xff1a; <?xml version"1.0" encoding"utf-8"?> <shape xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools…

Java常见数据结构

数组 数组的特性存储空间是连续的长度是不可变的只能存储 相同的类型(不严谨)可以通过下标访问数组的内容 a[10] 复杂度是O1每个元素的默认是为零值 0 null false -> 一个对象的基本的数据域的初始化也是这样的 Student 类中的username属性 默认值 链表 查找麻烦 插入和删…

logback日志导入使用

1导入配置 <!-- 日志 &#xff0c; 会自动传递slf4j门面--> <dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version> </dependency>2 引入配置 Logback要求…

开源实时数仓的构建

设计计思路 基本思路 开源数据平台的设计思路是通过 Flink SQL Batch、StartRocks SQL 、StartRocks物化视图 的能力实现一个离线任务的开发&#xff1b;使用 DolphinScheduler 进行离线工作流编排和调度&#xff1b;通过 Flink CDC 和 Flink SQL 实现流处理能力&#xff0c;进…

HarmonyOS 相对布局(RelativeContainer)

1. HarmonyOS 相对布局&#xff08;RelativeContainer&#xff09; 文档中心:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-layout-development-relative-layout-V5   RelativeContainer为采用相对布局的容器&#xff0c;支持容器内部的子元素设…

【C++】—— 模板进阶

【C】—— 模板进阶 1 非类型模板参数1.1 什么是非类型模板参数1.2 非类型模板参数对比宏的优势1.3 array 简单了解 2 模板的特化2.1 引子2.2 函数模板特化2.3 函数模板特化的坑2.4 类模板的特化2.4.1 全特化2.4.2 偏特化&#xff08;半特化&#xff09;2.4.3 选择2.4.4 偏特化…