android11.0(R) 谷歌浏览器去除短信分享功能

2023-10-15 03:50

本文主要是介绍android11.0(R) 谷歌浏览器去除短信分享功能,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

客户定制

GMS 版本下,要求去除谷歌浏览器中分享菜单下短信备选项,最终实现效果如下

oluyvR.png

要输入网页后右上角菜单中才会显示分享功能。

解决办法

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

@@ -7413,11 +7417,44 @@ public class PackageManagerService extends IPackageManager.Stubif (sortResult) {Collections.sort(result, RESOLVE_PRIORITY_SORTER);}
+       
+        android.util.Log.i("LogUtils", "step 3");
+        if (result!=null) {
+               android.util.Log.i("LogUtils", "step 4=="+result.size());//cczheng
+        }
+        if ("android.intent.action.SEND".equals(intent.getAction())) {
+               blockGoogleMessagingShare(result);
+        }return applyPostResolutionFilter(result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart,userId, intent);}+    //cczheng add 
+    private void blockGoogleMessagingShare(List<ResolveInfo> result){
+       android.util.Log.d("LogUtils", "into blockGoogleMessagingShare");
+       ActivityManager am = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
+        List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
+        if (!tasks.isEmpty()) {
+            ComponentName topActivity = tasks.get(0).topActivity;
+            String currentPackageName = topActivity.getPackageName();
+            if ("com.android.chrome".equals(currentPackageName)) {
+                for (int i = 0; i < result.size(); i++) {
+                           ResolveInfo resolveInfo = result.get(i);
+                           android.util.Log.i("LogUtils", "resolveInfoCount="+resolveInfo.toString());
+                           if ("com.google.android.apps.messaging".equals(resolveInfo.activityInfo.packageName)){
+                               result.remove(i);
+                           }
+                       }
+                android.util.Log.e("LogUtils", "step 4=="+result.size());
+            }
+        }
+    }//end
+private List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result, Intent intent,String resolvedType, int flags, int userId, boolean resolveForStart,boolean isRequesterInstantApp) {

frameworks/base/core/java/com/android/internal/app/ChooserActivity.java

@@ -588,6 +588,26 @@ public class ChooserActivity extends ResolverActivity implements}};+    //cchzeng add start
+    private String getLastPausedActivity() {
+        String result = "";
+        try {
+            java.lang.Process p = java.lang.Runtime.getRuntime().exec("dumpsys activity activities  grep mLastPausedActivity ");
+            java.io.InputStream input = p.getInputStream();
+            java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(input));
+            StringBuffer stringBuffer = new StringBuffer();
+            String content = "";
+            while ((content = in.readLine()) != null) {
+                stringBuffer.append(content);
+            }
+            result = stringBuffer.toString();
+            int status = p.waitFor();
+        } catch (Exception e) {
+           e.printStackTrace();
+        }
+        return result;
+    }//cczheng add end
+@Overrideprotected void onCreate(Bundle savedInstanceState) {final long intentReceivedTime = System.currentTimeMillis();
@@ -692,7 +712,26 @@ public class ChooserActivity extends ResolverActivity implementspa = intent.getParcelableArrayExtra(Intent.EXTRA_EXCLUDE_COMPONENTS);-
+        //cczheng add start
+        String lastPausedActivityStr = getLastPausedActivity();
+        Log.d(TAG,"ResolverActivity onCreate  "+lastPausedActivityStr);
+        try {
+         if (!TextUtils.isEmpty(lastPausedActivityStr)) {
+            lastPausedActivityStr = lastPausedActivityStr.substring(lastPausedActivityStr.indexOf("mLastPausedActivity"),
+                lastPausedActivityStr.indexOf("* Task"));
+            Log.d(TAG,"lastPausedActivityStr=  "+lastPausedActivityStr);
+            if (lastPausedActivityStr.contains("com.android.chrome")) {
+                android.provider.Settings.System.putInt(getContentResolver(), "tempflag", 1);
+            }else{
+                android.provider.Settings.System.putInt(getContentResolver(), "tempflag", 0);
+            }
+         }else{
+            android.provider.Settings.System.putInt(getContentResolver(), "tempflag", 0);
+         }
+        } catch (Exception e) {
+           e.printStackTrace();
+        } //cczheng add end
+        // Exclude out Nearby from main list if chip is present, to avoid duplicationComponentName nearbySharingComponent = getNearbySharingComponent();boolean hasNearby = nearbySharingComponent != null;

frameworks/base/core/java/com/android/internal/app/ResolverActivity.java

@@ -139,7 +139,7 @@ public class ResolverActivity extends Activity implements+    //cchzeng add start
+     private String getLastPausedActivity() {
+        String result = "";
+        try {
+            java.lang.Process p = java.lang.Runtime.getRuntime().exec("dumpsys activity activities  grep mLastPausedActivity ");
+            java.io.InputStream input = p.getInputStream();
+            java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(input));
+            StringBuffer stringBuffer = new StringBuffer();
+            String content = "";
+            while ((content = in.readLine()) != null) {
+                stringBuffer.append(content);
+            }
+            result = stringBuffer.toString();
+            int status = p.waitFor();
+        } catch (Exception e) {
+           e.printStackTrace();
+        }
+        return result;
+    }//cczheng add end
+@Overrideprotected void onCreate(Bundle savedInstanceState) {// Use a specialized prompt when we're handling the 'Home' app startActivity()
@@ -321,6 +341,26 @@ public class ResolverActivity extends Activity implementsmResolvingHome = true;}+        //cczheng add start
+        String lastPausedActivityStr = getLastPausedActivity();
+        Log.d(TAG,"ResolverActivity onCreate  "+lastPausedActivityStr);
+        try {
+         if (!TextUtils.isEmpty(lastPausedActivityStr)) {
+            lastPausedActivityStr = lastPausedActivityStr.substring(lastPausedActivityStr.indexOf("mLastPausedActivity"),
+                lastPausedActivityStr.indexOf("* Task"));
+            Log.d(TAG,"lastPausedActivityStr=  "+lastPausedActivityStr);
+            if (lastPausedActivityStr.contains("com.android.chrome")) {
+                android.provider.Settings.System.putInt(getContentResolver(), "tempflag", 1);
+            }else{
+                android.provider.Settings.System.putInt(getContentResolver(), "tempflag", 0);
+            }
+          }else{
+            android.provider.Settings.System.putInt(getContentResolver(), "tempflag", 0);
+          }
+        } catch (Exception e) {
+           e.printStackTrace();
+        } //cczheng add end
+setSafeForwardingMode(true);onCreate(savedInstanceState, intent, null, 0, null, null, true);

frameworks/base/core/java/com/android/internal/app/ResolverListAdapter.java

@@ -433,6 +433,16 @@ public class ResolverListAdapter extends BaseAdapter {// Check whether {@code dri} should be added into mDisplayList.protected boolean shouldAddResolveInfo(DisplayResolveInfo dri) {
+        //cczheng add start
+        int flag =android.provider.Settings.System.getInt(mContext.getContentResolver(), "tempflag", 0);
+        if (flag == 1) {
+            String driPackageName = dri.getResolvedComponentName().getPackageName();
+             Log.d(TAG, "shouldAddResolveInfo driPackageName: " + driPackageName);
+            if ("com.google.android.apps.messaging".equals(driPackageName)) {
+               return false;
+            }
+        }
+        //cczheng add end// Checks if this info is already listed in display.for (DisplayResolveInfo existingInfo : mDisplayList) {if (mResolverListCommunicator

分析过程

一开始有点懵,毕竟没有 message app 的源码,一时不知道从哪里下手,要想完成这个需求,毋庸置疑要改 framework

先看了下 message app 的 AndroidManifest.xml,在其中搜到了 send 相关的字样,然后自己新建个 demo,把配置

<intent-filter ><action android:name="android.intent.action.SEND" /><category android:name="android.intent.category.DEFAULT" /><data android:mimeType="text/plain" />
</intent-filter>

copy 了一份安装后发现 demo 也出现在分享列表里了,这么说就是和 android.intent.action.SEND 这个东东相关了。

按照惯例看下这个分享页面到底是系统界面还是 chrome 界面,得到结论

点 share 第一次弹出界面为 chrome 界面,com.android.chrome/com.google.android.apps.chrome.Main

点 more 按钮二次显示界面为系统界面,android/com.android.internal.app.ChooserActivity

这样看来两个地方走的逻辑不太一样,我们先从简单的系统界面入手,直接去找 ChooserActivity 源码

找源码之前我们要学会分析 log, 因为 log 中经常会藏有很多有用信息,我们要过滤的 message 包名

为 com.google.android.apps.messaging,在 log 中我发现了匹配的串

I/ResolverListAdapter: Add DisplayResolveInfo component: ComponentInfo{com.google.android.apps.messaging/com.google.android.apps.messaging.ui.conversationlist.ShareIntentActivity}, intent component: ComponentInfo{com.google.android.apps.messaging/com.google.android.apps.messaging.ui.conversationlist.ShareIntentActivity}
I/ResolverListAdapter: Add DisplayResolveInfo component: ComponentInfo{com.android.chrome/org.chromium.chrome.browser.printing.PrintShareActivity}, intent component: ComponentInfo{com.android.chrome/org.chromium.chrome.browser.printing.PrintShareActivity}
I/ResolverListAdapter: Add DisplayResolveInfo component: ComponentInfo{com.yandex.browser/com.yandex.browser.ShareActivity}, intent component: ComponentInfo{com.yandex.browser/com.yandex.browser.ShareActivity}
I/ResolverListAdapter: Add DisplayResolveInfo component: ComponentInfo{com.hj119.sygjx/com.e4a.runtime.components.impl.android.hjfzdjtb类库.CopyToClipboard}, intent component: ComponentInfo{com.hj119.sygjx/com.e4a.runtime.components.impl.android.hjfzdjtb类库.CopyToClipboard}
I/ResolverListAdapter: Add DisplayResolveInfo component: ComponentInfo{com.android.bluetooth/com.android.bluetooth.opp.BluetoothOppLauncherActivity}, intent component: ComponentInfo{com.android.bluetooth/com.android.bluetooth.opp.BluetoothOppLauncherActivity}
I/ResolverListAdapter: Add DisplayResolveInfo component: ComponentInfo{com.google.android.gm/com.google.android.gm.ComposeActivityGmailExternal}, intent component: ComponentInfo{com.google.android.gm/com.google.android.gm.ComposeActivityGmailExternal}
I/ResolverListAdapter: Add DisplayResolveInfo component: ComponentInfo{com.google.android.apps.docs/com.google.android.apps.docs.common.shareitem.UploadMenuActivity}, intent component: ComponentInfo{com.google.android.apps.docs/com.google.android.apps.docs.common.shareitem.UploadMenuActivity}

仔细一看发现和 ChooserActivity 界面显示 icon 个数正好对上,那岂不是找对地方了。看了源码发现 ChooserActivity 继承 ResolverActivity,

通过 ResolverListAdapter 填充数据,所以我们只需要去 Add DisplayResolveInfo component 打印地方将 message 过滤即可。

但这里有个麻烦点,并不是所有应用调用 ChooserActivity 时都过滤 message ,客户只指定了 chrome,所以我们还需要知道是从哪个 app 中拉起的系统 ChooserActivity

这个一开始在找各种 api 是否能知道是谁拉起的 Activity,后来通过巧妙的方法来达到了要求,秘密就是

adb shell dumpsys activity activities | grep mLastPausedActivity 指令,查询到 ActivityRecord 中上一个 pause 的 Activity 不就是调用者。

mLastPausedActivity: ActivityRecord{ae7cc5b u0 com.android.chrome/com.google.android.apps.chrome.Main t83}

mLastPausedActivity: ActivityRecord{1fcfbc6 u0 com.android.launcher3/com.android.searchlauncher.SearchLauncher t6}

java 代码中执行 dumpsys activity activities | grep mLastPausedActivity 实际并没有过滤到和命令行中执行一样的结果,

而是返回完整串需要 subString 处理一下,通过 Settings.System.putInt 存储 mLastPausedActivity 结果临时值

在 ResolverListAdapter 中取出进行过滤。

通过以下代码可以拉起 ChooserActivity/ResolverActivity

        Intent intentss = new Intent();intentss.setAction(Intent.ACTION_SEND);intentss.putExtra(Intent.EXTRA_TEXT, "文本内容");intentss.setType("text/plain");//startActivity(intentss);//android/com.android.internal.app.ResolverActivitystartActivity(Intent.createChooser(intentss, "share title"));//android/com.android.internal.app.ChooserActivity

简单的搞定了,接下来就是 chrome 里这个界面了,这个界面有点难顶,前后找了几天,最终发现了 ShareActionProvider 这货

使用ShareActionProvider分享数据

同样整了个简单 demo 跑起来发现和 chrome 里显示列表一毛一样,简单代码如下

res/文件夹下新建 menu 文件夹,新增 share.xml 文件

<!--androidX 版本-->
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"><itemandroid:id="@+id/menu_item_share"app:showAsAction="ifRoom"app:actionProviderClass="androidx.appcompat.widget.ShareActionProvider"android:title="Share" />
</menu><!--android低版本-->
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"><itemandroid:id="@+id/menu_item_share"android:showAsAction="ifRoom"android:actionProviderClass="android.widget.ShareActionProvider"android:title="share"/>
</menu>

注意看到一个是 androidx 一个是默认 widget 包里面的 ShareActionProvider,widget 包中的源码是存在 aosp 里的

androidX 中的都是库,很不幸 chrome 就是用的 androidX

随便新建一个 Activity 主题要求带 ActionBar,这样右上角才能添加 menu 菜单

<!--androidX 版本-->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"><!--android低版本-->
android:theme="@android:style/Theme.Holo.Light.DarkActionBar"

在 Activity 中添加 share menu 功能

    @Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.share, menu);MenuItem item = menu.findItem(R.id.menu_item_share);//androidX 版本ShareActionProvider mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(item);//android低版本//ShareActionProvider mShareActionProvider = (ShareActionProvider)item.getActionProvider();Intent shareIntent = new Intent();shareIntent.setAction(Intent.ACTION_SEND);shareIntent.setType("text/plain");shareIntent.putExtra(Intent.EXTRA_TEXT,"aaaaa");mShareActionProvider.setShareIntent(shareIntent);return true;}

这样在 demo 中点击右上角分享图标就能显示一样的分享列表数据

好了主角登场,跟进 ShareActionProvider.java 中

@Overridepublic void onPrepareSubMenu(SubMenu subMenu) {// Clear since the order of items may change.subMenu.clear();ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);PackageManager packageManager = mContext.getPackageManager();final int expandedActivityCount = dataModel.getActivityCount();final int collapsedActivityCount = Math.min(expandedActivityCount, mMaxShownActivityCount);// Populate the sub-menu with a sub set of the activities.for (int i = 0; i < collapsedActivityCount; i++) {ResolveInfo activity = dataModel.getActivity(i);subMenu.add(0, i, i, activity.loadLabel(packageManager)).setIcon(activity.loadIcon(packageManager)).setOnMenuItemClickListener(mOnMenuItemClickListener);}if (collapsedActivityCount < expandedActivityCount) {// Add a sub-menu for showing all activities as a list item.SubMenu expandedSubMenu = subMenu.addSubMenu(Menu.NONE, collapsedActivityCount,collapsedActivityCount,mContext.getString(R.string.abc_activity_chooser_view_see_all));for (int i = 0; i < expandedActivityCount; i++) {ResolveInfo activity = dataModel.getActivity(i);expandedSubMenu.add(0, i, i, activity.loadLabel(packageManager)).setIcon(activity.loadIcon(packageManager)).setOnMenuItemClickListener(mOnMenuItemClickListener);}}}

dataModel.getActivityCount() 关键信息,按字面展开 Activity 个数,下面 for 循环依次添加 icon 项

具体怎么获取 count? 跟进 ActivityChooserModel 中 mActivities

/*** Loads the activities for the current intent if needed which is* if they are not already loaded for the current intent.** @return Whether loading was performed.*/private boolean loadActivitiesIfNeeded() {if (mReloadActivities && mIntent != null) {mReloadActivities = false;mActivities.clear();List<ResolveInfo> resolveInfos = mContext.getPackageManager().queryIntentActivities(mIntent, 0);final int resolveInfoCount = resolveInfos.size();for (int i = 0; i < resolveInfoCount; i++) {ResolveInfo resolveInfo = resolveInfos.get(i);mActivities.add(new ActivityResolveInfo(resolveInfo));}return true;}return false;}

可以看到这个 mActivities.add 要是旧版本的 ShareActionProvider 我们在此处进行过滤应该就能生效

但 chrome 用的 androidX 库,只能从 queryIntentActivities() 入手,通过简单模拟 shareIntent

下面这段代码应该就是查询 share 列表代码

		Intent shareIntent = new Intent();shareIntent.setAction(Intent.ACTION_SEND);shareIntent.setType("text/plain");List<ResolveInfo> resolveInfos = getPackageManager().queryIntentActivities(shareIntent, 0);String action = shareIntent.getAction();final int resolveInfoCount = resolveInfos.size();LogUtils.d("resolveInfoCount="+resolveInfoCount + " action="+action);for (int i = 0; i < resolveInfos.size(); i++) {ResolveInfo resolveInfo = resolveInfos.get(i);LogUtils.d("resolveInfoCount="+resolveInfo.toString());LogUtils.i("resolveInfoCount=ResolveInfo{a8238ee "+resolveInfo.activityInfo.packageName+"/"+resolveInfo.activityInfo.name);}

看到 log 打印正好是分享界面显示的数据,那很明显了需要去 PackageManagerService 中做文章

同样也需要知道是谁调用了 queryIntentActivities 方法,获取当前任务栈中顶部 Activity 包名

ActivityManager.getRunningTasks(1) 如果是 chrome 则过滤 message

queryIntentActivitiesInternal() 有很多地方调用,log 会打印会多,通过多次过滤分析,点击分享瞬间

传递 action 为 android.intent.action.SEND,只有当 action 为 SEND 时才需要过滤,通过上面 demo

调用代码也能发现实际上就是 android.intent.action.SEND 决定的。

     @Overridepublic List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags, int filterCallingUid, int userId) {return PackageManagerService.this.queryIntentActivitiesInternal(intent, resolvedType, flags, 0, filterCallingUid,userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);}private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,String resolvedType, int flags, @PrivateResolveFlags int privateResolveFlags,int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) {if (!mUserManager.exists(userId)) return Collections.emptyList();final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,false /* requireFullPermission */, false /* checkShell */,"query intent activities");final String pkgName = intent.getPackage();//cczhengString action = intent.getAction();android.util.Log.d("LogUtils", "instantAppPkgName="+instantAppPkgName+" resolveInfoCount="+pkgName + " action="+action);ComponentName comp = intent.getComponent();if (comp == null) {if (intent.getSelector() != null) {intent = intent.getSelector();comp = intent.getComponent();}}....android.util.Log.i("LogUtils", "step 3");if (result!=null) {android.util.Log.i("LogUtils", "step 4=="+result.size());//cczheng}if ("android.intent.action.SEND".equals(intent.getAction())) {blockGoogleMessagingShare(result);}return applyPostResolutionFilter(result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart,userId, intent);}

关于 init.rc 启动可以参考下面

Android Q 开机启动流程
Android的init过程(二):初始化语言(init.rc)解析

这篇关于android11.0(R) 谷歌浏览器去除短信分享功能的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/215166

相关文章

SpringBoot UserAgentUtils获取用户浏览器的用法

《SpringBootUserAgentUtils获取用户浏览器的用法》UserAgentUtils是于处理用户代理(User-Agent)字符串的工具类,一般用于解析和处理浏览器、操作系统以及设备... 目录介绍效果图依赖封装客户端工具封装IP工具实体类获取设备信息入库介绍UserAgentUtils

Android使用ImageView.ScaleType实现图片的缩放与裁剪功能

《Android使用ImageView.ScaleType实现图片的缩放与裁剪功能》ImageView是最常用的控件之一,它用于展示各种类型的图片,为了能够根据需求调整图片的显示效果,Android提... 目录什么是 ImageView.ScaleType?FIT_XYFIT_STARTFIT_CENTE

SpringBoot请求参数接收控制指南分享

《SpringBoot请求参数接收控制指南分享》:本文主要介绍SpringBoot请求参数接收控制指南,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Spring Boot 请求参数接收控制指南1. 概述2. 有注解时参数接收方式对比3. 无注解时接收参数默认位置

Python的time模块一些常用功能(各种与时间相关的函数)

《Python的time模块一些常用功能(各种与时间相关的函数)》Python的time模块提供了各种与时间相关的函数,包括获取当前时间、处理时间间隔、执行时间测量等,:本文主要介绍Python的... 目录1. 获取当前时间2. 时间格式化3. 延时执行4. 时间戳运算5. 计算代码执行时间6. 转换为指

SpringBoot基于配置实现短信服务策略的动态切换

《SpringBoot基于配置实现短信服务策略的动态切换》这篇文章主要为大家详细介绍了SpringBoot在接入多个短信服务商(如阿里云、腾讯云、华为云)后,如何根据配置或环境切换使用不同的服务商,需... 目录目标功能示例配置(application.yml)配置类绑定短信发送策略接口示例:阿里云 & 腾

Android实现两台手机屏幕共享和远程控制功能

《Android实现两台手机屏幕共享和远程控制功能》在远程协助、在线教学、技术支持等多种场景下,实时获得另一部移动设备的屏幕画面,并对其进行操作,具有极高的应用价值,本项目旨在实现两台Android手... 目录一、项目概述二、相关知识2.1 MediaProjection API2.2 Socket 网络

Redis消息队列实现异步秒杀功能

《Redis消息队列实现异步秒杀功能》在高并发场景下,为了提高秒杀业务的性能,可将部分工作交给Redis处理,并通过异步方式执行,Redis提供了多种数据结构来实现消息队列,总结三种,本文详细介绍Re... 目录1 Redis消息队列1.1 List 结构1.2 Pub/Sub 模式1.3 Stream 结

MySQL索引的优化之LIKE模糊查询功能实现

《MySQL索引的优化之LIKE模糊查询功能实现》:本文主要介绍MySQL索引的优化之LIKE模糊查询功能实现,本文通过示例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一、前缀匹配优化二、后缀匹配优化三、中间匹配优化四、覆盖索引优化五、减少查询范围六、避免通配符开头七、使用外部搜索引擎八、分

Android实现悬浮按钮功能

《Android实现悬浮按钮功能》在很多场景中,我们希望在应用或系统任意界面上都能看到一个小的“悬浮按钮”(FloatingButton),用来快速启动工具、展示未读信息或快捷操作,所以本文给大家介绍... 目录一、项目概述二、相关技术知识三、实现思路四、整合代码4.1 Java 代码(MainActivi

SpringBoot集成Milvus实现数据增删改查功能

《SpringBoot集成Milvus实现数据增删改查功能》milvus支持的语言比较多,支持python,Java,Go,node等开发语言,本文主要介绍如何使用Java语言,采用springboo... 目录1、Milvus基本概念2、添加maven依赖3、配置yml文件4、创建MilvusClient