ConnectivityService框架初识-程序员宅基地

技术标签: NetworkAge  Connectivi  NetworkFac  网络连接机制  android框架  NetworkMon  

Android中提供的数据业务方式有几种:移动数据网络,WIFI,热点,网线等。这些数据业务本身可以独立使用,但是同一时刻,只能使用其中的一种数据业务方式。管理这些数据业务方式的使用由ConnectivityService,NetworkFactory,NetworkAgent,NetworkMonitor等来完成,ConnectivityService处于核心调度位置。

ConnectivityService框架主要有四个方面组成:

一 . 网络有效性检测(NetworkMonitor)
二 . 网络评分机制(NetworkFactory)
三 . 路由配置信息的获取(NetworkAgent)
四 . 网络物理端口的设置(Netd)

其大体框架图如下:
这里写图片描述

这篇博客就针对前两个方面进行逐一分析,Netd这一块暂时还没研究到,待日后有空再整理下。

ConnectivityService的工作总结起来就是:通过wifi,mobile data,Tethering,VPN 等方式来获取路由配置信息。无论通过哪种方式,获取到路由配置信息后,需要交给ConnectivityService来处理,ConnectivityService通过ping网络来检查网络的有效性,进而影响到各个数据业务方式的评分值,ConnectivityService通过这些评分值来决定以哪个数据业务方式连接网络。决定好数据业务方式后,把这些路由配置信息设置到网络物理设备中。这样我们的手机就可以正常上网了。


初始化:

ConnectivityService属于系统服务,在SystemServer中被启动。

SystemServer启动的服务:

NetworkManagementService networkManagement = null;
NetworkStatsService networkStats = null;
NetworkPolicyManagerService networkPolicy = null;
ConnectivityService connectivity = null;
NetworkScoreService networkScore = null;

一 . ConnectivityService初始化

1.获取其他服务的接口

private INetworkManagementService mNetd;
private INetworkStatsService mStatsService;
private INetworkPolicyManager mPolicyManager;
mNetd = checkNotNull(netManager, "missing INetworkManagementService");
mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);

2.注册其他必要的监听和广播,以便接收变化信息和通知变化信息。

mNetd.registerObserver(mTethering);
mNetd.registerObserver(mDataActivityObserver);

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_STARTED);
intentFilter.addAction(Intent.ACTION_USER_STOPPED);
intentFilter.addAction(Intent.ACTION_USER_ADDED);
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);

二 . NetworkFactory的初始化

NetworkFactory负责了网络评分机制的功能,为了在手机开机后可以及时依靠网络评分机制来选择网络。ConnectivityService服务起来后,在各个模块的初始化过程中,NetworkFactory必须要启动起来。以下的时序图只画了mobile data和wifi模块的NetworkFactory启动流程:
这里写图片描述

NetworkFactory在register()之后通过AsyncChannel与ConnectivityService建立起了连接,这一块的逻辑流程,如果看不太懂,那么需要去看看:AsyncChannel的工作机制

public void register() {
    if (DBG) log("Registering NetworkFactory");
    if (mMessenger == null) {
        mMessenger = new Messenger(this);
        ConnectivityManager.from(mContext).registerNetworkFactory(mMessenger, LOG_TAG);
    }
}
public void registerNetworkFactory(Messenger messenger, String name) {
    enforceConnectivityInternalPermission();
    NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger, new AsyncChannel());
    mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi));
}
private void handleRegisterNetworkFactory(NetworkFactoryInfo nfi) {
    if (DBG) log("Got NetworkFactory Messenger for " + nfi.name);
    mNetworkFactoryInfos.put(nfi.messenger, nfi);
    nfi.asyncChannel.connect(mContext, mTrackerHandler, nfi.messenger);
}

三 . NetworkAgent的初始化

NetworkAgent是一个网络代理,它里面保存了一些路由的配置信息,比如NetworkInfo,LinkProperties,NetworkCapabilities等。NetworkAgent的初始化都是在路由配置信息获取成功之后。比如打开数据开关,打开wifi开关等操作之后。

注:
NetworkInfo 描述一个给定类型的网络接口的状态方面的信息,包括网络连接状态、网络类型、网络可连接性、是否漫游等信息
LinkProperties 描述一个网络连接属性信息(包含网络地址、网关、DNS、HTTP代理等属性信息
NetworkCapabilities 描述一个网络连接能力方面的信息,包括带宽、延迟等

四 . NetworkMonitor的初始化

NetworkMonitor主要是检测网络有效性的,通过Http封装类去ping一个网站,根据ping网站的结果来影响评分值。因此,它的初始化是在NetworkAgent初始化之后,必须要获取到路由配置信息NetworkAgent后才会去初始化。


一 . 网络有效性检测(NetworkMonitor)

NetworkMonitor是一个状态机。负责检测网络有效性,也就是ping网络的过程。ping网络过程中产生的几种状态如下:

DefaultState 默认状态
EvaluatingState 验证状态
ValidatedState 验证通过状态
LingeringState 休闲状态,表示网络的验证位是真实的,并且曾经是满足特定NetworkRequest的最高得分网络,但是此时另一个网络满足了NetworkRequest的更高分数,在断开连接前的一段时间前,该网络被“固定”为休闲状态。
CaptivePortalState 强制门户状态
MaybeNotifyState 可能通知状态,表示用户可能已被通知需要登录。 在退出该状态时,应该小心清除通知。

NetworkMonitor中各个状态之间的关系:

这里写图片描述

以正常的ping网站过程为例,DefaultState为默认状态,NetworkMonitor接收到CMD_NETWORK_CONNECTED事件消息后,先由DefaultState状态处理,然后由EvaluatingState处理,最后交给ValidatedState处理。这一块的逻辑流程,如果看不太懂,那么需要去看看StateMachine状态机的使用:StateMachine状态机初识

从NetworkMonitor的初始化,到ping网站的过程,到ping网站的结果影响评分值。这个过程的时序图如下:
这里写图片描述

接下来将按照时序图中的三大步骤去结合代码分析。

1 . NetworkMonitor的初始化(以mobile data为例)

DataConnection从modem中获取到了代理信息,并把此代理信息保存到了NetworkAgent中:

mNetworkAgent = new DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(),
        "DcNetworkAgent", mNetworkInfo, makeNetworkCapabilities(), mLinkProperties,
        50, misc);

在NetworkAgent的构造函数中,把它自己注册到了ConnectivityService中,

ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
        Context.CONNECTIVITY_SERVICE);
netId = cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
        new LinkProperties(lp), new NetworkCapabilities(nc), score, misc);

接下来的流程就如时序图所示了,意味着每产生一个代理信息NetworkAgent的对象,就会有自己相应的NetworkMonitor状态机来处理ping网站的过程。

2 . ping网站的过程

NetworkMonitor状态机运行起来后,接收到sendMessage的消息就可以做相应的处理。这里比较重要的就是CMD_NETWORK_LINGER和CMD_NETWORK_CONNECTED消息,分别由ConnectivityService的linger()和unlinger()方法封装发送的操作。

linger():

封装了CMD_NETWORK_LINGER消息的发送操作,让NetworkMonitor进入到休闲状态:
CMD_NETWORK_LINGER消息首先进入到DefaultState.processMessage()处理:

case CMD_NETWORK_LINGER:
    transitionTo(mLingeringState);

CMD_NETWORK_LINGER消息切换到LingeringState处理,enter()做一个CMD_LINGER_EXPIRED消息延迟发送:

public void enter() {
    mEvaluationTimer.reset();
    final String cmdName = ACTION_LINGER_EXPIRED + "." + mNetId;
mWakeupMessage = makeWakeupMessage(mContext, getHandler(), cmdName, CMD_LINGER_EXPIRED);
    long wakeupTime = SystemClock.elapsedRealtime() + mLingerDelayMs;
    mWakeupMessage.schedule(wakeupTime);
}

LingeringState.processMessage()中对延迟消息CMD_LINGER_EXPIRED做处理:

case CMD_LINGER_EXPIRED:
    mConnectivityServiceHandler.sendMessage(
            obtainMessage(EVENT_NETWORK_LINGER_COMPLETE, mNetworkAgentInfo));
    return HANDLED;

ConnectivityService中处理EVENT_NETWORK_LINGER_COMPLETE消息:

case NetworkMonitor.EVENT_NETWORK_LINGER_COMPLETE: {
    NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
    if (isLiveNetworkAgent(nai, msg.what)) {
        handleLingerComplete(nai);
    }
    break;
}
private void handleLingerComplete(NetworkAgentInfo oldNetwork) {
    teardownUnneededNetwork(oldNetwork);
}
private void teardownUnneededNetwork(NetworkAgentInfo nai) {
    for (int i = 0; i < nai.networkRequests.size(); i++) {
        NetworkRequest nr = nai.networkRequests.valueAt(i);
if (!isRequest(nr)) continue;
        break;
    }
    nai.asyncChannel.disconnect();
}

综上,CMD_NETWORK_LINGER消息的处理就是让NetworkMonitor进入空闲状态,NetworkMonitor处于空闲状态说明此网络不再需要,可以释放掉ConnectivityService和NetworkAgent的连接。

unlinger():

封装了CMD_NETWORK_CONNECTED消息的发送操作,让NetworkMonitor进入到非休闲状态:
CMD_NETWORK_CONNECTED消息首先进入到DefaultState.processMessage()处理:

case CMD_NETWORK_CONNECTED:
    transitionTo(mEvaluatingState);
    return HANDLED;

CMD_NETWORK_LINGER消息切换到EvaluatingState处理,enter()方法发送CMD_REEVALUATE消息:

public void enter() {
sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
}

CMD_REEVALUATE消息由EvaluatingState.processMessage()处理:
Ping网络的关键地方:

CaptivePortalProbeResult probeResult = isCaptivePortal();

isCaptivePortal()通过HttpURLConnection类去ping一个网站,android原生给的网站在中国由于墙的存在,是ping不通的,因此就会出现wifi和信号格旁边有一个感叹号。芯片厂商一般会对这个网站进行客制化:这里写图片描述

private static String getCaptivePortalServerUrl(Context context, boolean isHttps) {
    String server = Settings.Global.getString(context.getContentResolver(),
            Settings.Global.CAPTIVE_PORTAL_SERVER);
if (server == null) server = DEFAULT_SERVER_SECONDARY;
    return (isHttps ? "https" : "http") + "://" + server + "/generate_204";
}

在log中可以找到当前ping的网站是哪一个:

这里写图片描述

3 . 根据ping网站的结果影响评分值

if (probeResult.isSuccessful()) {
    transitionTo(mValidatedState);
} else if (probeResult.isPortal()) {
transitionTo(mCaptivePortalState);
} else {
final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
sendMessageDelayed(msg, mReevaluateDelayMs);
mReevaluateDelayMs *= 2;
if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) {
    mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS;
}
}
boolean isSuccessful() { return mHttpResponseCode == 204; }
boolean isPortal() {
    return !isSuccessful() && mHttpResponseCode >= 200 && mHttpResponseCode <= 399;
}

根据ping网络的结果来执行不同的操作:
一.如果ping网络成功,网络返回204,切换到ValidatedState状态处理。
二.如果ping网络失败,网络返回200~399,转到CaptivePortalState状态处理。
三.如果ping网络失败,不是204,也不是200~399,则发送CMD_REEVALUATE消息,重新触发ping网络的动作。第一次失败,8s后重新ping网络,第二次失败,16s后重新ping网络,时间依次倍增,最长的时间间隔为10分钟。

重点关注ping网络成功后,ValidatedState状态的处理:ValidatedState.enter()

mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
        NETWORK_TEST_RESULT_VALID, mNetworkAgentInfo.network.netId, null));

ConnectivityService中处理EVENT_NETWORK_TESTED消息,把ping成功的状态保存到NetworkAgentInfo中,并通知NetworkFactory评分值已变,需要重新评估。

if (valid != nai.lastValidated) {
    final int oldScore = nai.getCurrentScore();
    nai.lastValidated = valid;
    nai.everValidated |= valid;
    updateCapabilities(nai, nai.networkCapabilities);
    // If score has changed, rebroadcast to NetworkFactories. b/17726566
    if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
}

这里需要说明一下,ping网络的状态会保存到NetworkAgentInfo中,而后续所有的评分值都会调用NetworkAgentInfo的getCurrentScore()方法来获取,getCurrentScore()方法会根据当前ping网络的状态重新计算评分值:

private int getCurrentScore(boolean pretendValidated) {
if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
        return MAXIMUM_NETWORK_SCORE;
    }
    int score = currentScore;
    if (!networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED) && !pretendValidated) {
        score -= UNVALIDATED_SCORE_PENALTY;
    }
    if (score < 0) score = 0;
    return score;
}

如果是用户指定的联网方式,评分值设置为100,如果ping网络失败,评分值-40,如果ping网络成功,则评分值不变。

二 . 网络评分机制(NetworkFactory)

NetworkFactory的存在意义就是为了帮助ConnectivityService进行评分的管理。一般在NetworkFactory在初始化时,设置固定的评分值,作为评判的标准。
NetworkAgent作为一个代理信息的抽象,在其初始化时,也设置了固定的评分值,不过,这个评分值会根据当前的网络情况的不同而变化,其最后的评分值会和NetworkFactory中的固定评分值进行比较,从而筛选出最优网络。

NetworkFactory和NetworkAgent的评分初始化:

TelephonyNetworkFactory:

private final static int TELEPHONY_NETWORK_SCORE = 50;
setCapabilityFilter(makeNetworkFilter(subscriptionController, phoneId));
setScoreFilter(TELEPHONY_NETWORK_SCmNetworkAgent = new 

DataConnection(NetworkAgent子类):

DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(),
        "DcNetworkAgent", mNetworkInfo, makeNetworkCapabilities(), mLinkProperties,
        50, misc);
ORE);

以上只列出了TelephonyNetwork的评分初始化情况。以下是各个网络类型的评分初始化和变化情况:

NetworkFactory初始化 NetworkAgent初始化 NetworkMonitor中ping网络 disconnect
TelephonyNetwork 50 50 成功:+0 失败:-40 用户指定:+100 0
Wifi 60 60 成功:+0 失败:-40 用户指定:+100 0
EthernetNetwork 69 69 成功:+0 失败:-40 用户指定:+100 0
PhoneSwitcher 101 101 成功:+0 失败:-40 用户指定:+100 0

各种数据业务类型的评分标准,除了其基础评分值不同之外,其他的评判标准都一样。其评分值的变化,主要有以下几种情况:

一.代理信息获取结束后,会参与ping网络的过程,如果ping网络成功,那么NetworkAgent中的评分值不变。如果ping网络失败,那么NetworkAgent中的评分值-40。如果用户指定了某种网络类型作为连接方式,那么NetworkAgent重的评分值+100。

二.如果NetworkAgent和ConnectivityService的AsyncChannel通道断开,需要设置其评分值为0,好让其他的评分高的网络类型连接。

NetworkFactory中的评分标准:

NetworkFactory中维持了基础的评分分值mScore,mScore只有在 NetworkFactory对象创建的时候才会赋值。因网络环境的变化导致需要重新进行网络评估时,使用基础评分分值与传进来的NetworkRequestInfo中的分值进行比较。如果当前的NetworkRequestInfo没有requested过,且当前的分值score比基础分值mScore小,说明当前的NetworkRequestInfo为最优网络,调用needNetworkFor()连接网络。如果当前的NetworkRequestInfo已经requested过,且当前的分值score比基础分值mScore大,说明当前的NetworkRequestInfo已经不是最优网络了,有个更优的网络可用连接,此时应该调用releaseNetworkFor()释放掉此类网络连接。

private void evalRequest(NetworkRequestInfo n) {
    if (VDBG) log("evalRequest request = " + n.request + " with requested = " + n.requested);
    if (n.requested == false && n.score < mScore &&
            n.request.networkCapabilities.satisfiedByNetworkCapabilities(
            mCapabilityFilter) && acceptRequest(n.request, n.score)) {
        if (VDBG) log("  needNetworkFor");
        needNetworkFor(n.request, n.score);
        n.requested = true;
    } else if (n.requested == true &&
            (n.score > mScore || n.request.networkCapabilities.satisfiedByNetworkCapabilities(
            mCapabilityFilter) == false || acceptRequest(n.request, n.score) == false)) {
        if (VDBG) log("  releaseNetworkFor");
        releaseNetworkFor(n.request);
        n.requested = false;
    } else {
        if (VDBG) log("  done");
    }
}

以上是NetworkFactory的评分标准,那么,应该在哪里触发这个评分过程呢?

触发评分的过程:

1.NetworkFactory与ConnectivityService通过AsyncChannel建立连接的时候,初始化评分,并参与了第一次的评分过程。如果此时还没有ping网络的话,其传进来的评分值为基础评分值,以上代码会执行else逻辑。
这里写图片描述

2.调用sendUpdatedScoreToFactories()方法触发了评分过程

在ping网络过程中,会触发多次评分过程。在NetworkMonitor的多个状态中,都有向ConnectivityService发起EVENT_NETWORK_TESTED事件消息更新评分:

                mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
                    NETWORK_TEST_RESULT_VALID, mNetworkAgentInfo.network.netId, null));

ConnectivityService接收到EVENT_NETWORK_TESTED事件消息,更新评分值和触发评分机制:

private boolean maybeHandleNetworkMonitorMessage(Message msg) {
    switch (msg.what) {
        default:
            return false;
        case NetworkMonitor.EVENT_NETWORK_TESTED: {
                if (valid != nai.lastValidated) {
                    final int oldScore = nai.getCurrentScore();
                    nai.lastValidated = valid;
                    nai.everValidated |= valid;
                    updateCapabilities(nai, nai.networkCapabilities);
                    // If score has changed, rebroadcast to NetworkFactories. b/17726566
                    if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
                }
        }
    }
}               

从log中看评分过程:

例子:打开wifi开关连接wifi —> 打开数据开关 —-> 关闭wifi开关

radio.log
Telephony 角度看评分:
这里写图片描述

wifi角度看评分:
这里写图片描述

从wifi的log来看,此次wifi的评分过程没有满足条件,因此都没有执行releaseNetworkFor()或者needNetworkFor()方法。即使wifi执行了needNetworkFor()和releaseNetworkFor()方法,对于wifi也没有影响,因为wifi并没有实现对这两个方法的具体实现。如果存在比wifi优先级更高的数据业务方式,此处应该是要自己实现的吧??

WifiStateMachine.java

private class WifiNetworkFactory extends NetworkFactory {
    
    public WifiNetworkFactory(Looper l, Context c, String TAG, NetworkCapabilities f) {
        super(l, c, TAG, f);
    }

    @Override
    protected void needNetworkFor(NetworkRequest networkRequest, int score) {
        ++mConnectionRequests;
    }

    @Override
    protected void releaseNetworkFor(NetworkRequest networkRequest) {
        --mConnectionRequests;
    }
}

三 . 路由配置信息的获取(NetworkAgent)

路由配置信息的获取方式有多种,wifi,mobile data,Tethering,VPN都可,此处主要研究的是mobile data的路由配置信息的获取,由于篇幅较长,因此另起一篇博客。

四 . 网络物理端口的设置(Netd)

此篇章暂时留空白,待日后补充。可参考:http://www.360doc.com/content/13/0817/17/9171956_307859123.shtml

借用一张图表示Netd层的框架:
这里写图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sjz4860402/article/details/78532856

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法