SettingsIntelligence
Android Settings 系列文章:
- Android Settings解析
- SettingsIntelligence
- SettingsProvider
首语
Android Settings中搜索功能帮助我们可以快速访问设置项,进行自定义设置,以得到更佳的使用体验。Android Settings搜索的实现实际不在Settings模块里,而是存在一个单独的模块—SettingsIntelligence,它里面实现了Settings的核心搜索功能,因此,学习SettingsIntelligence搜索实现可以让我们更多了解Settings模块。
搜索实现流程
本文以Android 13 SettingsIntelligence模块源码进行分析。
首先搜索栏的跳转实现在SearchFeatureProvider的initSearchToolbar中,initSearchToolbar在Android Settings解析文章分析过,在SettingsHomepageActivity的initSearchBarView方法中调用。最终跳转到包名为com.android.settings.intelligence,action为android.settings.APP_SEARCH_SETTINGS的页面中。
public interface SearchFeatureProvider {default void initSearchToolbar(FragmentActivity activity, Toolbar toolbar, int pageId) {...final Intent intent = buildSearchIntent(context, pageId).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);...toolbar.setOnClickListener(tb -> {FeatureFactory.getFactory(context).getSlicesFeatureProvider().indexSliceDataAsync(context);FeatureFactory.getFactory(context).getMetricsFeatureProvider().logSettingsTileClick(KEY_HOMEPAGE_SEARCH_BAR, pageId);final Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(activity).toBundle();activity.startActivity(intent, bundle);});}
}
它对应的模块为SettingsIntelligence,模块路径:packages/apps/SettingsIntelligence。从AndroidManifest.xml可以看到,Settings跳转搜索的页面为SearchActivity,SearchActivity添加SearchFragment,在SearchFragment中实现了搜索的核心逻辑。
查看onCreate方法,进行了一些变量的初始化,onCreateView方法中进行view初始化,设置布局为search_panel,我们只需要关注搜索框控件SearchView,设置查询字符串为mQuery,即输入搜索的内容。
设置查询监听,重写onQueryTextSubmit和onQueryTextChange方法。当搜索框文本改变时,通过restartLoaders方法调用LoadManager开启加载数据流程。当Loader创建成功时,回调onCreateLoader方法,调用getSearchResultLoader方法来SearchResultLoader实例。
public class SearchFragment extends Fragment implements SearchView.OnQueryTextListener,LoaderManager.LoaderCallbacks<List<? extends SearchResult>>, IndexingCallback {@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {... mSearchView = toolbar.findViewById(R.id.search_view);mSearchView.setQuery(mQuery, false /* submitQuery */);mSearchView.setOnQueryTextListener(this);mSearchView.requestFocus();return view;} @Overridepublic boolean onQueryTextChange(String query) {...if (isEmptyQuery) {final LoaderManager loaderManager = getLoaderManager();loaderManager.destroyLoader(SearchCommon.SearchLoaderId.SEARCH_RESULT);mShowingSavedQuery = true;mSavedQueryController.loadSavedQueries();mSearchFeatureProvider.hideFeedbackButton(getView());} else {mMetricsFeatureProvider.logEvent(SettingsIntelligenceEvent.PERFORM_SEARCH);restartLoaders();}return true;}@Overridepublic boolean onQueryTextSubmit(String query) {// Save submitted query.mSavedQueryController.saveQuery(mQuery);hideKeyboard();return true;}private void restartLoaders() {mShowingSavedQuery = false;final LoaderManager loaderManager = getLoaderManager();loaderManager.restartLoader(SearchCommon.SearchLoaderId.SEARCH_RESULT,null /* args */, this /* callback */);}@Overridepublic Loader<List<? extends SearchResult>> onCreateLoader(int id, Bundle args) {final Activity activity = getActivity();switch (id) {case SearchCommon.SearchLoaderId.SEARCH_RESULT:return mSearchFeatureProvider.getSearchResultLoader(activity, mQuery);default:return null;}}
}
在SearchFeatureProvider实现类SearchFeatureProviderImpl中创建了SearchResultLoader实例,SearchResultLoader在子线程进行数据查找。
public class SearchResultLoader extends AsyncLoader<List<? extends SearchResult>> {private final String mQuery;public SearchResultLoader(Context context, String query) {super(context);mQuery = query;}@Overridepublic List<? extends SearchResult> loadInBackground() {SearchResultAggregator aggregator = SearchResultAggregator.getInstance();return aggregator.fetchResults(getContext(), mQuery);}
}
fetchResults方法进行数据查找,并创建了一个tasks集合,然后变量tasks,保存到taskResults中。
public class SearchResultAggregator {@NonNullpublic synchronized List<? extends SearchResult> fetchResults(Context context, String query) {final SearchFeatureProvider mFeatureProvider = FeatureFactory.get(context).searchFeatureProvider();final ExecutorService executorService = mFeatureProvider.getExecutorService();final List<SearchQueryTask> tasks =mFeatureProvider.getSearchQueryTasks(context, query);// Start tasksfor (SearchQueryTask task : tasks) {executorService.execute(task);}// Collect resultsfinal Map<Integer, List<? extends SearchResult>> taskResults = new ArrayMap<>();final long allTasksStart = System.currentTimeMillis();for (SearchQueryTask task : tasks) {final int taskId = task.getTaskId();try {taskResults.put(taskId,task.get(SHORT_CHECK_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS));} catch (TimeoutException | InterruptedException | ExecutionException e) {Log.d(TAG, "Could not retrieve result in time: " + taskId, e);taskResults.put(taskId, Collections.EMPTY_LIST);}}// Merge resultsfinal List<? extends SearchResult> mergedResults = mergeSearchResults(taskResults);return mergedResults;}
}
getSearchQueryTasks中构建了各种类型的task,如DatabaseResultTask/InstalledAppResultTask等等。这些task都继承于SearchQueryTask.QueryWorker。
public class SearchFeatureProviderImpl implements SearchFeatureProvider {@Overridepublic List<SearchQueryTask> getSearchQueryTasks(Context context, String query) {final List<SearchQueryTask> tasks = new ArrayList<>();final String cleanQuery = cleanQuery(query);tasks.add(DatabaseResultTask.newTask(context, getSiteMapManager(), cleanQuery));tasks.add(InstalledAppResultTask.newTask(context, getSiteMapManager(), cleanQuery));tasks.add(AccessibilityServiceResultTask.newTask(context, getSiteMapManager(), cleanQuery));tasks.add(InputDeviceResultTask.newTask(context, getSiteMapManager(), cleanQuery));return tasks;}
}
而SearchQueryTask又继承于FutureTask,call方法去处理任务,完成后返回结果。
public class SearchQueryTask extends FutureTask<List<? extends SearchResult>> {public static abstract class QueryWorker implements Callable<List<? extends SearchResult>> {@Overridepublic List<? extends SearchResult> call() throws Exception {final long startTime = System.currentTimeMillis();try {return query();} finally {final long endTime = System.currentTimeMillis();FeatureFactory.get(mContext).metricsFeatureProvider(mContext).logEvent(getQueryWorkerId(), endTime - startTime);}}}
}
我们以DatabaseResultTask为例,查看它实现的query方法。query方法通过一系列的查询方法将数据添加到resultSet中,可以看到query方法中获取SQLite数据库实例,IndexDatabaseHelper中初始化数据库,可以看到数据库名为search_index.db,表名和表字段。最后通过query方法查询数据。
public class DatabaseResultTask extends SearchQueryTask.QueryWorker {public static SearchQueryTask newTask(Context context, SiteMapManager siteMapManager,String query) {return new SearchQueryTask(new DatabaseResultTask(context, siteMapManager, query));}@Overrideprotected List<? extends SearchResult> query() {if (mQuery == null || mQuery.isEmpty()) {return new ArrayList<>();}// Start a Future to get search result scores.FutureTask<List<Pair<String, Float>>> rankerTask = mFeatureProvider.getRankerTask(mContext, mQuery);if (rankerTask != null) {ExecutorService executorService = mFeatureProvider.getExecutorService();executorService.execute(rankerTask);}final Set<SearchResult> resultSet = new HashSet<>();resultSet.addAll(firstWordQuery(MATCH_COLUMNS_PRIMARY, BASE_RANKS[0]));resultSet.addAll(secondaryWordQuery(MATCH_COLUMNS_PRIMARY, BASE_RANKS[1]));resultSet.addAll(anyWordQuery(MATCH_COLUMNS_SECONDARY, BASE_RANKS[2]));resultSet.addAll(anyWordQuery(MATCH_COLUMNS_TERTIARY, BASE_RANKS[3]));// Try to retrieve the scores in time. Otherwise use static ranking.if (rankerTask != null) {try {final long timeoutMs = mFeatureProvider.smartSearchRankingTimeoutMs(mContext);List<Pair<String, Float>> searchRankScores = rankerTask.get(timeoutMs,TimeUnit.MILLISECONDS);return getDynamicRankedResults(resultSet, searchRankScores);} catch (TimeoutException | InterruptedException | ExecutionException e) {Log.d(TAG, "Error waiting for result scores: " + e);}}List<SearchResult> resultList = new ArrayList<>(resultSet);Collections.sort(resultList);return resultList;}private Set<SearchResult> firstWordQuery(String[] matchColumns, int baseRank) {final String whereClause = buildSingleWordWhereClause(matchColumns);final String query = mQuery + "%";final String[] selection = buildSingleWordSelection(query, matchColumns.length);return query(whereClause, selection, baseRank);}private Set<SearchResult> query(String whereClause, String[] selection, int baseRank) {final SQLiteDatabase database =IndexDatabaseHelper.getInstance(mContext).getReadableDatabase();//查询搜索数据try (Cursor resultCursor = database.query(TABLE_PREFS_INDEX, SELECT_COLUMNS,whereClause,selection, null, null, null)) {return mConverter.convertCursor(resultCursor, baseRank, mSiteMapManager);}}
}
那么问题来了,Settings搜索数据存储在SQLite数据库中,我们分析了它的查询流程,那么它是如何存储的呢?
其实在SearchFragment的onCreate就有实现,通过updateIndexAsync刷新数据。
public class SearchFragment extends Fragment implements SearchView.OnQueryTextListener,LoaderManager.LoaderCallbacks<List<? extends SearchResult>>, IndexingCallback {...mSearchFeatureProvider.updateIndexAsync(getContext(), this /* indexingCallback */);
}
通过indexDatabase方法更新数据。
public class SearchFeatureProviderImpl implements SearchFeatureProvider {@Overridepublic void updateIndexAsync(Context context, IndexingCallback callback) {if (DEBUG) {Log.d(TAG, "updating index async");}getIndexingManager(context).indexDatabase(callback);}
}
IndexingTask继承于AsyncTask。异步执行performIndexing方法,通过queryIntentContentProviders方法获取ContentProvider,然后根据provider查找数据,更新到数据库中。看下intent指定的action PROVIDER_INTERFACE为"android.content.action.SEARCH_INDEXABLES_PROVIDER",在Settings查找是否有定义此action的ContentProvider。
public class DatabaseIndexingManager {public void indexDatabase(IndexingCallback callback) {IndexingTask task = new IndexingTask(callback);task.execute();}public class IndexingTask extends AsyncTask<Void, Void, Void> {@VisibleForTestingIndexingCallback mCallback;private long mIndexStartTime;public IndexingTask(IndexingCallback callback) {mCallback = callback;}@Overrideprotected void onPreExecute() {mIndexStartTime = System.currentTimeMillis();mIsIndexingComplete.set(false);}@Overrideprotected Void doInBackground(Void... voids) {performIndexing();return null;}@Overrideprotected void onPostExecute(Void aVoid) {int indexingTime = (int) (System.currentTimeMillis() - mIndexStartTime);FeatureFactory.get(mContext).metricsFeatureProvider(mContext).logEvent(SettingsIntelligenceLogProto.SettingsIntelligenceEvent.INDEX_SEARCH,indexingTime);mIsIndexingComplete.set(true);if (mCallback != null) {mCallback.onIndexingFinished();}}}public void performIndexing() {final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE);final List<ResolveInfo> providers =mContext.getPackageManager().queryIntentContentProviders(intent, 0);final boolean isFullIndex = IndexDatabaseHelper.isFullIndex(mContext, providers);if (isFullIndex) {rebuildDatabase();}PreIndexData indexData = getIndexDataFromProviders(providers, isFullIndex);final long updateDatabaseStartTime = System.currentTimeMillis();updateDatabase(indexData, isFullIndex);IndexDatabaseHelper.setIndexed(mContext, providers);if (DEBUG) {final long updateDatabaseTime = System.currentTimeMillis() - updateDatabaseStartTime;Log.d(TAG, "performIndexing updateDatabase took time: " + updateDatabaseTime);}}
}
可以发现,在Settings的AndroidManifest.xml中指定一个Provider。
<providerandroid:name=".search.SettingsSearchIndexablesProvider"android:authorities="com.android.settings"android:multiprocess="false"android:grantUriPermissions="true"android:permission="android.permission.READ_SEARCH_INDEXABLES"android:exported="true"><intent-filter><action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER" /></intent-filter></provider>
SettingsSearchIndexablesProvider继承于SearchIndexablesProvider,SearchIndexablesProvider继承于ContentProvider, query方法进行了分类查询,插入,删除,更新均不支持,通过final修饰和抛出UnsupportedOperationException屏蔽了。
public abstract class SearchIndexablesProvider extends ContentProvider {@Overridepublic Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder) {try {switch (mMatcher.match(uri)) {case MATCH_RES_CODE:return queryXmlResources(null);case MATCH_RAW_CODE:return queryRawData(null);case MATCH_NON_INDEXABLE_KEYS_CODE:return queryNonIndexableKeys(null);case MATCH_SITE_MAP_PAIRS_CODE:return querySiteMapPairs();case MATCH_SLICE_URI_PAIRS_CODE:return querySliceUriPairs();case MATCH_DYNAMIC_RAW_CODE:return queryDynamicRawData(null);default:throw new UnsupportedOperationException("Unknown Uri " + uri);}} catch (UnsupportedOperationException e) {throw e;} catch (Exception e) {Log.e(TAG, "Provider querying exception:", e);return null;}}@Overridepublic final Uri insert(Uri uri, ContentValues values) {throw new UnsupportedOperationException("Insert not supported");}
}
以queryXmlResources为例,通过getSearchIndexableResourcesFromProvider方法获取数据集合,并保存到cursor中。bundles里一个class类型的集合。
public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {@Overridepublic Cursor queryXmlResources(String[] projection) {final MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);final List<SearchIndexableResource> resources =getSearchIndexableResourcesFromProvider(getContext());for (SearchIndexableResource val : resources) {final Object[] ref = new Object[INDEXABLES_XML_RES_COLUMNS.length];ref[COLUMN_INDEX_XML_RES_RANK] = val.rank;ref[COLUMN_INDEX_XML_RES_RESID] = val.xmlResId;ref[COLUMN_INDEX_XML_RES_CLASS_NAME] = val.className;ref[COLUMN_INDEX_XML_RES_ICON_RESID] = val.iconResId;ref[COLUMN_INDEX_XML_RES_INTENT_ACTION] = val.intentAction;ref[COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE] = val.intentTargetPackage;ref[COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS] = null; // intent target classcursor.addRow(ref);}return cursor;}private List<SearchIndexableResource> getSearchIndexableResourcesFromProvider(Context context) {final Collection<SearchIndexableData> bundles = FeatureFactory.getFactory(context).getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();List<SearchIndexableResource> resourceList = new ArrayList<>();for (SearchIndexableData bundle : bundles) {Indexable.SearchIndexProvider provider = bundle.getSearchIndexProvider();final List<SearchIndexableResource> resList =provider.getXmlResourcesToIndex(context, true);if (resList == null) {continue;}for (SearchIndexableResource item : resList) {item.className = TextUtils.isEmpty(item.className)? bundle.getTargetClass().getName(): item.className;}resourceList.addAll(resList);}return resourceList;}
}
SearchIndexableResourcesMobile继承于SearchIndexableResourcesBase,
public class SearchFeatureProviderImpl implements SearchFeatureProvider {@Overridepublic SearchIndexableResources getSearchIndexableResources() {if (mSearchIndexableResources == null) {mSearchIndexableResources = new SearchIndexableResourcesMobile();}return mSearchIndexableResources;}
}
SearchIndexableResourcesMobile类生成在IndexableProcessor中,IndexableProcessor设置的注解为SearchIndexable,SearchIndexable注解可以指定target(ALL/MOBILE/TV/WEAR/AUTO/ARC)对应不同平台。通过JavaPoet库来addCode实例化SearchIndexableData,getProviderValues方法返回的是带有SearchIndexable注解的所有类集合。
@SupportedSourceVersion(SourceVersion.RELEASE_9)
@SupportedAnnotationTypes({"com.android.settingslib.search.SearchIndexable"})
public class IndexableProcessor extends AbstractProcessor {@Overridepublic boolean process(Set<? extends TypeElement> annotations,for (Element element : roundEnvironment.getElementsAnnotatedWith(SearchIndexable.class)) {if (element.getKind().isClass()) {Name className = element.accept(new SimpleElementVisitor8<Name, Void>() {@Overridepublic Name visitType(TypeElement typeElement, Void aVoid) {return typeElement.getQualifiedName();}}, null);if (className != null) {SearchIndexable searchIndexable = element.getAnnotation(SearchIndexable.class);int forTarget = searchIndexable.forTarget();MethodSpec.Builder builder = baseConstructorBuilder;if (forTarget == SearchIndexable.ALL) {builder = baseConstructorBuilder;} else if ((forTarget & SearchIndexable.MOBILE) != 0) {builder = mobileConstructorBuilder;} else if ((forTarget & SearchIndexable.TV) != 0) {builder = tvConstructorBuilder;} else if ((forTarget & SearchIndexable.WEAR) != 0) {builder = wearConstructorBuilder;} else if ((forTarget & SearchIndexable.AUTO) != 0) {builder = autoConstructorBuilder;} else if ((forTarget & SearchIndexable.ARC) != 0) {builder = arcConstructorBuilder;}//实例化SearchIndexableDatabuilder.addCode("$N(new SearchIndexableData($L.class, $L"+ ".SEARCH_INDEX_DATA_PROVIDER));\n",addIndex, className, className);...}}inal MethodSpec getProviderValues = MethodSpec.methodBuilder("getProviderValues").addAnnotation(Override.class).addModifiers(Modifier.PUBLIC).returns(ParameterizedTypeName.get(ClassName.get(Collection.class),searchIndexableData)).addCode("return $N;\n", providers).build();final TypeSpec baseClass = TypeSpec.classBuilder(CLASS_BASE).addModifiers(Modifier.PUBLIC).addSuperinterface(ClassName.get(PACKAGE, "SearchIndexableResources")).addField(providers).addMethod(baseConstructorBuilder.build()).addMethod(addIndex).addMethod(getProviderValues).build();final JavaFile searchIndexableResourcesBase = JavaFile.builder(PACKAGE, baseClass).build();final JavaFile searchIndexableResourcesMobile = JavaFile.builder(PACKAGE,TypeSpec.classBuilder(CLASS_MOBILE).addModifiers(Modifier.PUBLIC).superclass(ClassName.get(PACKAGE, baseClass.name)).addMethod(mobileConstructorBuilder.build()).build()).build();
}
实例化SearchIndexableData,mTargetClass为className.class,mSearchIndexProvider为className.SEARCH_INDEX_DATA_PROVIDER,其中的className就是对应添加SearchIndexable注解的类名
public class SearchIndexableData {public SearchIndexableData(Class targetClass, Indexable.SearchIndexProvider provider) {mTargetClass = targetClass;mSearchIndexProvider = provider;}
}
总结一下,Settings搜索功能就是在需要被提供的页面添加@SearchIndexable注解,在这页面创建一个常量SEARCH_INDEX_DATA_PROVIDER,这个常量类型必须为Indexable.SearchIndexProvider。以TopLevelSettings为例。添加了@SearchIndexable注解,指定Target为MOBILE,也创建了SEARCH_INDEX_DATA_PROVIDER,Settings封装了一个基础的SearchIndexProvider,不返回任何要索引的数据,类名为BaseSearchIndexProvider。
@SearchIndexable(forTarget = MOBILE)
public class TopLevelSettings extends DashboardFragment implements SplitLayoutListener,PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =new BaseSearchIndexProvider(R.xml.top_level_settings) {@Overrideprotected boolean isPageSearchEnabled(Context context) {// Never searchable, all entries in this page are already indexed elsewhere.return false;}};
}
SearchIndexProvider和BaseSearchIndexProvider扩展的方法可以让我们准确处理菜单搜索需求。
public class BaseSearchIndexProvider implements Indexable.SearchIndexProvider {public BaseSearchIndexProvider() {}public BaseSearchIndexProvider(int xmlRes) {mXmlRes = xmlRes;}//返回SearchIndexableResource@Overridepublic List<SearchIndexableResource> getXmlResourcesToIndex(Context context, boolean enabled) {if (mXmlRes != 0) {final SearchIndexableResource sir = new SearchIndexableResource(context);sir.xmlResId = mXmlRes;return Arrays.asList(sir);}return null;}//返回SearchIndexableRaw集合@Overridepublic List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {return null;}//返回动态数据集合@Overridepublic List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {return null;}//无法搜索的集合@Override@CallSuperpublic List<String> getNonIndexableKeys(Context context) {...}//页面是否启用搜索protected boolean isPageSearchEnabled(Context context) {return true;}//获取xml设置禁用搜索的集合@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)public List<String> getNonIndexableKeysFromXml(Context context, @XmlRes int xmlResId,boolean suppressAllPage) {return getKeysFromXml(context, xmlResId, suppressAllPage);}
}
以上就是Settings的搜索逻辑。要测试新菜单的可搜索性,需要先清除Settings数据,让数据库重新添加数据。
总结
Settings菜单如果想要支持搜索,首先对应页面需要添加@SearchIndexable注解,其次在本页面创建一个常量SEARCH_INDEX_DATA_PROVIDER,然后根据需要重写需要的实现方法。这样这个菜单就支持搜索了。
SettingsIntelligence会扫描这些添加@SearchIndexable注解的页面,将这些页面的菜单添加到数据库中,查询时根据关键词进行匹配查询。
相关文章:
SettingsIntelligence
Android Settings 系列文章: Android Settings解析SettingsIntelligenceSettingsProvider 首语 Android Settings中搜索功能帮助我们可以快速访问设置项,进行自定义设置,以得到更佳的使用体验。Android Settings搜索的实现实际不在Setting…...
C#WPF Prism框架区域管理应用实例
本文实例演示C#WPFPrism框架区域管理应用实例 目录 一、Prism框架区域 二、不使用Prism框架的RegionManager 三、使用Prism框架的RegionManager 一、Prism框架区域...
LabVIEW基于机器视觉的钢轨表面缺陷检测系统
LabVIEW基于机器视觉的钢轨表面缺陷检测系统 机器视觉检测技术和LabVIEW软件程序,可以实现轨道工件的表面质量。CMOS彩色工业相机采集的图像通过图像预处理、图像阈值分割、形态分析、特征定位和图案匹配进行处理和分析。图形显示界面采用LabVIEW软件编程设计&…...
Qt程序的发布和打包,任何电脑都可以安装
## 1. Qt程序的发布 当Qt程序编写完成通过IDE编译就可以得到对应的可执行程序,这个可执行程序在本地运行是完全没有问题的(因为在本地有Qt环境,程序运行过程中可以加载到相关的动态库),但是如果我们想把这个Qt程序给到其他小伙伴使用可能就会出问题了,原因如下: 对方电…...
MD5生成和校验
MD5生成和校验 2021年8月19日席锦 任何类型的一个文件,它都只有一个MD5值,并且如果这个文件被修改过或者篡改过,它的MD5值也将改变。因此,我们会对比文件的MD5值,来校验文件是否是有被恶意篡改过。 什么是MD5ÿ…...
PostgreSQL 正则表达式匹配字段
在 PostgreSQL 数据库中,可以使用 ~ 和 !~ 操作符进行正则表达式的匹配和否定匹配。还可以使用 :: 操作符进行正则表达式的模式匹配。 例如,假设我们有一个名为 users 的表,其中有一个名为 email 的字段,我们可以使用以下 SQL 语句…...
关于iterm2的美化
iterm2 美化 笔者公司最近给发了一个新 M1 mac pro,所以一些软件需要重新安装。其中比较麻烦就是iterm2的一个美化工程 , 由于每次安装的效果都不尽相同所以这次写一个博客来记录一下 安装的过程 。 全程高能开始: 使用brew 来安装 iterm2 …...
Hook原理--逆向开发
今天我们将继续讲解逆向开发工程另一个重要内容--Hook原理讲解。Hook,可以中文译为“挂钩”或者“钩子”,逆向开发中改变程序运行的一种技术。按照如下过程进行讲解 Hook概述Hook技术方式fishhook原理及实例符号表查看函数名称总结 一、Hook概述 在逆…...
做数据可视化,谨记三大要点
数据可视化报表就是“一图胜千言”的最佳例子。数据可视化,也就是将数据图形化、图表化,以良好的视觉效果呈现数据,达到发现、分析、预测、监控、决策等目的。要想做出一份优秀的数据可视化报表,那就要在做报表时谨记三大要点&…...
软件设计原则-接口隔离原则讲解以及代码示例
接口隔离原则 一,介绍 1.前言 接口隔离原则(Interface Segregation Principle,ISP)是面向对象设计中的一个原则,提倡使用多个专门的接口,而不使用单一的大接口。它最早由Robert C. Martin在其《敏捷软件…...
yolov8x-p2 实现 tensorrt 推理
简述 在最开始的yolov8提供的不同size的版本,包括n、s、m、l、x(模型规模依次增大,通过depth, width, max_channels控制大小),这些都是通过P3、P4和P5提取图片特征; 正常的yolov8对象检测模型输出层是P3、…...
Type Script的变量类型
Typescript 的重要特性之一就是数据有类型了。 常见的类型如:字符串、数值、布尔等都有了明确的定义。 变量声明的格式 let 变量名:类型 初始值;字符型 let str:string "abc";数值型 数值型也支持不同的进制,用前缀区分 支持 整…...
系统架构师备考倒计时13天(每日知识点)
1. 数据仓库四大特点 面向主题的。操作型数据库的数据组织面向事务处理任务,各个业务系统之间各自分离,而数据仓库中的数据是按照一定的主题域进行组织的。集成的。数据仓库中的数据是在对原有分散的数据库数据抽取、清理的基础上经过系统加工、汇总和整…...
20 | Spring Data JPA 中文文档
Spring Data JPA 中文文档 1. 前言 Spring Data JPA 为 Jakarta Persistence API(JPA)提供 repository 支持。它简化了需要访问JPA数据源的应用程序的开发。 1.1. 项目元数据 版本控制: https://github.com/spring-projects/spring-data-jpaBug跟踪:…...
【AOA-VMD-LSTM分类故障诊断】基于阿基米德算法AOA优化变分模态分解VMD的长短期记忆网络LSTM分类算法(Matlab代码)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
K8s:Pod 中 command、args 与 Dockerfile 中 CMD、 ENTRYPOINT 的对应关系
写在前面 前几天被问到,这里整理笔记之前也没怎么注意这个问题理解不足小伙伴帮忙指正 曾以为老去是很遥远的事,突然发现年轻是很久以前的事了。时光好不经用,抬眼已是半生,所谓的中年危机,真正让人焦虑的不是孤单、不…...
Visual Studio Code (VS Code)安装教程
Visual Studio Code(简称“VS Code”)。 1.下载安装包 VS Code的官网: Visual Studio Code - Code Editing. Redefined 首先提及一下,vscode是不需要破解操作的; 第一步,看好版本,由于我的系…...
技巧 | 如何解决 zsh: permission denied 问题 | Mac
技巧 | 如何解决 zsh: permission denied 问题 | Mac 问题描述 在 macOS 系统终端执行 sh 程序脚本时,抛出异常 zsh: permission denied 原因分析 用户没有权限,所以才出现了这个错误,所以只需要用 chmod 修改一下权限就可以了 解决方法…...
【JavaEE】线程安全的集合类 -- 多线程篇(9)
线程安全的集合类 多线程环境使用 ArrayList多线程环境使用队列多线程环境使用哈希表 多线程环境使用 ArrayList 自己使用同步机制 (synchronized 或者 ReentrantLock)Collections.synchronizedList(new ArrayList); synchronizedList 是标准库提供的一个基于 synchronized 进…...
【MySQL架构篇】MySQL字符集、大小写规范及默认数据库
文章目录 1. 字符集与字符集比较规则2. 大小写规范3. 默认数据库4. 与文件系统相关 1. 字符集与字符集比较规则 MySQL有4个级别的字符集和比较规则,分别是 服务器级别数据库级别表级别列级别 当创建对应表或列未指定字符集时,默认会取其上一级别的字符…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
