展开说说:Android服务之bindService解析
前面两篇文章我们分别总结了Android四种Service的基本使用以及源码层面总结一下startService的执行过程,本篇继续从源码层面总结bindService的执行过程。
本文依然按着是什么?有什么?怎么用?啥原理?的步骤来分析。
bindService使用方法和调用流程都与startService时有很多相似之处,方便的话请先阅读上一篇《展开说说:Android服务之startService解析》。
- 是什么
调用bindService()来创建,调用方可以通过一个IBinder接口和service进行通信,需要通过ServiceConnection建立连接。多用于有交互的场景。
只能调用方通过unbindService()方法来断开连接。调用方可以和Service通讯,并且一个service可以同时和多个调用方存在绑定关系,解除绑定也需要所有调用全部解除绑定之后系统才会销毁service。
2、有什么
Service和Activity一样也有自己的生命周期,也需要在AndroidManifest.xml中注册。另外bindService的使用比startService要复杂一些:第一需要中创建一个Binder子类并定义方法来给使用者调用在onBind方法中返回它的实例;第二使用者需要创建ServiceConnection对象,并在onServiceConnected回调方法调用Binder子类中定义方法。
2.1 在AndroidManifest.xml中注册
和startService注册流程一样:
<service android:name="com.example.testdemo.service.ServiceJia" />
2.2 bindService时Service的生命周期
与startService时执行的生命周期有些不同。
onCreate
它只在Service刚被创建的时刻被调用,Service在运行中,这个方法将不会被调用。也就是只有经历过onDestroy生命周期以后再次。
onBind
当另一个组件调用 bindService()想要与Service绑定(例如执行 RPC)时执行,在此方法的实现中,必须通过返回 IBinder 提供一个接口,供客户端用来与服务通信。您必须始终实现此方法;但是,如果您不想允许绑定,则应返回 null。这个方法默认时返回null。
onUnbind
调用方调用 unbindService() 来解除Service绑定时执行。
onDestroy
所有绑定到Service的调用方都解绑以后,则系统会销毁该服务。
onRebind
当Service中的onUnbind方法返回true,并且Service调用unbindService之后并没有销毁,此时重新绑定时将会触发onRebind。Service执行过onBind又onUnbind返回true并且还没执行onDestroy,等再次bindService就会执行它。
日志打印
bindService
2024-12-01 11:19:14.434 30802-30802/com.example.testdemo3 E/com.example.testdemo3.service.ServiceJia: onCreate:
2023-12-01 11:19:14.436 30802-30802/com.example.testdemo3 E/com.example.testdemo3.activity.ServiceActivity: onServiceConnected:
2023-12-01 11:19:14.436 30802-30802/com.example.testdemo3 E/com.example.testdemo3.service.ServiceJia: JiaBinder --doSomething: start conncetion //这里不是生命周期,是binder对象调用binder内方法的打印,证明完成交互
unbindService
2023-12-01 11:21:10.705 12765-12765/com.example.testdemo3 E/com.example.testdemo3.service.ServiceJia: onUnbind:
2023-12-01 11:21:10.705 12765-12765/com.example.testdemo3 E/com.example.testdemo3.service.ServiceJia: onDestroy:
- 怎么用
因为是有交互的嘛,因此肯定比那些启动以后就成了甩手掌柜的startService使用稍微负责一些,第一需要中创建一个Binder子类并定义方法来给使用者调用在onBind方法中返回它的实例;第二使用者需要创建ServiceConnection对象,并在onServiceConnected回调方法调用Binder子类中定义方法。
具体可以参考前面的《展开说说:Android四大组件之Service使用》已经总结了使用方法,这里不在赘述。
- 啥原理,SDK版本API 30
bindService调用流程都与startService时有很多相似之处,方便的话请先阅读上一篇《展开说说:Android服务之startService解析》。
bindService的启动方法是调用
Intent serviceIntent = new Intent(ServiceActivity.this, ServiceJia.class);
bindService(serviceIntent,serviceConnection, Context.BIND_AUTO_CREATE)
然后我们顺着bindService方法开始解析源码,Go :
4.1 从ContexWrapper的bindService开始,同startService:
@Override
public boolean bindService(Intent service, ServiceConnection conn,int flags) {return mBase.bindService(service, conn, flags);
}
4.2 ContextImpl类bindService
mBase的类型是Context,但实际代码逻辑是在它的实现类ContextImpl类。
@Overridepublic boolean bindService(Intent service, ServiceConnection conn, int flags) {warnIfCallingFromSystemProcess();return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), null,getUser());
}private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,String instanceName, Handler handler, Executor executor, UserHandle user) {// Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.IServiceConnection sd;if (conn == null) {throw new IllegalArgumentException("connection is null");}if (handler != null && executor != null) {throw new IllegalArgumentException("Handler and Executor both supplied");}if (mPackageInfo != null) {if (executor != null) {sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags);} else {sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);}} else {throw new RuntimeException("Not supported in system context");}validateServiceIntent(service);try {IBinder token = getActivityToken();if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null&& mPackageInfo.getApplicationInfo().targetSdkVersion< android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {flags |= BIND_WAIVE_PRIORITY;}service.prepareToLeaveProcess(this);int res = ActivityManager.getService().bindIsolatedService(mMainThread.getApplicationThread(), getActivityToken(), service,service.resolveTypeIfNeeded(getContentResolver()),sd, flags, instanceName, getOpPackageName(), user.getIdentifier());if (res < 0) {throw new SecurityException("Not allowed to bind to service " + service);}return res != 0;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}
bindService调用bindServiceCommon方法。将ServiceConnection 转为Binder的实现类IServiceConnection方便跨进程的远程服务的回调自己定义的方法。
4.3 来到LoadedApk
final @NonNull LoadedApk mPackageInfo;
因此来到LoadedApk 查看getServiceDispatcher方法:
@UnsupportedAppUsage
public final IServiceConnection getServiceDispatcher(ServiceConnection c,Context context, Handler handler, int flags) {return getServiceDispatcherCommon(c, context, handler, null, flags);
}private IServiceConnection getServiceDispatcherCommon(ServiceConnection c,Context context, Handler handler, Executor executor, int flags) {synchronized (mServices) {LoadedApk.ServiceDispatcher sd = null;ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);if (map != null) {if (DEBUG) Slog.d(TAG, "Returning existing dispatcher " + sd + " for conn " + c);sd = map.get(c);}if (sd == null) {if (executor != null) {sd = new ServiceDispatcher(c, context, executor, flags);} else {sd = new ServiceDispatcher(c, context, handler, flags);}if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c);if (map == null) {map = new ArrayMap<>();mServices.put(context, map);}map.put(c, sd);} else {sd.validate(context, handler, executor);}return sd.getIServiceConnection();}
}
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
= new ArrayMap<>();
mServices记录了应用当前活动的ServiceConnection和ServiceDispatcher的映射关系,不知是否记得ActivityThread中也有一个final ArrayMap<IBinder, Service> mServices = new ArrayMap<>(); 记录了IBinder,和Service的映射关系。
继续说LoadedApk中的service哈,上面代码会判断是否存在相同的ServiceConnection,如果不存在就创建新ServiceDispatcher实例并将其存储在mService中,key时ServiceConnection,value为ServiceDispatcher,ServiceDispatcher内部存储了ServiceConnection和InnerConnection对象。在调用bindService以后Service和调用方成功建立连接时系统会通过InnerConnection调用ServiceConnection中的onServiceConnected方法,此时我们就可以利用传过来的IBinder调用Service中的方法完成交互了。这个过程支持跨进程IPC通信,比如两个进程使用AIDL通信。
4.4 ContextImpl类bindService的bindIsolatedService
返回头看上面4.2中的bindService方法,继续向下看会调用ActivityManagerService的bindIsolatedService方法:
synchronized(this) {return mServices.bindServiceLocked(caller, token, service,resolvedType, connection, flags, instanceName, callingPackage, userId);}
4.5 来到ActiveService类的bindServiceLocked
继续调用本类的bringUpServiceLocked:
bringUpServiceLocked(serviceRecord,
serviceIntent.getFlags(),
callerFg, false, false);
在调用本类realStartServiceLocked:
realStartServiceLocked(r, app, execInFg);
一般来说源码中当一个方法多次穿梭调用之后突然带上了real,那一定是离真相不远了。
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
app.getReportedProcState());
这一行就很熟悉了,和上一篇startService一样,调用的ApplicationThread来创建Service实例并调用它的onCreate生命周期。
上一篇分析这个方法之下是调用onStartCommand生命周期
,没错这里也不例外下面requestServiceBindingsLocked(r, execInFg)也会去调用ApplicationThread的scheduleBindService:
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.getReportedProcState());
4.6来到ApplicationThread
利用它封装的handler发送BIND_SERVICE消息:
public final void scheduleBindService(IBinder token, Intent intent,boolean rebind, int processState) {updateProcessState(processState, false);BindServiceData s = new BindServiceData();s.token = token;s.intent = intent;s.rebind = rebind;if (DEBUG_SERVICE)Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="+ Binder.getCallingUid() + " pid=" + Binder.getCallingPid());sendMessage(H.BIND_SERVICE, s);}
接收消息:
case BIND_SERVICE:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");handleBindService((BindServiceData)msg.obj);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;
关键来咯:
private void handleBindService(BindServiceData data) {Service s = mServices.get(data.token);if (DEBUG_SERVICE)Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);if (s != null) {try {data.intent.setExtrasClassLoader(s.getClassLoader());data.intent.prepareToEnterProcess();try {if (!data.rebind) {IBinder binder = s.onBind(data.intent);ActivityManager.getService().publishService(data.token, data.intent, binder);} else {s.onRebind(data.intent);ActivityManager.getService().serviceDoneExecuting(data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);}} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}} catch (Exception e) {if (!mInstrumentation.onException(s, e)) {throw new RuntimeException("Unable to bind to service " + s+ " with " + data.intent + ": " + e.toString(), e);}}}
}
上面代码显示根据token获取Service对象,然后判断首次绑定就调用onBind生命周期,已经绑定过就调用onReBind生命周期,返回的IBinder对象就可以用来调用Service中的方法了。但是为了让调用方拿到这个IBinder就同过onServiceConnected方法回调回去,这个工作就有ActivityManagerService的publishService方法来完成。
4.6 来到ActivityManagerService
public void publishService(IBinder token, Intent intent, IBinder service) {// Refuse possible leaked file descriptorsif (intent != null && intent.hasFileDescriptors() == true) {throw new IllegalArgumentException("File descriptors passed in Intent");}synchronized(this) {if (!(token instanceof ServiceRecord)) {throw new IllegalArgumentException("Invalid service token");}mServices.publishServiceLocked((ServiceRecord)token, intent, service);}}
然后它有调用了ActiveService的publishServiceLocked方法来处理:
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {final long origId = Binder.clearCallingIdentity();try {if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "PUBLISHING " + r+ " " + intent + ": " + service);if (r != null) {Intent.FilterComparison filter= new Intent.FilterComparison(intent);IntentBindRecord b = r.bindings.get(filter);if (b != null && !b.received) {b.binder = service;b.requested = true;b.received = true;ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();for (int conni = connections.size() - 1; conni >= 0; conni--) {ArrayList<ConnectionRecord> clist = connections.valueAt(conni);for (int i=0; i<clist.size(); i++) {ConnectionRecord c = clist.get(i);if (!filter.equals(c.binding.intent.intent)) {if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Not publishing to: " + c);if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bound intent: " + c.binding.intent.intent);if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Published intent: " + intent);continue;}if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Publishing to: " + c);try {c.conn.connected(r.name, service, false);} catch (Exception e) {Slog.w(TAG, "Failure sending service " + r.shortInstanceName+ " to connection " + c.conn.asBinder()+ " (in " + c.binding.client.processName + ")", e);}}}}serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);}} finally {Binder.restoreCallingIdentity(origId);}
}
它在for循环里调用一行代码:
c.conn.connected(r.name, service, false);
顺着代码看:
Conn的类型是是在ConnectionRecord类定义的IServiceConnection:
final IServiceConnection conn; // The client connection.
Service就是建立连接的Ibinder实例。
4.7再次来到LoadedApk类
看一下IServiceConnection类connected方法:
private static class InnerConnection extends IServiceConnection.Stub {@UnsupportedAppUsagefinal WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;InnerConnection(LoadedApk.ServiceDispatcher sd) {mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);}public void connected(ComponentName name, IBinder service, boolean dead)throws RemoteException {LoadedApk.ServiceDispatcher sd = mDispatcher.get();if (sd != null) {sd.connected(name, service, dead);}}
}
而它又调用了ActivityThread类,mActivityThread就是其中Handler子类 H ,这一步就是为了利用Handler在主线程回调给调用方的onServiceConnected:
public void connected(ComponentName name, IBinder service, boolean dead) {if (mActivityExecutor != null) {mActivityExecutor.execute(new RunConnection(name, service, 0, dead));} else if (mActivityThread != null) {mActivityThread.post(new RunConnection(name, service, 0, dead));} else {doConnected(name, service, dead);}
}
RunConnection 的实现:
private final class RunConnection implements Runnable {RunConnection(ComponentName name, IBinder service, int command, boolean dead) {mName = name;mService = service;mCommand = command;mDead = dead;}public void run() {if (mCommand == 0) {doConnected(mName, mService, mDead);} else if (mCommand == 1) {doDeath(mName, mService);}}final ComponentName mName;final IBinder mService;final int mCommand;final boolean mDead;
}
回调onServiceConnected,彻底呼应上了:
public void doConnected(ComponentName name, IBinder service, boolean dead) {ServiceDispatcher.ConnectionInfo old;ServiceDispatcher.ConnectionInfo info;synchronized (this) {if (mForgotten) {// We unbound before receiving the connection; ignore// any connection received.return;}old = mActiveConnections.get(name);if (old != null && old.binder == service) {// Huh, already have this one. Oh well!return;}if (service != null) {// A new service is being connected... set it all up.info = new ConnectionInfo();info.binder = service;info.deathMonitor = new DeathMonitor(name, service);try {service.linkToDeath(info.deathMonitor, 0);mActiveConnections.put(name, info);} catch (RemoteException e) {// This service was dead before we got it... just// don't do anything with it.mActiveConnections.remove(name);return;}} else {// The named service is being disconnected... clean up.mActiveConnections.remove(name);}if (old != null) {old.binder.unlinkToDeath(old.deathMonitor, 0);}}// If there was an old service, it is now disconnected.if (old != null) {mConnection.onServiceDisconnected(name);}if (dead) {mConnection.onBindingDied(name);}// If there is a new viable service, it is now connected.if (service != null) {mConnection.onServiceConnected(name, service);} else {// The binding machinery worked, but the remote returned null from onBind().mConnection.onNullBinding(name);}
}
至此bindService的绑定流程分析完毕!
才疏学浅,如有错误,欢迎指正,多谢。
相关文章:
展开说说:Android服务之bindService解析
前面两篇文章我们分别总结了Android四种Service的基本使用以及源码层面总结一下startService的执行过程,本篇继续从源码层面总结bindService的执行过程。 本文依然按着是什么?有什么?怎么用?啥原理?的步骤来分析。 b…...
node-sass 老版本4.14.0 安装失败解决办法
旧项目 npm install 发现 node-sass 安装 失败 切换淘宝镜像之后 不能完全解决问题。因为需要编译,本地没有Python环境不能实现 安装node-sass时,在install阶段会从Github上下载一个叫binding.node的文件,而「GitHub Releases」里的文件…...
最近很火的字幕截图生成器
网址 https://disksing.com/fake-screenshot/ 最近很火的字幕截图生成器,对于自媒体来说真的太实用了 另外透露一下,你仔细研究就会发现,这是个纯前端的项目...
使用RabbitMQ实现可靠的消息传递机制
使用RabbitMQ实现可靠的消息传递机制 大家好,我是微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 1. RabbitMQ简介 RabbitMQ是一个开源的消息代理软件,实现了高级消息队列协议(AMQP&…...
Function Call ReACT,Agent应用落地的加速器_qwen的function calling和react有什么不同
探索智能体Agent的未来之路:Function Call与ReACT框架的较量,谁能引领未来? 引言 各大平台出现智能体应用创建,智能体逐渐落地,背后的使用哪种框架? 随着各大平台,例如百度千帆APPbuilder、阿…...
Java的JSONPath(fastjson)使用总结
背景 最近使用json实现复杂业务配置, 因为功能需要解析读取json的中节点数据。如果使用循环或者stream处理,可以实现,但是都过于麻烦。在想能否使用更简单json读取方式,正好发现fastjson支持该功能,本文做一个记录 案例说明 示…...
【大模型】大语言模型:光鲜背后的阴影——事实准确性和推理能力的挑战
大语言模型:光鲜背后的阴影——事实准确性和推理能力的挑战 引言一、概念界定二、事实准确性的局限2.1 训练数据的偏差2.2 知识的时效性问题2.3 复杂概念的理解与表述 三、推理能力的局限3.1 表层理解与深层逻辑的脱节3.2 缺乏常识推理3.3 无法进行长期记忆和连续推…...
Java面向对象练习(1.手机类)(2024.7.4)
手机类 package Phone;public class Phone {private String brand;private int price;private String color;public Phone(){}public Phone(String brand, int price, String color){this.brand brand;this.price price;this.color color;}public void setBrand(String bra…...
智慧生活新篇章,Vatee万腾平台领航前行
在21世纪的科技浪潮中,智慧生活已不再是一个遥远的梦想,而是正逐步成为我们日常生活的现实。从智能家居的温馨便捷,到智慧城市的高效运转,科技的每一次进步都在为我们的生活增添新的色彩。而在这场智慧生活的变革中,Va…...
Spring Cloud Gateway报sun.misc.Unsafe.park(Native Method)
项目引入spring cloud gateway的jar报,启动的时候报: [2024-07-05 10:10:16.162][main][ERROR][org.springframework.boot.web.embedded.tomcat.TomcatStarter][61]:Error starting Tomcat context. Exception: org.springframework.beans.factory.Bean…...
select single , select endselect
select single , select endselect single 根据条件找到一条数据,就出来了。 select endselect是在里面循环,每次找一条,依次放到into table中,或者放到into work area中,下面append table 。 实际开发中不建议这么操…...
后端学习(一)
添加数据库包: 数据库连接时 发生错误: 解决方式: SqlConnection conn new SqlConnection("serverlocalhost;databaseMyBBSDb;uidsa;pwd123456;Encryptfalse;") ;conn.Open();SqlCommand cmd new SqlCommand("SELECT * FROM…...
【活动行】参与上海两场线下活动,教育生态行业赛总决赛活动和WAIC人工智能大会活动 - 上海活动总结
目录 背景决赛最后一公里领域范围 决赛作品AI智教相机辅导老师Copilot辅导老师Copilot雅思写作竞技场 优秀作品总结 背景 决赛 百度发起的千帆杯教育生态行业赛于2024年7月4日进行线下决赛,博主虽然没能进入决赛,但也非常荣幸能够以嘉宾身份到现场给进…...
conda 安装设置
安装anaconda 推荐官网下载和安装,最新版本是anaconda3+python3.11,个人选择。有可能找不到 Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror Tips:小白一定要全部勾选,特别第二项“add anaconda3 to my path environment variable…...
用PlantUML和语雀画UML类图
概述 首先阐述一下几个简单概念: UML:是统一建模语言(Unified Modeling Language)的缩写,它是一种用于软件工程的标准化建模语言,旨在提供一种通用的方式来可视化软件系统的结构、行为和交互。UML由Grady…...
uniapp微信小程序电子签名
先上效果图,不满意可以直接关闭这页签 新建成单独的组件,然后具体功能引入,具体功能点击签名按钮,把当前功能页面用样式隐藏掉,v-show和v-if也行,然后再把这个组件显示出来。 【签名-撤销】原理是之前绘画时…...
MetaPoint_速读
Meta-Point Learning and Refining for Category-Agnostic Pose Estimation https://arxiv.org/abs/2404.14808https://github.com/chenbys/metapointabstract 这篇文章介绍了一种名为Meta-Point Learning and Refining的框架,用于实现类别不可知的姿势估计。该框…...
数据库逆向工程工具reverse_sql
reverse_sql 是一个用于解析和转换 MySQL 二进制日志(binlog)的工具。它可以将二进制日志文件中记录的数据库更改操作(如插入、更新、删除)转换为反向的 SQL 语句,以便对系统或人为产生的误操作进行数据回滚和恢复。 *…...
四大内网穿透利器对比
本文精选四款市场上的佼佼者——巴比达、花生壳、Frp及NatApp,详细剖析它们的特点与优势,助力企业和个人用户精准选择,其中特别强调了巴比达在企业级安全访问方面的突出贡献。 1. 巴比达 特点 深度安全防护:巴比达提供全方位安…...
【LeetCode】每日一题:跳跃游戏 II
给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i j] 处: 0 < j < nums[i] i j < n 返回到达 nums[n - 1] 的最小…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...
无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...
计算机基础知识解析:从应用到架构的全面拆解
目录 前言 1、 计算机的应用领域:无处不在的数字助手 2、 计算机的进化史:从算盘到量子计算 3、计算机的分类:不止 “台式机和笔记本” 4、计算机的组件:硬件与软件的协同 4.1 硬件:五大核心部件 4.2 软件&#…...
