基于Linphone android sdk开发Android软话机
1.Linphone简介
1.1 简介
LinPhone是一个遵循GPL协议的开源网络电话或者IP语音电话(VOIP)系统,其主要如下。使用linphone,开发者可以在互联网上随意的通信,包括语音、视频、即时文本消息。linphone使用SIP协议,是一个标准的开源网络电话系统,能将linphone与任何基于SIP的VoIP运营商连接起来,包括我们自己开发的免费的基于SIP的Audio/Video服务器。
LinPhone是一款自由软件(或者开源软件),你可以随意的下载和在LinPhone的基础上二次开发。LinPhone是可用于Linux, Windows, MacOSX 桌面电脑以及Android, iPhone, Blackberry移动设备。
学习LinPhone的源码,开源从以下几个部分着手: Java层框架实现的SIP三层协议架构: 传输层,事务层,语法编解码层; linphone动态库C源码实现的SIP功能: 注册,请求,请求超时,邀请会话,挂断电话,邀请视频,收发短信... linphone动态库C源码实现的音视频编解码功能; Android平台上的音视频捕获,播放功能;
1.2 基本使用
如果是Android系统用户,可以从谷歌应用商店安装或者从这个链接下载Linphone 。安装完成后,点击左上角的菜单按钮,选择进入助手界面。在助手界面,可以设定SIP账户或者Linphone账号,如下图:
2.基于linphone android sdk开发linphone
引入sdk依赖
dependencies {
//linphone
debugImplementation "org.linphone:linphone-sdk-android-debug:5.0.0"
releaseImplementation "org.linphone:linphone-sdk-android:5.0.0"
}
为了方便调用,我们需要对Linphone进行简单的封装。首先,按照官方文档的介绍,创建一个CoreManager类,此类是sdk里面的管理类,用来控制来电铃声和启动CoreService,无特殊需求不需调用。需要注意的是,启动来电铃声需要导入media包,否则不会有来电铃声,如下
implementation 'androidx.media:media:1.2.0'
基本代码开发:
package com.matt.linphonelibrary.coreimport android.annotation.SuppressLint
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.telephony.PhoneStateListener
import android.telephony.TelephonyManager
import android.util.Log
import android.view.TextureView
import com.matt.linphonelibrary.R
import com.matt.linphonelibrary.callback.PhoneCallback
import com.matt.linphonelibrary.callback.RegistrationCallback
import com.matt.linphonelibrary.utils.AudioRouteUtils
import com.matt.linphonelibrary.utils.LinphoneUtils
import com.matt.linphonelibrary.utils.VideoZoomHelper
import org.linphone.core.*
import java.io.File
import java.util.*class LinphoneManager private constructor(private val context: Context) {private val TAG = javaClass.simpleNameprivate var core: Coreprivate var corePreferences: CorePreferencesprivate var coreIsStart = falsevar registrationCallback: RegistrationCallback? = nullvar phoneCallback: PhoneCallback? = nullinit {//日志收集Factory.instance().setLogCollectionPath(context.filesDir.absolutePath)Factory.instance().enableLogCollection(LogCollectionState.Enabled)corePreferences = CorePreferences(context)corePreferences.copyAssetsFromPackage()val config = Factory.instance().createConfigWithFactory(corePreferences.configPath,corePreferences.factoryConfigPath)corePreferences.config = configval appName = context.getString(R.string.app_name)Factory.instance().setDebugMode(corePreferences.debugLogs, appName)core = Factory.instance().createCoreWithConfig(config, context)}private var previousCallState = Call.State.Idleprivate val coreListener = object : CoreListenerStub() {override fun onGlobalStateChanged(core: Core, state: GlobalState?, message: String) {if (state === GlobalState.On) {}}//登录状态回调override fun onRegistrationStateChanged(core: Core,cfg: ProxyConfig,state: RegistrationState,message: String) {when (state) {RegistrationState.None -> registrationCallback?.registrationNone()RegistrationState.Progress -> registrationCallback?.registrationProgress()RegistrationState.Ok -> registrationCallback?.registrationOk()RegistrationState.Cleared -> registrationCallback?.registrationCleared()RegistrationState.Failed -> registrationCallback?.registrationFailed()}}//电话状态回调override fun onCallStateChanged(core: Core,call: Call,state: Call.State,message: String) {Log.i(TAG, "[Context] Call state changed [$state]")when (state) {Call.State.IncomingReceived, Call.State.IncomingEarlyMedia -> {if (gsmCallActive) {Log.w(TAG,"[Context] Refusing the call with reason busy because a GSM call is active")call.decline(Reason.Busy)return}phoneCallback?.incomingCall(call)gsmCallActive = true//自动接听if (corePreferences.autoAnswerEnabled) {val autoAnswerDelay = corePreferences.autoAnswerDelayif (autoAnswerDelay == 0) {Log.w(TAG, "[Context] Auto answering call immediately")answerCall(call)} else {Log.i(TAG,"[Context] Scheduling auto answering in $autoAnswerDelay milliseconds")val mainThreadHandler = Handler(Looper.getMainLooper())mainThreadHandler.postDelayed({Log.w(TAG, "[Context] Auto answering call")answerCall(call)}, autoAnswerDelay.toLong())}}}Call.State.OutgoingInit -> {phoneCallback?.outgoingInit(call)gsmCallActive = true}Call.State.OutgoingProgress -> {if (core.callsNb == 1 && corePreferences.routeAudioToBluetoothIfAvailable) {AudioRouteUtils.routeAudioToBluetooth(core, call)}}Call.State.Connected -> phoneCallback?.callConnected(call)Call.State.StreamsRunning -> {// Do not automatically route audio to bluetooth after first callif (core.callsNb == 1) {// Only try to route bluetooth / headphone / headset when the call is in StreamsRunning for the first timeif (previousCallState == Call.State.Connected) {Log.i(TAG,"[Context] First call going into StreamsRunning state for the first time, trying to route audio to headset or bluetooth if available")if (AudioRouteUtils.isHeadsetAudioRouteAvailable(core)) {AudioRouteUtils.routeAudioToHeadset(core, call)} else if (corePreferences.routeAudioToBluetoothIfAvailable && AudioRouteUtils.isBluetoothAudioRouteAvailable(core)) {AudioRouteUtils.routeAudioToBluetooth(core, call)}}}if (corePreferences.routeAudioToSpeakerWhenVideoIsEnabled && call.currentParams.videoEnabled()) {// Do not turn speaker on when video is enabled if headset or bluetooth is usedif (!AudioRouteUtils.isHeadsetAudioRouteAvailable(core) &&!AudioRouteUtils.isBluetoothAudioRouteCurrentlyUsed(core, call)) {Log.i(TAG,"[Context] Video enabled and no wired headset not bluetooth in use, routing audio to speaker")AudioRouteUtils.routeAudioToSpeaker(core, call)}}}Call.State.End, Call.State.Released, Call.State.Error -> {if (core.callsNb == 0) {when (state) {Call.State.End -> phoneCallback?.callEnd(call)Call.State.Released -> phoneCallback?.callReleased(call)Call.State.Error -> {val id = when (call.errorInfo.reason) {Reason.Busy -> R.string.call_error_user_busyReason.IOError -> R.string.call_error_io_errorReason.NotAcceptable -> R.string.call_error_incompatible_media_paramsReason.NotFound -> R.string.call_error_user_not_foundReason.Forbidden -> R.string.call_error_forbiddenelse -> R.string.call_error_unknown}phoneCallback?.error(context.getString(id))}}gsmCallActive = false}}}previousCallState = state}}/*** 启动linphone*/fun start() {if (!coreIsStart) {coreIsStart = trueLog.i(TAG, "[Context] Starting")core.addListener(coreListener)core.start()initLinphone()val telephonyManager =context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManagerLog.i(TAG, "[Context] Registering phone state listener")telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE)}}/*** 停止linphone*/fun stop() {coreIsStart = falseval telephonyManager =context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManagerLog.i(TAG, "[Context] Unregistering phone state listener")telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)core.removeListener(coreListener)core.stop()}/*** 注册到服务器** @param username 账号名* @param password 密码* @param domain IP地址:端口号*/fun createProxyConfig(username: String,password: String,domain: String,type: TransportType? = TransportType.Udp) {core.clearProxyConfig()val accountCreator = core.createAccountCreator(corePreferences.xmlRpcServerUrl)accountCreator.language = Locale.getDefault().languageaccountCreator.reset()accountCreator.username = usernameaccountCreator.password = passwordaccountCreator.domain = domainaccountCreator.displayName = usernameaccountCreator.transport = typeaccountCreator.createProxyConfig()}/*** 取消注册*/fun removeInvalidProxyConfig() {core.clearProxyConfig()}/*** 拨打电话* @param to String* @param isVideoCall Boolean*/fun startCall(to: String, isVideoCall: Boolean) {try {val addressToCall = core.interpretUrl(to)addressToCall?.displayName = toval params = core.createCallParams(null)//启用通话录音
// params?.recordFile = LinphoneUtils.getRecordingFilePathForAddress(context, addressToCall!!)//启动低宽带模式if (LinphoneUtils.checkIfNetworkHasLowBandwidth(context)) {Log.w(TAG, "[Context] Enabling low bandwidth mode!")params?.enableLowBandwidth(true)}if (isVideoCall) {params?.enableVideo(true)core.enableVideoCapture(true)core.enableVideoDisplay(true)} else {params?.enableVideo(false)}if (params != null) {core.inviteAddressWithParams(addressToCall!!, params)} else {core.inviteAddress(addressToCall!!)}} catch (e: Exception) {e.printStackTrace()}}/*** 接听来电**/fun answerCall(call: Call) {Log.i(TAG, "[Context] Answering call $call")val params = core.createCallParams(call)//启用通话录音
// params?.recordFile = LinphoneUtils.getRecordingFilePathForAddress(context, call.remoteAddress)if (LinphoneUtils.checkIfNetworkHasLowBandwidth(context)) {Log.w(TAG, "[Context] Enabling low bandwidth mode!")params?.enableLowBandwidth(true)}params?.enableVideo(isVideoCall(call))call.acceptWithParams(params)}/*** 谢绝电话* @param call Call*/fun declineCall(call: Call) {val voiceMailUri = corePreferences.voiceMailUriif (voiceMailUri != null && corePreferences.redirectDeclinedCallToVoiceMail) {val voiceMailAddress = core.interpretUrl(voiceMailUri)if (voiceMailAddress != null) {Log.i(TAG, "[Context] Redirecting call $call to voice mail URI: $voiceMailUri")call.redirectTo(voiceMailAddress)}} else {Log.i(TAG, "[Context] Declining call $call")call.decline(Reason.Declined)}}/*** 挂断电话*/fun terminateCall(call: Call) {Log.i(TAG, "[Context] Terminating call $call")call.terminate()}fun micEnabled() = core.micEnabled()fun speakerEnabled() = core.outputAudioDevice?.type == AudioDevice.Type.Speaker/*** 启动麦克风* @param micEnabled Boolean*/fun enableMic(micEnabled: Boolean) {core.enableMic(micEnabled)}/*** 扬声器或听筒* @param SpeakerEnabled Boolean*/fun enableSpeaker(SpeakerEnabled: Boolean) {if (SpeakerEnabled) {AudioRouteUtils.routeAudioToEarpiece(core)} else {AudioRouteUtils.routeAudioToSpeaker(core)}}/*** 是否是视频电话* @return Boolean*/fun isVideoCall(call: Call): Boolean {val remoteParams = call.remoteParamsreturn remoteParams != null && remoteParams.videoEnabled()}/*** 设置视频界面* @param videoRendering TextureView 对方界面* @param videoPreview CaptureTextureView 自己界面*/fun setVideoWindowId(videoRendering: TextureView, videoPreview: TextureView) {core.nativeVideoWindowId = videoRenderingcore.nativePreviewWindowId = videoPreview}/*** 设置视频电话可缩放* @param context Context* @param videoRendering TextureView*/fun setVideoZoom(context: Context, videoRendering: TextureView) {VideoZoomHelper(context, videoRendering, core)}fun switchCamera() {val currentDevice = core.videoDeviceLog.i(TAG, "[Context] Current camera device is $currentDevice")for (camera in core.videoDevicesList) {if (camera != currentDevice && camera != "StaticImage: Static picture") {Log.i(TAG, "[Context] New camera device will be $camera")core.videoDevice = camerabreak}}// val conference = core.conference
// if (conference == null || !conference.isIn) {
// val call = core.currentCall
// if (call == null) {
// Log.w(TAG, "[Context] Switching camera while not in call")
// return
// }
// call.update(null)
// }}//初始化一些操作private fun initLinphone() {configureCore()initUserCertificates()}private fun configureCore() {// 来电铃声core.isNativeRingingEnabled = false// 来电振动core.isVibrationOnIncomingCallEnabled = truecore.enableEchoCancellation(true) //回声消除core.enableAdaptiveRateControl(true) //自适应码率控制}private var gsmCallActive = falseprivate val phoneStateListener = object : PhoneStateListener() {override fun onCallStateChanged(state: Int, phoneNumber: String?) {gsmCallActive = when (state) {TelephonyManager.CALL_STATE_OFFHOOK -> {Log.i(TAG, "[Context] Phone state is off hook")true}TelephonyManager.CALL_STATE_RINGING -> {Log.i(TAG, "[Context] Phone state is ringing")true}TelephonyManager.CALL_STATE_IDLE -> {Log.i(TAG, "[Context] Phone state is idle")false}else -> {Log.i(TAG, "[Context] Phone state is unexpected: $state")false}}}}//设置存放用户x509证书的目录路径private fun initUserCertificates() {val userCertsPath = corePreferences!!.userCertificatesPathval f = File(userCertsPath)if (!f.exists()) {if (!f.mkdir()) {Log.e(TAG, "[Context] $userCertsPath can't be created.")}}core.userCertificatesPath = userCertsPath}companion object {// For Singleton instantiation@SuppressLint("StaticFieldLeak")@Volatileprivate var instance: LinphoneManager? = nullfun getInstance(context: Context) =instance ?: synchronized(this) {instance ?: LinphoneManager(context).also { instance = it }}}}
相关文章:
基于Linphone android sdk开发Android软话机
1.Linphone简介 1.1 简介 LinPhone是一个遵循GPL协议的开源网络电话或者IP语音电话(VOIP)系统,其主要如下。使用linphone,开发者可以在互联网上随意的通信,包括语音、视频、即时文本消息。linphone使用SIP协议&#…...
[论文分享]TimeDRL:多元时间序列的解纠缠表示学习
论文题目:TimeDRL: Disentangled Representation Learning for Multivariate Time-Series 论文地址:https://arxiv.org/abs/2312.04142 代码地址:暂无 关键要点:多元时间序列,自监督表征学习,分类和预测 摘…...
分享一个好看的vs主题
最近发现了一个很好看的vs主题(个人认为挺好看的),想要分享给大家。 主题的名字叫NightOwl,和vscode的主题颜色挺像的。操作方法也十分简单,首先我们先在最上面哪一行找到扩展。 然后点击管理扩展,再搜索栏…...
什么是云呼叫中心?
云呼叫中心作为一种高效的企业呼叫管理方案,越来越受到企业的青睐,常被用于管理客服和销售业务。那么,云呼叫中心到底是什么? 什么是云呼叫中心? 云呼叫中心是一种基于互联网的呼叫管理系统,与传统的呼叫…...
还在用nvm?来试试更快的node版本管理工具——fnm
前言 📫 大家好,我是南木元元,热衷分享有趣实用的文章,希望大家多多支持,一起进步! 🍅 个人主页:南木元元 目录 什么是node版本管理 常见的node版本管理工具 fnm是什么 安装fnm …...
【Hadoop精讲】HDFS详解
目录 理论知识点 角色功能 元数据持久化 安全模式 SecondaryNameNode(SNN) 副本放置策略 HDFS写流程 HDFS读流程 HA高可用 CPA原则 Paxos算法 HA解决方案 HDFS-Fedration解决方案(联邦机制) 理论知识点 角色功能 元数据持久化 另一台机器就…...
企业需要哪些数字化管理系统?
企业需要哪些数字化管理系统? ✅企业引进管理系统肯定是为了帮助整合和管理大量的数据,从而优化业务流程,提高工作效率和生产力。 ❌但是,如果各个系统之间不互通、无法互相关联数据的话,反而会增加工作量和时间成本…...
【vue】开发常见问题及解决方案
有一些问题不限于 Vue,还适应于其他类型的 SPA 项目。 1. 页面权限控制和登陆验证页面权限控制 页面权限控制是什么意思呢? 就是一个网站有不同的角色,比如管理员和普通用户,要求不同的角色能访问的页面是不一样的。如果一个页…...
飞天使-k8s知识点3-卸载yum 安装的k8s
要彻底卸载使用yum安装的 Kubernetes 集群,您可以按照以下步骤进行操作: 停止 Kubernetes 服务: sudo systemctl stop kubelet sudo systemctl stop docker 卸载 Kubernetes 组件: sudo yum remove -y kubelet kubeadm kubectl…...
ZooKeeper 集群搭建
文章目录 ZooKeeper 概述选举机制搭建前准备分布式配置分布式安装解压缩并重命名配置环境配置服务器编号配置文件 操作集群编写脚本运行脚本搭建过程中常见错误 ZooKeeper 概述 Zookeeper 是一个开源的分布式服务协调框架,由Apache软件基金会开发和维护。以下是对Z…...
Meson:现代的构建系统
Meson是一款现代化、高性能的开源构建系统,旨在提供简单、快速和可读性强的构建脚本。Meson被设计为跨平台的,支持多种编程语言,包括C、C、Fortran、Python等。其目标是替代传统的构建工具,如Autotools和CMake,提供更简…...
【大模型AIGC系列课程 5-2】视觉-语言大模型原理
重磅推荐专栏: 《大模型AIGC》;《课程大纲》 本专栏致力于探索和讨论当今最前沿的技术趋势和应用领域,包括但不限于ChatGPT和Stable Diffusion等。我们将深入研究大型模型的开发和应用,以及与之相关的人工智能生成内容(AIGC)技术。通过深入的技术解析和实践经验分享,旨在…...
震惊!难怪别人家的孩子越来越聪明,原来竟是因为它
前段时间工作调动给孩子换了个新学校,刚开始担心她不能适应新学校的授课方式,但任课老师对她评价很高,夸她上课很专注。 为了训练孩子的专注力,作为家长可没少下功夫,画画,下五子棋等益智游戏的兴趣班没少…...
Linux操作系统(UMASK+SUID+SGID+STICK)
UMASK反掩码 如何查看反掩码:直接在终端窗口运行 umask root用户反掩码:0022 普通用户反掩码:0002 UMASK的作用:确定目录,文件的缺省权限值 以root身份创建目录,观察目录的9位权限值 以root身份创建普通文件…...
Java 中单例模式的常见实现方式
目录 一、什么是单例模式? 二、单例模式有什么作用? 三、常见的创建单例模式的方式 1、饿汉式创建 2、懒汉式创建 3、DCL(Double Checked Lock)双检锁方式创建 3.1、synchronized 同步锁的基本使用 3.2、使用 DCL 中存在的疑…...
【C语言】自定义类型之联合和枚举
目录 1. 前言2. 联合体2.1 联合体类型的声明2.2 联合体的特点2.3 相同成员的结构体和联合体对比2.4 联合体大小的计算2.4 判断当前机器的大小端 3. 枚举3.1 枚举类型的声明3.2 枚举类型的优点3.3 枚举类型的使用 1. 前言 在之前的博客中介绍了自定义类型中的结构体,…...
使用Mosquitto/python3进行MQTT连接
一、简介 MQTT(消息队列遥测传输)是ISO 标准(ISO/IEC PRF 20922)下基于发布/订阅范式的消息协议。它工作在 TCP/IP协议族上,是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型消息协议,为此,它需要一个消息中间件。 …...
JavaWeb笔记之前端开发HTML
一、引言 1.1HTML概念 网页,是网站中的一个页面,通常是网页是构成网站的基本元素,是承载各种网站应用的平台。通俗的说,网站就是由网页组成的。通常我们看到的网页都是以htm或html后缀结尾的文件,俗称 HTML文件。 …...
通过IP地址定位解决被薅羊毛问题
随着互联网的普及,线上交易和优惠活动日益增多,这也为一些不法分子提供了可乘之机。他们利用技术手段,通过大量注册账号或使用虚假IP地址进行异常操作,以获取更多的优惠或利益,这种行为被称为“薅羊毛”。对于企业和平…...
Leetcode 122 买卖股票的最佳时机 II
题意理解: 已知:一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格 如何哪个时间点买入,哪个时间点卖出,多次交易,能够收益最大化 目的:收益最大化 解题思路: 使用贪心…...
音频文件合成
音频文件合成 音频文件合成 http://ffmpeg.org/download.html https://blog.csdn.net/u013314786/article/details/89682800 http://www.360doc.com/content/19/0317/01/10519289_822112563.shtml https://chaijunkun.blog.csdn.net/article/details/116491526?spm1001.210…...
20231220将NanoPC-T4(RK3399)开发板的Android10的SDK按照Rockchip官方挖掘机开发板编译打包刷机之后启动跑飞
20231220将NanoPC-T4(RK3399)开发板的Android10的SDK按照Rockchip官方挖掘机开发板编译打包刷机之后启动跑飞 2023/12/20 17:19 简略步骤:rootrootrootroot-X99-Turbo:~/3TB$ tar --use-compress-programpigz -xvpf rk3399-android-10.git-20210201.tgz rootrootro…...
vivo 容器平台资源运营实践
作者:vivo 互联网服务器团队 - Chen Han 容器平台针对业务资源申请值偏大的运营问题,通过静态超卖和动态超卖两种技术方案,使业务资源申请值趋于合理化,提高平台资源装箱率和资源利用率。 一、背景 在Kubernetes中,容…...
ASP.NET Core面试题之Redis高频问题
🎈🎈在.NET后端开发岗位中,如今也少不了、微服务、分布式、高并发高可用相关的面试题🎈🎈 👍👍本文分享一些整理的Redis高频面试题🎉 👍👍机会都是给有准备…...
【教程】Ubuntu基本软件安装
文章目录 一、搜狗输入法安装二、百度网盘安装三、划词翻译 一、搜狗输入法安装 全网最准确的Ubuntu 20.04 安装搜狗输入法的步骤 二、百度网盘安装 百度云盘for Linux安装教程和体验 三、划词翻译 ubuntu最好用的划词翻译词典:有道词典和GoldenDict...
Jenkins 构建环境指南
目录 Delete workspace before build starts(常用) Use secret text(s) or file(s) (常用) Add timestamps to the Console Output (常用) Inspect build log for published build scans Terminate a …...
基于Go语言的HTTP路由设计与实现
在Go语言的世界里,HTTP路由是一种将HTTP请求映射到相应处理函数的技术。通过路由,我们可以确定当用户发送一个HTTP请求时,应该调用哪个函数来处理该请求。在这个过程中,我们可以使用多种方法来实现路由设计,下面我将以…...
SpringMVC01
SpringMVC 1. 学习⽬标2. 什么叫MVC?3. SpringMVC 框架概念与特点4. SpringMVC 请求流程5. Spring MVC 环境搭建6. URL 地址映射配置7. 参数绑定8. JSON 数据开发JSON普通数组步骤1:pom.xml添加依赖步骤2: 修改配置⽂件步骤3. 注解使⽤ 1. 学习⽬标 2. 什…...
基于Redis限流(aop切面+redis实现“令牌桶算法”)
令牌桶算法属于流量控制算法,在一定时间内保证一个键(key)的访问量不超过某个阈值。这里的关键是设置一个令牌桶,在某个时间段内生成一定数量的令牌,然后每次访问时从桶中获取令牌,如果桶中没有令牌&#x…...
【小白专用】php pdo方式连接sqlserver 设置方法 更新23.12.21
windows系统的拓展相对来说比较好安装,直接下载对应的dll文件,修改php.ini配置文件即可。 添加PHP对SQL SERVER的支持 1.新建PHP 文件,输入内容: <?php echo phpinfo(); ?> 2.运行后,可以查看到如下数据&…...
专门做装修的网站/最近的电脑培训学校
给定post请求包体(是json的字符串) {"A" : 10086,"B" : "请求信息"} 发送HTTP过程 1. 将post请求包体(是个json格式的字符串),Marshal成json格式 2. 发送HTTP请求 3. 接收HTTP返回…...
做网站客户最关心哪些问题/搜索引擎成功案例分析
704. 二分查找 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。 class Solution { public:int search(vector<int>&…...
建设银行办信用卡网站/网络推广公司专业网络
发这篇心得主要想告诉大家博主的博客虽然是随心情发的,但博主真的没有偷懒,一直在学习!而且对现在的博主来说专业技能固然重要,但在未来规划还没定之前,广度还是更重要的!对的,我要摊牌…...
wordpress图片pin按钮/赵阳竞价培训
网络安全的定义: 网络安全(Cyber Security)是指网络系统的硬件、软件及其系统中的数据受到保护,不因偶然的或者恶意的原因而遭受到破坏、更改、泄露,系统连续可靠正常地运行,[网络服务](https://baike.bai…...
php做电商网站的难点/百度推广关键词越多越好吗
熊猫帮帮主cnblogs 2018/1/25 问题描述:在Windows下将中文文件名的文件打成压缩包,在Linux下解压出现文件名乱码。 问题原因:Windows和Linux下采用不同中文编码格式,导致在Linux下解压时出现文件名乱码。 解决方案:在命…...
黑龙江省垦区建设协会网站/武汉seo托管公司
西雅图IT圈:seattleit【今日作者】宇直宇宙第一直男近日有网友发现疑似刘强东的证件照被强行植入进了某印度电影的预告片里疑似造成了侵权这部电影播出了预告片片中大概40秒处有一幕镜头是:疑似正在进行案件分析,墙面上出现了许多照片&#x…...