由于形势和工作的需要,我不得不windows开发和Android开发同时进行
本来我入职这个公司是做windows端的。但由于安卓端没有人维护了,所以就让我来搞。
讲道理即便Android 0基础, 都是程序员维护个客户端还是可以的。
这句话说的稍微有点大,毕竟我之前是c++,windows。 java 安卓系统一概没弄过。 那么我就来说说怎么来快速上手安卓,或者说转到安卓?
1.那就是开发环境的安装,既然咱是新来的,那就一切用最新的,当下最流行的就好了,因为我没有任何旧的经验和偏好,在我看来这也是一件好事。
开发工具的选择
IDE我们使用主流的Android Studio简称AS,具体怎么配置安装网上一堆教程,肯定能最终搞定.
开发语言的选择
这个有点纠结,我在之前只听说过java,而且公司的app也是用java写的。那就暂用java吧,至于java的学习,找个菜鸟教程看一遍就行了,最多3天,可能很多人说,你开玩笑吧。的确如此,学java最多3天(小时)。有别的语言的基础,以后遇到啥问题查就行了,逐渐的就熟练了。说什么语言多高深的那都是新手,你再高深我只用我用到的功能,特别难懂的我遇到问题再去解决学习一点都不晚,没必要一下子啥都弄明白 ,又不考试。长期不用的技术很快就忘记了。在我看来一门高级语言,只要你会基本语法就算会了,剩下的就是工作中实际去熟练更多的套路了,总去研究那些代码糖,没用。
维护别人的代码
维护别人代码,最大的好处是--出问题了可以甩锅
这是笔记,不是教程。。。
由于各种原因 我又决定用kotlin了,正如之前所说,学kotlin也是如此的,最多3天(小时)
用着用着 kotlin还是不错的,其实我用kotlin的一个原因就是因为我写了一个不好的代码编译器给出很细致的提示,这让我很高兴,以前用VS从来没这种事儿,错就错,对就对,它从来不告诉我 好不好。
那么既然犯了程序员的老毛病了(爱有偏好,而且即便100个人劝说也能找出理由反驳别人),那就得多干活了,重构吧,下决心,虽然原来java的代码写的还是很好的,也不难维护。 接下来咱就记录一个新人用kotlin重构别人用java写的app的过程,纯属流水账式样的笔记。
前边说了,学习任何一个语言只需要3小时,然后就是遇到问题解决问题,那么现在遇到第一个问题了,因为没安卓经验所以别人很多不是问题的问题 我们也要去解决,但下次你不就会了吗,这不就是学习,不要做过多的准备 学100天kotlin,熟悉100天安卓系统,然后去做项目 你会发现 你还是会遇到一堆问题,你之前学那些高深的代码套路,很标准的结构解析 往往不知道实际中有啥用? 言归正传,来说遇到的第一个问题
问题1:
不管是不是菜鸟,对APP提前的规划,以及app大体需要的功能模块 还是需要思考的,最好是画UML图。
那我在规划中有个功能模块就是需要一个全局共享的数据类,这个类应该可以在任何activity service中访问使用。我们是个视频会议的软件,这个类会记录很多配置类的信息,已达到方便使用的目的,其实一个全局的配置类,可能任何app都有这种需求吧?如果有人知道请赐教啊。
那么根据win编程的思想,我可以弄一个类实例,设置为全局变量,在让这个类实例去存读程序目录下的一个文件来达到存储的目的。
同样的目的到安卓中如何实现?
这几天疫情,还得去客户那调试东西,不容易啊,接上题,今天有点功夫了 继续。。。。
那么到现在为止我还没有忘记我是个windows程序员,所以安卓遇到的问题,我往往会类比到windows开发,那我要共享一个数据配置类,我会想到用一个全局单例的类实例。那么在安卓中,这种东西要怎么实现呢,一搜索,一大堆结果,基本都是说用Application类,在我目前的认识下, 安卓Application类相当于win MFC中的CWinApp类, 这个类的确可以全局可访问。讲道理我刚学win编程的时候用的就是MFC,也非常习惯把一些全局用的到的变量,类对象什么的定义在CWinApp中,在win中似乎也中规中矩, 但随着用的多了,感觉这样不好。毕竟人家winApp就是个初始化环境的类,少弄几个变量还好。要是配置信息比较多,比较复杂的时候,都写这里,就有点不好看了。 基于此,我又更多的查看了一些文章,其中有个老安卓说,用Application来共享数据之类的并不是谷歌推荐的,最好是自己单独创建一个单例类实例,还给出了谷歌对此解释的截图,大意就是说除非需要做很多自己的初始化逻辑,否则不要重载Application类。 我感觉说的有点道理,什么人干什么事,身兼数职不是好事儿。 那就开始来搞个全局单例的Android kotlin的类实例吧。
说到全局单例,这个去网上一查能看到很多办法了,什么线程安全的不安全等等,但就我的需求来说无需考虑线程安全,我的配置主要都是一些静态信息,只不过根据公司要求,这些配置对不同的客户大有不同而已。说白了就是经常需要定制化开发,所以需要配置不少东西,也许你会说 应该开n个代码分支来处理这种需求。你说的没错,但对于我这种需求来说,开分支不值得,就是参数不一样,没必要开分支的。说的太啰嗦了。 来说明如何创建单例吧
kotlin没有static这种东西,但有伴生对象...关于kotlin如何创建单例,网上搜 一堆一堆的。语法都很简单.
我先是创建了一个工具类,这个类我打算所有工具函数 都写在里面,那这个创建个单例,也算合适,那就先贴这个工具类,目前就实现2个接口,从文件存/读一个对象,
package com.jsh.jim.utils
import android.content.Context
import android.util.Log
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.io.Serializable
class CommonUtils() {
companion object{
private var context:Context?=null
private val constData:JimConstData by lazy { JimConstData() }
//must call this first in app start
fun initUtils(con:Context)
{
context = con
}
//saveObject:save object to file saveName
//obj:the object to be saved
//saveName:the saved file name, eg->data.dat
fun saveObject(obj:Any, saveName:String):Boolean?
{
return context?.let {
try {
ObjectOutputStream(it.openFileOutput(saveName, Context.MODE_PRIVATE)).apply {
writeObject(obj)
}
return true
} catch (e:Exception) {
Log.i(constData.logTag, "saveObject failed($saveName), ${e.toString()}")
}
return false
}
}
//readObject:read object from saveName
//saveName:the saved file name, eg->data.dat
//return:the read object from file
fun readObject(saveName:String):T?
{
return context?.let {
try {
ObjectInputStream(it.openFileInput(saveName)).run {
this.readObject() as T
}
}
catch (e:Exception)
{
Log.i(constData.logTag, "readObject failed($saveName), ${e.toString()}")
return null
}
}
}
}
}
这代码一共就3个接口 initUtils需要在程序启动的时候调用一次
之后在任何activity或者service就可以调用接口来存读对象(可序列化的对象)
package com.jsh.jim
import android.content.Context
import android.os.Bundle
import android.os.Environment
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.jsh.jim.utils.CommonUtils
import java.io.*
import kotlin.properties.Delegates
class MySettings():Serializable
{
var mName:String=""
var mAge:Int = 18
override fun toString(): String {
return "name=$mName, age=$mAge"
}
}
class MainActivity : AppCompatActivity() {
private var mySettings:MySettings? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
CommonUtils.initUtils(applicationContext)
mySettings = MySettings()
}
@Suppress("UNUSED_PARAMETER")
fun onButton1Click(view: View) {//write data to test.dat
mySettings?.mName="朱瑞明"
mySettings?.mAge = 21
mySettings?.apply {
CommonUtils.saveObject(this, "test4.dat")
}
@Suppress("UNUSED_PARAMETER")
fun onButton2Click(view: View) {
CommonUtils.readObject("test4.dat")?.apply {
mySettings = this
Log.i("readIt", mySettings.toString())
}
}
Toast.makeText(this, mySettings.toString(), Toast.LENGTH_SHORT).show()
}
}
对了 这是笔记啊,相当于随想学习的,如果有人看 别误导啊,我基本是给留念的,等60岁的时候看看,自己也曾经学习过。。。
对,我们平时做程序,都喜欢把一些常量定义到一个什么commonDefine.h里面,然后到处使用里面的定义,我大安卓也需要这么个东西,咋办,okay--->就弄个全局的数据类吧,直接贴代码
package com.jsh.jim.utils
data class JimConstData(
val logTag:String="JIM_LOG",
val fileNameUser:String="user.config"
)
{
}
...就俩值一个log的tag 一个准备存储用户信息的文件名, 以后肯定会越来越多的。做程序不就是这样吗,一开始老板说让做个自行车,最后老板抱怨你这个自行车不会飞,最关键是一到水里就不能跑了。咱程序员能说啥,只能赶紧给它加俩翅膀,再补上俩救生圈,虽然水上不能跑,但至少不会死。。。 让老板闭嘴!
得干本职工作了,kotlin重构,基本属于个人爱好,每天写一点,真有同道中人 不妨共勉。
现在继续完善全局共享的数据类,经过分析 我需要2个全局共享的单例类实例 一个叫做oem 一个叫做util。 oem的配置信息将决定很多逻辑和UI表现, util自然就是常用的一些工具函数了。 其中用户信息我决定放入了oem,用户信息有很多是根据用户的设定和操作随时改变的,但我希望当用户改变了这个信息后,我的app能够自动的存储下来,以便下次读取,比如最简单的,是否自动登录。 由于没任何经验,响应属性改变这件事儿,我最自然的就想到了属性委托的监测属性"Delegates.observable"比如类似这样的一个类
class JimProductInfo:Serializable
{
var brandName:String by Delegates.observable("及时会"){_,_,_->this.onValueChanged()}
var domainUrl:String by Delegates.observable("www.zoomus.cn"){_,_,_->this.onValueChanged()}
private fun onValueChanged()
{
CommonUtils.saveObject(this, CommonUtils.constData.fileNameProductSettings)
}
}
预期在属性变化的时候,在函数onValueChanged中进行存储,似乎看起来没什么可说的,但一运行调试,发现存储文件是产生异常,说我的类为序列化,我明明继承序列化类了啊。进一步发现只要属性被委托了,就无法序列化,想想也是,人家编译器怎么知道你的委托是不是能够进行序列化。 okay,那就不能用看起来比较高级的委托了, 其实也是,就这么个事情 为啥不规矩点呢,非要用看起来高级的委托? 这也许就是程序员的老毛病吧。
规矩点这样就行了
class JimProductInfo:Serializable
{
var brandName:String ="及时会"
set(value){
field=value
this.onValueChanged()
}
var domainUrl:String ="www.zoomus.cn"
set(value){
field=value
this.onValueChanged()
}
private fun onValueChanged()
{
CommonUtils.saveObject(this, CommonUtils.constData.fileNameProductSettings)
}
}
好了,这样更改后,一切运行正常了,也能正确存储了。 但代码看起来很冗余,如果属性很多的话,每一个都得敲复制一遍一样的代码。 关于这个怎么处理,我没找到好的办法,谁知道请告诉问我,谢谢了 QQ24160586