Jetpack Compose开发一个Android WiFi导航应用
在以前的一篇文章构建一个WIFI室内定位系统_wifi定位系统-CSDN博客中,我介绍了如何用Android来测量WiFi信号,上传到服务器进行分析后,生成室内不同地方的WiFi指纹,从而帮助进行室内导航。当时我是用的HTML5+的技术来快速开发一个Android的应用,可以看到HTML5+能很便利的用我们熟悉的Web技术来进行开发,而不需要了解原生Android应用繁琐的开发知识。但是Android原生应用也有其优势,尤其在性能上以及一些Android核心功能的调用上。尤其是Google推出了新的Jetpack Compose用于构建原生 Android 界面的新工具包,它使用更少的代码、强大的工具和直观的 Kotlin API,可以帮助简化并加快 Android 界面开发,打造生动而精彩的应用,让我们能更快速、更轻松地构建 Android 界面以及更加便利进行原生应用的开发。因此这次我也用Jetpack Compose来重构了我之前写的Wifi信号测量的应用。
WiFi测量主界面
界面UI设计
Jepack Compose的精髓在于用可组合函数来声明一个UI界面。界面是不可变的,在绘制后无法进行更新。您可以控制的是界面的状态。每当界面的状态发生变化时,Compose 都会重新创建界面数更新的部分。
在Android studio里面新建一个项目,选择Empty activity类型,在这种类型的项目,res资源文件夹没有layout这个子文件夹,因为这种类型已经是用新的Compose方式来进行布局了,不再采用以前的XML方式来定义布局。
新建一个名为WifiMeasure的class,在里面定义一个MeasureScreen的Composable函数,用来声明我们的主界面,代码如下:
@Composable
fun MeasureScreen() {Column(modifier = Modifier.padding(all = 8.dp)) {Text(text = stringResource(R.string.screen_title),style = MaterialTheme.typography.titleLarge,)Spacer(modifier = Modifier.height(8.dp))val imageModifier = Modifier.height(150.dp).fillMaxWidth().border(BorderStroke(1.dp, Color.Black))Image(painter = painterResource(id = R.drawable.indooratlas),contentDescription = null,contentScale = ContentScale.FillWidth,modifier = imageModifier)Spacer(modifier = Modifier.height(8.dp))OutlinedTextField(value = "",onValueChange = { },label = { Text(text = stringResource(R.string.label_position_name), style = MaterialTheme.typography.bodyMedium)},modifier = Modifier.fillMaxWidth())Spacer(modifier = Modifier.height(8.dp))TextField(value = "0.0",onValueChange = { },label = { Text(text = stringResource(R.string.label_current_angle), style = MaterialTheme.typography.bodyMedium)},readOnly = true,modifier = Modifier.fillMaxWidth())Spacer(modifier = Modifier.height(16.dp))TextButton(onClick = { },shape = RectangleShape,contentPadding = PaddingValues(16.dp),modifier = Modifier.fillMaxWidth().border(1.5.dp, MaterialTheme.colorScheme.secondary, CircleShape)) {Texttext = "Measure",style = MaterialTheme.typography.bodyMedium)}}
}
这里采用了Column来作为一个垂直布局,在里面放置了Text, Image等组件来显示界面。我们可以添加一个Preview的函数,这样在进行代码改动的时候,我们就可以马上在Android studio的Design里面看到UI的改动了,非常方便,代码如下:
@Preview(showBackground = true)
@Composable
fun PreviewMeasureScreen() {MeasureScreen()
}
这个界面的效果如下图:
在要进行测量的时候,我们需要首先输入当前位置的名字,同时手机会实时显示当前的朝向,因为不同的朝向对Wifi信号的测量也有影响。然后当我们点击Measure这个按钮的时候,就会把当前这个位置的Wifi信号信息测量出来。
在MainActivity的onCreate方法的setContent中直接调用刚才我们定义的函数MeasureScreen()即可在APP中显示我们的界面。
定义ViewModel保存UI状态
现在在输入框中输入位置的名字,可以看到输入无法显示,这是因为在OutlinedTextField里面我们没有定义value是一个可观测状态,因此Compose组件无法进行重组更新。为此我们需要定义一个ViewModel来保存UI的状态。新建一个WifiMeasureViewModel的class,代码如下:
class WifiMeasureViewModel : ViewModel() {var positionName by mutableStateOf("")private setfun updatePositionName(name: String) {positionName = name}
这个类里面定义了一个positionName的mutableStateOf的State容器,通过一个update方法来更新数值。
修改WifiMeasure这个函数,传入这个ViewModel进行绑定,这里的viewModel()是一个生命周期的组件,可以使得ViewModel与Compose UI生命周期同步存在。
@Composable
fun MeasureScreen(measureViewModel: WifiMeasureViewModel = viewModel()
)
修改OutlinedTextField,现在可以正常输入文字了,如果我们旋转手机,可以看到之前输入的文字能保留下来。
OutlinedTextField(value = measureViewModel.positionName,onValueChange = { measureViewModel.updatePositionName(it) },label = { Text(text = stringResource(R.string.label_position_name), style = MaterialTheme.typography.bodyMedium)},modifier = Modifier.fillMaxWidth())
获取手机朝向
因为手机的朝向对于Wifi测量会有影响,因此通常我们会在同一个地点测试不同朝向的WiFi信号并记录下来。我们需要在APP上实时显示当前的朝向,这就需要用到手机提供的传感器数据。
传统的Android应用的方法是,在Activity类里面继承SensorEventListener并重写相应的方法来实现。但是在Composable function里面如何实现,在官网上并没有介绍。我的做法是先定义一个新的类继承SensorEventListener,例如我们新建一个SensorDataManager的类,代码如下:
class SensorDataManager(context: Context): SensorEventListener {private val sensorManager by lazy {context.getSystemService(Context.SENSOR_SERVICE) as SensorManager}private var accelerometerReading = FloatArray(3)private var magnetometerReading = FloatArray(3)private var rotationMatrix = FloatArray(9)private var orientationAngles = FloatArray(3)val data: Channel<Float> = Channel(Channel.UNLIMITED)fun init() {Log.d("SensorDataManager", "init")val accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)val magnetometer = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI)sensorManager.registerListener(this, magnetometer, SensorManager.SENSOR_DELAY_UI)}override fun onSensorChanged(event: SensorEvent) {if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) {System.arraycopy(event.values, 0, accelerometerReading, 0, accelerometerReading.size)} else if (event.sensor.type == Sensor.TYPE_MAGNETIC_FIELD) {System.arraycopy(event.values, 0, magnetometerReading, 0, magnetometerReading.size)}SensorManager.getRotationMatrix(rotationMatrix,null,accelerometerReading,magnetometerReading)SensorManager.getOrientation(rotationMatrix, orientationAngles)data.trySend(orientationAngles[0])}override fun onAccuracyChanged(p0: Sensor?, p1: Int) {}fun cancel() {Log.d("SensorDataManager", "cancel")sensorManager.unregisterListener(this)}
}
解释一下代码,在init函数中获取accelerometer和maganetic这两个传感器并注册listener,根据官网的介绍,推荐用这两个传感器数据来获取准确的朝向。在重写的onSensorChanged方法中,根据这两个传感器的数据计算朝向,并通过Channel把协程的数据发送出去。最后在cancel中取消listener注册。
现在修改一下MeasureScreen这个函数,增加以下代码:
val context = LocalContext.currentval scope = rememberCoroutineScope()var angle by remember { mutableStateOf<Float>(0f) }DisposableEffect(Unit) {val dataManager = SensorDataManager(context)dataManager.init()val job = scope.launch {dataManager.data.receiveAsFlow().onEach { angle = it }.collect()}onDispose {dataManager.cancel()job.cancel()}}
这里用到了Compose里面的附带效应Side effects,按照官网的解释附带效应是指发生在可组合函数作用域之外的应用状态的变化。DisposableEffect可以在键发生变化或可组合项退出组合后进行清理。因此我采用DiposableEffect(Unit)来监控这个MeasureScreen函数,完成初始化和清除Sensor Listener的工作。
修改一下显示朝向角度的TextField,设置其Value
TextField(value = angle.toString(),onValueChange = { },label = { Text(text = stringResource(R.string.label_current_angle), style = MaterialTheme.typography.bodyMedium)},readOnly = true,modifier = Modifier.fillMaxWidth())
现在这个测量页面可以正常工作了。
WiFi测量报告页面
增加导航
当点击测量页面的Measure按钮的时候,应该能跳转到另一个页面,显示WiFi的测量结果。要实现导航的功能,我们需要用到Navigation组件。新增一个名为Navigation的class,代码如下:
object Destinations {const val MEASURE_ROUTE = "measure"const val REPORT_ROUTE = "report/{positionName}"
}@Composable
fun AppNavHost(modifier: Modifier = Modifier,navController: NavHostController = rememberNavController(),startDestination: String = MEASURE_ROUTE
) {NavHost(navController = navController,startDestination = startDestination) {composable(MEASURE_ROUTE) {MeasureScreen(navController = navController)}composable(REPORT_ROUTE,arguments = listOf(navArgument("positionName") {type = NavType.StringType},)) { backStackEntry ->val positionName = backStackEntry.arguments?.getString("positionName")WifiMeasureReport(positionName)}}
}
这里定义了两个route,分别对应APP的两个页面。在跳转到测量报告页面的时候,route会带上positionName这个参数。
修改MainActivity,把setContent的内容替换为调用AppNavHost(),如以下代码:
class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {WifiPositionTheme {// A surface container using the 'background' color from the themeSurface(modifier = Modifier.fillMaxSize(),color = MaterialTheme.colorScheme.background) {AppNavHost()}}}}
}
WiFi扫描服务
增加一个Wifi扫描的服务,实现对Wifi信号的测量。新增一个WifiScanService的class,代码如下:
class WifiScanService(context: Context) {private val wifiManager by lazy {context.getSystemService(Context.WIFI_SERVICE) as WifiManager}private val context: Context = contextval data: Channel<List<WifiMeasureData>> = Channel(Channel.UNLIMITED)private val wifiScanReceiver = object : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent) {val success = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)if (success) {scanSuccess()} else {scanFailure()}}}fun init() {val intentFilter = IntentFilter()intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)context.registerReceiver(wifiScanReceiver, intentFilter)val success = wifiManager.startScan()if (!success) {scanFailure()}}private fun scanFailure() {Log.d("WIFI", "Scan failure")}@SuppressLint("MissingPermission")private fun scanSuccess() {val results = wifiManager.scanResultsif (!results.isNullOrEmpty()) {val wifiMeasureData = results.map {WifiMeasureData(it.BSSID,it.level)}data.trySend(wifiMeasureData)}}fun cancel() {context.unregisterReceiver(wifiScanReceiver)}
}
定义一个WifiMeasureData的数据class,保存测量数据
data class WifiMeasureData (val bssId: String,val signalStrength: Int
)
另外,要开启WiFi测量,还需要申请相应的权限,在AndroidManifest.xml里面,增加以下权限申请
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
报告页面设计
同样是采用Compose的方式来设计一个页面展示Wifi测量数据的结果,新建一个WifiMeasureReport的class,代码如下:
@Composable
fun WifiMeasureReport (positionName: String?) {val context = LocalContext.currentval scope = rememberCoroutineScope()var wifiScanResult by remember { mutableStateOf<List<WifiMeasureData>>(listOf(WifiMeasureData("", 0))) }DisposableEffect(Unit) {val wifiScanService = WifiScanService(context)wifiScanService.init()val job = scope.launch {wifiScanService.data.receiveAsFlow().onEach { wifiScanResult = it }.collect()}onDispose {wifiScanService.cancel()job.cancel()}}Column() {Text(text = stringResource(id = R.string.report_title),style = MaterialTheme.typography.titleLarge,textAlign = TextAlign.Center,modifier = Modifier.fillMaxWidth())Spacer(modifier = Modifier.height(8.dp))Text(text = stringResource(id = R.string.report_position_name)+": "+positionName,style = MaterialTheme.typography.bodyLarge,modifier = Modifier.padding(10.dp))LazyColumn(Modifier.fillMaxWidth(),contentPadding = PaddingValues(horizontal = 4.dp)){item {ItemHeader()}itemsIndexed(wifiScanResult) { index: Int, item: WifiMeasureData ->ItemRow(index, item)}}}
}@Composable
fun ItemHeader() {Row(Modifier.fillMaxWidth()//.border(BorderStroke(0.5.dp, Color.Black))) {Text(text = stringResource(R.string.report_header_bssid), fontWeight = FontWeight.Bold, modifier = Modifier.weight(5f).padding(10.dp))Text(text = stringResource(R.string.report_header_strength), fontWeight = FontWeight.Bold, modifier = Modifier.weight(5f).padding(10.dp))}Divider(color = Color.LightGray,modifier = Modifier.height(1.dp).fillMaxHeight().fillMaxWidth())
}@Composable
fun ItemRow(index: Int, item: WifiMeasureData) {val modifier: Modifier = Modifier.fillMaxWidth()Row(modifier = if (index%2 == 0) modifier.background(Color.LightGray) else modifier) {Text(text = item.bssId, modifier = Modifier.weight(5f).padding(10.dp))Text(text = item.signalStrength.toString(), modifier = Modifier.weight(5f).padding(10.dp))}Divider(color = Color.LightGray,modifier = Modifier.height(1.dp).fillMaxHeight().fillMaxWidth())
}@Preview(showBackground = true)
@Composable
fun PreviewMeasureReportScreen() {WifiMeasureReport("grid_1")
}
这里同样采用了Column垂直布局,其中用了一个LazyColumn来展示Wifi测量结果的列表。这个LazyColumn类似于以前的RecyclerView。
测试结果上报
WiFi测试的结果要上报到服务器来进行汇总分析,最后生成Wifi指纹,这部分的内容可以参考我之前提到的博客内容,这里不再重复。我们只需要修改一下WifiMeasureReport,把拿到的结果通过REST API上传即可。改动如下:
待补充。。。
运行效果
最后把项目打包为APK后上传到手机运行,实际效果如下:
待补充。。。
相关文章:
Jetpack Compose开发一个Android WiFi导航应用
在以前的一篇文章构建一个WIFI室内定位系统_wifi定位系统-CSDN博客中,我介绍了如何用Android来测量WiFi信号,上传到服务器进行分析后,生成室内不同地方的WiFi指纹,从而帮助进行室内导航。当时我是用的HTML5的技术来快速开发一个An…...
【Mode Management】ComM详细介绍
目录 1. Introduction and functional overview 2.Dependencies to other modules 3.Functional specification 3.1 Partial Network Cluster Management 3.2 ComM channel state machine 3.2.1 Behaviour in state COMM_NO_COMMUNICATION 3.2.1.1 COMM_NO_COM_NO_PENDI…...
【C++多线程编程】(二)之详解锁(lock)和解锁(unlock)
在C多线程编程中,锁(lock)和解锁(unlock)通常用于管理共享资源的访问,以防止多个线程同时对资源进行修改,从而避免竞态条件(Race Condition)和数据不一致性问题。C标准库…...
【Mypy】超级实用的python高级库!
今天,我很兴奋地向大家介绍一个神奇的Python库:Mypy。这个库是Python世界中的一颗璀璨明星,提供了静态类型检查的强大功能,极大地增强了Python这门动态类型语言的健壮性和可维护性。我们将深入探索Mypy的多个方面,并通…...
【Python基础】循环语句
文章目录 [toc]什么是循环Python中的循环方式while循环格式示例 什么是循环 程序中需要重复执行的代码,可以通过循环实现比如和女朋友道歉,或一万遍“宝宝,我错了”,在没有学习循环之前,我们只能通过如下方式实现 pr…...
【面试】广告优化
a1:点击率公式是什么?点击率低的原因是什么? 点击率点击/曝光,点击率低的原因主要有两点:一是创意不吸引人;二是目标受众不准确/定向过宽不精确,广告曝光给了对产品不感兴趣用户 a2:…...
RabbitMQ插件详解:rabbitmq_message_timestamp【Rabbitmq 五】
欢迎来到我的博客,代码的世界里,每一行都是一个故事 RabbitMQ时空之旅:rabbitmq_message_timestamp的奇妙世界 前言什么是rabbitmq_message_timestamprabbitmq_message_timestamp 的定义与作用:如何在 RabbitMQ 中启用消息时间戳&…...
AD9361 Evaluation Software配置脚本转换工具
最近在玩一个开源的AD9361项目,AD9361采用纯逻辑配置,不需要ARM或者MicroBlaze。其中,先是用AD9361 Evaluation Software生成配置脚本,再转换成ad9361_lut.v。 在网上查了一圈,有个转换工具叫bit_converter࿰…...
Centos7 配置Git
随笔记录 目录 1, 新建用户 2. 给用户设置密码相关操作 3. 为新用户添加sudo 权限 4. 配置Git 4.1 配置Git 4.2 查看id_ras.pub 5, 登录Git 配置SSH 秘钥 6. Centos7 登录Git 7. clone 指定branch到本地 8. 将新代码复制到指定路径 9. 上传指定代码 …...
python工具方法 44 数据仿真生成(粘贴目标切片到背景图像上,数据标签校验)
在深度学习训练中数据是一个很重要的因素,在数据不够时需要我们基于现有的数据进行增强生成新的数据。此外,在某特殊情况,如对某些目标切片数据(例如:石块分割切片)预测效果较差,需要增强其在训练数据中的频率。故此,我们可以将先有数据标注中的目标裁剪出来,作为样本…...
Llama 架构分析
从代码角度进行Llama 架构分析 Llama 架构分析前言Llama 架构分析分词网络主干DecoderLayerAttentionMLP 下游任务因果推理文本分类 Llama 架构分析 前言 Meta 开发并公开发布了 Llama系列大型语言模型 (LLM),这是一组经过预训练和微调的生成文本模型,参…...
vue3前端 md5工具类
工具类 /*** Namespace for hashing and other cryptographic functions* Copyright (c) Andrew Valums* Licensed under the MIT license, http://valums.com/mit-license/*/var V V || {}; V.Security V.Security || {};(function () {// for faster accessvar S V.Secur…...
Unity触摸 射线穿透UI解决
unity API 之EventSystem.current.IsPointerOverGameObject() 命名空间 :UnityEngine.EventSystems 官方描述: public bool IsPointerOverGameObject(); public bool IsPointerOverGameObject(int pointerId); //触摸屏时需要的参数ÿ…...
基于QTreeWidget实现带Checkbox的多级组织结构选择树
基于QTreeWidget实现带Checkbox的多级组织结构选择树 采用基于QWidgetMingw实现的原生的组织结构树 通过QTreeWidget控件实现的带Checkbox多级组织结构树。 Qt相关系列文章: 一、Qt实现的聊天画面消息气泡 二、基于QTreeWidget实现多级组织结构 三、基于QTreeWidget…...
探索 Vim:一个强大的文本编辑器
引言: Vim(Vi IMproved)是一款备受推崇的文本编辑器,拥有强大的功能和高度可定制性,提供丰富的编辑和编程体验。本文将探讨 Vim 的基本概念、使用技巧以及为用户带来的独特优势。 简介和发展 1. Vim 的简介和历史 V…...
K8S(十)—容器探针
这里写目录标题 容器探针(probe)检查机制探测结果探测类型何时该使用存活态探针?何时该使用就绪态探针?何时该使用启动探针? 使用exechttptcpgrpc使用命名端口 使用启动探针保护慢启动容器定义就绪探针配置探针HTTP 探测TCP 探测探针层面的…...
[C错题本]
1.int,short,long都是signed的 但是char可能是signed 也可能是unsigned的——《C Primer》 2.在16位的PC中 char类型占1个字节 int占2个字节 long int占4个字节 float占四个字节 double占八个字节 3.自增运算符和自减运算符即使是在判断条件中使用也会实际生效 int i 1; int…...
tomcat启动异常:子容器启动失败(a child container failed during start)
最近在使用eclipse启动Tomcat时,发现一个问题,启动以前的项目突然报子容器启动异常。 异常信息如下: 严重: 子容器启动失败 java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: 无法启动组件[org.apache.…...
JAVA序列化(创建可复用的 Java 对象)
JAVA 序列化(创建可复用的 Java 对象) 保存(持久化)对象及其状态到内存或者磁盘 Java 平台允许我们在内存中创建可复用的 Java 对象,但一般情况下,只有当 JVM 处于运行时,这些对象才可能存在,即,这些对象的生命周期不…...
如何使用自动化工具编写测试用例?
以下为作者观点,仅供参考: 在快速变化的软件开发领域,保证应用程序的可靠性和质量至关重要。随着应用程序复杂性和规模的不断增加,仅手动测试无法满足行业需求。 这就是测试自动化发挥作用的地方,它使软件测试人员能…...
redis底层数据结构之skiplist实现
skiplist实现 skiplist跳跃表,是一种有序数据结构,通过在每个节点中维持多个指向其他节点的指针,来达到快速访问节点的目的,redis使用skiplist作为zsort的底层实现之一 结构很像树形结构 typedef struct zskiplistNode { // 对象…...
mjpg-streamer配置其它端口访问视频
环境 树莓派4B ubuntu 20.04 U口摄像头 确认摄像头可访问 lsusb查看 在dev下可查看到video* sudo mplayer tv://可打开摄像头并访问到视频 下载mjpg-streamer并编译安装 在github下载zip包,下载的源码,需要编译安装 unzip解压 cd mjpg-streamer/mjp…...
C++相关闲碎记录(15)
1、string字符串 #include <iostream> #include <string> using namespace std;int main (int argc, char** argv) {const string delims(" \t,.;");string line;// for every line read successfullywhile (getline(cin,line)) {string::size_type beg…...
汽车IVI中控开发入门及进阶(十一):ALSA音频
前言 汽车中控也被称为车机、车载多媒体、车载娱乐等,其中音频视频是非常重要的部分,音频比如播放各种格式的音乐文件、播放蓝牙接口的音乐、播放U盘或TF卡中的音频文件,如果有视频文件也可以放出音频,看起来很简单,在windows下音乐播放器很多,直接打开文件就能播放各…...
Gradle 之初体验
文章目录 1.安装1)检查 JDK2)下载 Gradle3)解压 Gradle4)环境变量5)验证安装 2.优势总结 Gradle 是一款强大而灵活的构建工具,用于自动化构建、测试和部署项目。它支持多语言、多项目和多阶段的构建&#x…...
【Spark精讲】Spark内存管理
目录 前言 Java内存管理 Java运行时数据区 Java堆 新生代与老年代 永久代 元空间 垃圾回收机制 JVM GC的类型和策略 Minor GC Major GC 分代GC Full GC Minor GC 和 Full GC区别 Executor内存管理 内存类型 堆内内存 堆外内存 内存管理模式 静态内存管理 …...
C语言实现Hoare版快速排序(递归版)
Hoare版 快速排序是由Hoare发明的,所以我们先来讲创始人的想法。我们直接切入主题,Hoare版快速排序的思想是将一个值设定为key,这个值不一定是第一个,如果你选其它的值作为你的key,那么你的思路也就要转换一下…...
git 避免输入用户名 密码 二进制/文本 文件冲突解决
核心概念介绍 工作区是你当前正在进行编辑和修改的文件夹,可见的。 暂存区位于.git/index(git add放入)。 代码库(工作树)位于.git(git commit将暂存区中的更改作为一个提交保存到代码库中,并清空暂存区) 避免输入用户 密码: 方式一: ht…...
[OpenWrt]RAX3000一根线实现上网和看IPTV
背景: 1.我家电信宽带IPTV 2.入户光猫,桥接模式 3.光猫划分vlan,将上网信号IPTV信号,通过lan口(问客服要光猫超级管理员密码,具体教程需要自行查阅,关键是要设置iptv在客户侧的vlan id&#…...
最新50万字312道Java经典面试题52道场景题总结(附答案PDF)
最近有很多粉丝问我,有什么方法能够快速提升自己,通过阿里、腾讯、字节跳动、京东等互联网大厂的面试,我觉得短时间提升自己最快的手段就是背面试题;花了3个月的时间将市面上所有的面试题整理总结成了一份50万字的300道Java高频面…...
代做计算机毕业设计网站/手机百度提交入口
树立新的课程观念提高教育教学质量<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />在这段时间里,我们参加了新一轮校本培训。在一学期走过的路,我们开展了许多的探索,也取得了一些阶段性成果&…...
营销网站建设步骤/友缘在线官网
1.方法一:创建分割日志文件的脚本,添加定时任务 脚本存放路径:/usr/local/nginx/sbin/cut_nginx_logs.sh,按天分割具体内容: [rootroot sbin]# pwd /usr/local/nginx/sbin [rootroot sbin]# ll -rwxr-xr-x 1 root root 865 …...
北京网站制作/百度一下首页官网
层叠顺序与堆栈上下文知多少z-index 看上去其实很简单,根据 z-index 的高低决定层叠的优先级,实则深入进去,会发现内有乾坤。看看下面这题,定义两个 div A 和 B,被包括在同一个父 div 标签下。HTML结构如下:…...
wordpress用户个人资料/短视频赚钱app软件
一. 程序题(共1题,100分) (程序题) 题目描述: 众所周知,人类基因可以被简单认为是一个字符串,包含四种分别用A,C,T,G表示的核苷酸。生物学家对鉴别人类基因核确定他们的功能很感兴趣。因为这对诊断人类疾病和开发新药很有用。 人类基因可以用一堆特别的快速的试验来鉴别,…...
wow电影在线观看/系统清理优化工具
[转载自博客](http://blog.csdn.net/huang_wei_cai/article/details/52515817) 前言: Android Studio中对一个自己库进行生成操作时将会同时生成.jar与.aar文件。如下是本人测试可行的方案,需要学习的可以参考。 分别存储位置: *.jar&#x…...
黄页模式/凯里seo排名优化
需求:使用spark统计词频,并求出现次数最多的10个词以及出现次数问题:用Spark算子top(),求top N的时候结果不准确我们用一首被初中生唱收费的《That girl》来做测试:步骤一:使用算子map() 、reduceByKey()、…...