最近做外销机,在实现设置时区的功能中,遇到了一些问题,特此记录。

       1,如何获取到时区列表?如何设置时区?

       2,默认时区怎么设置?

       3,自动时区是怎么回事?

      一,设置时区问题 

      首先我们看一下android的原生设置中的逻辑,代码位置在原生设置setting中的com.android.tv.settings.system.DateTimeActivity.java中。在这个类中,很容易找到其中的setTimeZone方法。而其中的alarm.setTimeZone(tzId);正是设置时区的关键代码,其中tzId是String类型的,其实就是时区id。

时区示例:(
//    KEY_DISPLAYNAME 中国标准时间
//    KEY_OFFSET 28800000
//    KEY_ID Asia/Shanghai
//    KEY_GMT GMT+08:00)

Android 设置时区和时间 安卓手机如何设置时区_timezone

       很好,设置时区的关键方法有了,但是如何获取时区列表?如何获取到时区名称和时区ID(tzId)之间的关系呢?在DateTimeActivity.java中继续查找。

mTimeZones = ZoneGetter.getZonesList(this);

其中:private List<Map<String, Object>> mTimeZones;

我们继续进行,找到ZoneGetter的文件位置。

./frameworks/base/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java. 通过查找其对应的mk文件(./frameworks/base/packages/SettingsLib/Android.mk),我们知道对应的LOCAL_MODULE := SettingsLib. 它对应的编译后jar位置是:out/target/common/obj/JAVA_LIBRARIES/SettingsLib_intermediates。

       但是如果直接将这个jar包导入到AndroidStudio中,由于在ZoneGetter.java有依赖R,import com.android.settingslib.R; 而jar中是不包含资源文件R的,虽然编译能过,但是运行时报如下错误,这个跟之前eclipse直接集成带有资源的jar包一样的错误,比如很多初学者在第一次使用Recycleview的时候会直接导入它的jar,而不做模块依赖。

 Caused by: java.lang.ClassNotFoundException: Didn't find class "com.android.settingslib.R$xml" on path:DexPathList[[。。。。。。

        分析代码发现,这里的R正是为了获取时区列表:

context.getResources().getXml(R.xml.timezones). 似乎Android并未为我们提供一种很方便的方式,将资源文件一起打包进来。无非就是再AndroidStudio中新建一个Module,在这个Module中导入jar包和对应的xml,在我们主工程中依赖这个Module。

         这里我们用另一种更加简单的方案解决此问题,观察发现,ZoneGetter.java这个类import的包除了java,android的以外,还有一个特别的,那就是import libcore.icu.TimeZoneNames;这个类的位置:./libcore/luni/src/main/java/libcore/icu/TimeZoneNames.java. 同样的方法,找到其mk文件(./libcore/JavaLibrary.mk)。找到对应的Module名字。

Android 设置时区和时间 安卓手机如何设置时区_android tv_02

它编译后对应的jar包是:out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates。经过上面的分析,问题的最终解决方案如下:

步骤1:将libcore的jar包导入到AndroidStudio中,导入方式为CompileOnly;

步骤2:将xml(frameworks/base/packages/SettingsLib/res/xml/timezones.xml)拷贝到自己工程,如需要定制可修改。

步骤3:将ZoneGetter.java直接拷贝到自己的工程,修改packagename和R的依赖即可。

二,设置默认时区

        这就比较简单了,只是设置一个系统属性即可,可以在机型的device.mk(比如device/mstar/synsepalum/device.mk)中设置。

Android 设置时区和时间 安卓手机如何设置时区_android studio_03

 

三,自动时区是怎么回事?

1,先简单介绍一下自动时间问题:

       Android的时间更新分成2种,一种是走运营商协议的NITZ,另外一种是走网络时钟的SNTP。 SNTP是简单网络时间协议(Simple Network Time protocol)的简称。 

       在Android系统中,Settings->Date & time->Automatic项打上后,时间会通过网络进行同步,而Android默认使用的是NITZ来获取移动网络时间,需要有移动网络服务商的支持才能使用,而有些Android产品只支持WIFI等无线网络,而不支持移动网络,此时就需要采用SNTP方式来获取网络时间进行同步了。

        当不支持NITZ获取时间同步时就使用SNTP方式获取时间进行同步。不过使用SNTP方式进行时间同步时不能同步时区(后面我们会重点关注这一点),需要自己先去掉自动同步并设置时区后再设置自动同步,这样才能进行网络时间的同步更新。

        2,NITZ:NetworkIdentityandTimeZone(网络标识和时区) 依赖基站发出的消息的回调更新时间。

        NITZ更新时间依赖运营商,当运营商基站发出更新时间的消息,基站附近的手机接收到对应消息后,会通过RIL层上报UNSOL_NITZ_TIME_RECEIVED事件,此时ServiceStateTracker便会处理相关时间更新流程。

由于NITZ主要依赖于运营商,但在国内移动和联通貌似不怎么好用,在这里就不在详细说了,简单总结下如下:1、在ServiceStateTracker构造方法里调用setOnNITZTime注册RIL事件RIL_UNSOL_NITZ_TIME_RECEIVED2、RIL层上报RIL_UNSOL_NITZ_TIME_RECEIVED,在ServiceStateTracker的handleMessage里处理3、调用ServiceStateTracker的setTimeFromNITZString设置时间和时区,在setAndBroadcastNetworkSetTime里调用setCurrentTimeMillis设置系统时间,并发送广播通知NetworkTimeUpdateService。

        3,SNTP:(Simple Network Time protocol) 

相关的类有下面几个,这里简单介绍一下,深入了解还是需要看代码:

Android 设置时区和时间 安卓手机如何设置时区_android studio_04

其中:NetworkTimeUpdateService.java 这是控制时间的逻辑,设计时间更新的策略,机制等等。

 NteTrustedTime.java这里封装了几个方法比如foreceRefresh给NetworkTimeUpdateService调用。

 SntpClient.java是实际的与网络交互的类,这个类可以单独拿出来跑。

 通过Settings.java, DatabaseHelper.java Config.xml找到了服务器地址等,服务器等的配置再Config.xml中:

Android 设置时区和时间 安卓手机如何设置时区_Android 设置时区和时间_05

自己写demo打印了时间的数据返回值如下类型是byte[] ,从中能解析出时间出来。

Arrays.toString(buffer):

12-11 07:49:42.061 3640-3686/com.xgimi.home D/SntpClient: buffer = [28, 2, 0, -24, 0, 0, 0, 10, 0, 0, 0, 69, 10, -119, 53, 7, -31, -101, 1, -56, 107, 119, 35, -59, -31, -101, 1, -10, 1, 71, -82, 73, -31, -101, 1, -10, 8, -100, 90, 31, -31, -101, 1, -10, 8, -96, 62, 11]

12-11 07:49:42.061 3640-3686/com.xgimi.home D/SntpClient: mNtpTime = 1576043382061        mNtpTimeReference = 209944         mRoundTripTime = 56

       4,AUTO_TIME_ZONE是否只在NITZ下面有效?

       在SNTP的相关文档中没有找到能够更新时区的相关代码,我们猜测时区无法通过SNTP更新。从上面NTP返回的数据来看,也确实是不包括时区信息的。

我们在Setting中找到了下面的代码:

Android 设置时区和时间 安卓手机如何设置时区_Android 设置时区和时间_06

这里特别标明了NITZ,是否也暗示了只有NITZ方式能够获取到时区。

为了验证我的猜测,用手机做了一些实现:

a,魅族note2,打开飞行模式,时间时区调错乱;

b,连接wifi的瞬间自动校时,但是无法自动设置时区,;

c,关闭飞行模式的瞬间,时区立即校准。

d,插拔sim2卡测试。sim在即可更新时区,sim卡不在就不能更新时区。

        这可能是某个规范或者标准,Android目前不能通过SNTP更新时区。我目前在Android6上做的这些工作,或许后面能够更新也未可知。总之,目前来说,如果是电视机型开发,无法插入sim卡,就不会有自动时区的功能。