1. 程式人生 > >Android OTA升級(1):編譯升級全包

Android OTA升級(1):編譯升級全包

2013-3-23

     Android原生系統中就已經支援OTA升級。所謂OTA升級就是通過空中介面獲取升級包,然後更新系統韌體。一般地,升級包無論如何獲取,哪怕是直接TCard本地升級,也被稱為OTA升級。

     OTA升級首要是生成OTA升級包,升級包又分為升級全包和升級差分包(或要增量包)。升級全包是編譯當前系統得到的軟體包,這個包很大,有上百兆,但是不依賴與當前手機裡的軟體版本;升級差分包是對手機兩個軟體版本做差分,在第一個版本上打patch,得到第二個升級包,所以差分包只能對第一個版本的機器進行升級。

    本文主要講述升級全包的生成過程。

編譯升級全包,用下面指令:

#make otapackage

最終生成INTERNAL_OTA_PACKAGE_TARGET,也就是$(PRODUCT_OUT)/$(name).zip。

一、升級全包($(PRODUCT_OUT)/$(name).zip)的生成

INTERNAL_OTA_PACKAGE_TARGET依賴於BUILT_TARGET_FILES_PACKAGE

生成規則【在build/core/Makefile中】:

$(INTERNAL_OTA_PACKAGE_TARGET):$(BUILT_TARGET_FILES_PACKAGE) $(OTATOOLS)
                @echo"Package OTA: [email protected]
" $(hide)./build/tools/releasetools/ota_from_target_files -v \ -p $(HOST_OUT) \ -k $(KEY_CERT_PAIR) \ $(BUILT_TARGET_FILES_PACKAGE) [email protected]

ota_from_target_files是python指令碼,需要中間檔案$(BUILT_TARGET_FILES_PACKAGE)。所以,在分析完中間檔案的生成之後再看最終升級全包的生成過程

二、中間檔案包($(TARGET_PRODUCT)-target_files-$(FILE_NAME_TAG).zip)的生成

BUILT_TARGET_FILES_PACKAGE是$(intermediates)/$( TARGET_PRODUCT)-target_files-$(FILE_NAME_TAG).zip

形如:out/target/product/<product>/obj/PACKAGING/target_files_intermediates/<product>-target_files-<eng>.haili.tian

生成規則【在build/core/Makefile中】:

$(BUILT_TARGET_FILES_PACKAGE):\
              $(INSTALLED_BOOTIMAGE_TARGET) \
              $(INSTALLED_RADIOIMAGE_TARGET) \
              $(INSTALLED_RECOVERYIMAGE_TARGET)\
              $(INSTALLED_SYSTEMIMAGE) \
              $(INSTALLED_USERDATAIMAGE_TARGET)\
              $(INSTALLED_ANDROID_INFO_TXT_TARGET)\
              $(built_ota_tools) \
              $(APKCERTS_FILE) \
              $(HOST_OUT_EXECUTABLES)/fs_config\
              | $(ACP)
       @echo "Package target files:[email protected]"
       $(hide) rm -rf [email protected] $(zip_root)
       $(hide) mkdir -p $(dir [email protected]) $(zip_root)
       @# Components of the recovery image
       $(hide) mkdir -p $(zip_root)/RECOVERY
       $(hide) $(call package_files-copy-root, \
              $(TARGET_RECOVERY_ROOT_OUT),$(zip_root)/RECOVERY/RAMDISK)
ifdef INSTALLED_KERNEL_TARGET
       $(hide) $(ACP) $(INSTALLED_KERNEL_TARGET)$(zip_root)/RECOVERY/kernel
endif
ifdef INSTALLED_2NDBOOTLOADER_TARGET
       $(hide) $(ACP) \
              $(INSTALLED_2NDBOOTLOADER_TARGET)$(zip_root)/RECOVERY/second
endif
ifdef BOARD_KERNEL_CMDLINE
       $(hide) echo"$(BOARD_KERNEL_CMDLINE)" > $(zip_root)/RECOVERY/cmdline
endif
ifdef BOARD_KERNEL_BASE
       $(hide) echo"$(BOARD_KERNEL_BASE)" > $(zip_root)/RECOVERY/base
endif
ifdef BOARD_KERNEL_PAGESIZE
       $(hide) echo "$(BOARD_KERNEL_PAGESIZE)"> $(zip_root)/RECOVERY/pagesize
endif
       @# Components of the boot image
       $(hide) mkdir -p $(zip_root)/BOOT
       $(hide) $(call package_files-copy-root, \
              $(TARGET_ROOT_OUT),$(zip_root)/BOOT/RAMDISK)
ifdef INSTALLED_KERNEL_TARGET
       $(hide) $(ACP) $(INSTALLED_KERNEL_TARGET)$(zip_root)/BOOT/kernel
endif
ifdef INSTALLED_2NDBOOTLOADER_TARGET
       $(hide) $(ACP) \
              $(INSTALLED_2NDBOOTLOADER_TARGET)$(zip_root)/BOOT/second
endif
ifdef BOARD_KERNEL_CMDLINE
       $(hide) echo"$(BOARD_KERNEL_CMDLINE)" > $(zip_root)/BOOT/cmdline
endif
ifdef BOARD_KERNEL_BASE
       $(hide) echo"$(BOARD_KERNEL_BASE)" > $(zip_root)/BOOT/base
endif
ifdef BOARD_KERNEL_PAGESIZE
       $(hide) echo"$(BOARD_KERNEL_PAGESIZE)" > $(zip_root)/BOOT/pagesize
endif
       $(hide) $(foreach t,$(INSTALLED_RADIOIMAGE_TARGET),\
                   mkdir -p $(zip_root)/RADIO; \
                   $(ACP) $(t)$(zip_root)/RADIO/$(notdir $(t));)
       @# Contents of the system image
       $(hide) $(call package_files-copy-root, \
              $(SYSTEMIMAGE_SOURCE_DIR),$(zip_root)/SYSTEM)
       @# Contents of the data image
       $(hide) $(call package_files-copy-root, \
              $(TARGET_OUT_DATA),$(zip_root)/DATA)
       @# Extra contents of the OTA package
       $(hide) mkdir -p $(zip_root)/OTA/bin
       $(hide) $(ACP)$(INSTALLED_ANDROID_INFO_TXT_TARGET) $(zip_root)/OTA/
       $(hide) $(ACP) $(PRIVATE_OTA_TOOLS)$(zip_root)/OTA/bin/
       @# Files that do not end up in anyimages, but are necessary to
       @# build them.
       $(hide) mkdir -p $(zip_root)/META
       $(hide) $(ACP) $(APKCERTS_FILE)$(zip_root)/META/apkcerts.txt
       $(hide)       echo"$(PRODUCT_OTA_PUBLIC_KEYS)" > $(zip_root)/META/otakeys.txt
       $(hide) echo"recovery_api_version=$(PRIVATE_RECOVERY_API_VERSION)" >$(zip_root)/META/misc_info.txt
ifdef BOARD_FLASH_BLOCK_SIZE
       $(hide) echo"blocksize=$(BOARD_FLASH_BLOCK_SIZE)" >> $(zip_root)/META/misc_info.txt
endif
ifdef BOARD_BOOTIMAGE_PARTITION_SIZE
       $(hide) echo"boot_size=$(BOARD_BOOTIMAGE_PARTITION_SIZE)" >>$(zip_root)/META/misc_info.txt
endif
ifdef BOARD_RECOVERYIMAGE_PARTITION_SIZE
       $(hide) echo"recovery_size=$(BOARD_RECOVERYIMAGE_PARTITION_SIZE)" >>$(zip_root)/META/misc_info.txt
endif
ifdef BOARD_SYSTEMIMAGE_PARTITION_SIZE
       $(hide) echo"system_size=$(BOARD_SYSTEMIMAGE_PARTITION_SIZE)" >>$(zip_root)/META/misc_info.txt
endif
ifdef BOARD_USERDATAIMAGE_PARTITION_SIZE
       $(hide) echo "userdata_size=$(BOARD_USERDATAIMAGE_PARTITION_SIZE)">> $(zip_root)/META/misc_info.txt
endif
       $(hide) echo"tool_extensions=$(tool_extensions)" >>$(zip_root)/META/misc_info.txt
ifdef mkyaffs2_extra_flags
       $(hide) echo"mkyaffs2_extra_flags=$(mkyaffs2_extra_flags)" >>$(zip_root)/META/misc_info.txt
endif
       @# Zip everything up, preserving symlinks
       $(hide) (cd $(zip_root) && zip-qry ../$(notdir [email protected]) .)
       @# Run fs_config on all the system filesin the zip, and save the output
       $(hide) zipinfo -1 [email protected] | awk -F/ 'BEGIN {OFS="/" } /^SYSTEM\// {$$1 = "system"; print}' |$(HOST_OUT_EXECUTABLES)/fs_config > $(zip_root)/META/filesystem_config.txt
       $(hide) (cd $(zip_root) && zip -q../$(notdir [email protected]) META/filesystem_config.txt)

其中,變數定義:

-         TARGET_RECOVERY_ROOT_OUT也就是$(TARGET_RECOVERY_OUT)/root;TARGET_RECOVERY_OUT是$(PRODUCT_OUT)/recovery;PRODUCT_OUT是out/target/product/<product>。所以,TARGET_RECOVERY_ROOT_OUT也就是out/target/product/<product>/recovery/root;

-         zip_root是$(intermediates)/$( TARGET_PRODUCT)-target_files-$(FILE_NAME_TAG)路徑,也就是out/target/product/<product>/obj/PACKAGING/target_files_intermediates/<product>-target_files-<eng>.haili.tian;

-         TARGET_ROOT_OUT是$(PRODUCT_OUT)/root,也就是out/target/product/<product>/root;

-         INSTALLED_RADIOIMAGE_TARGET;

-         SYSTEMIMAGE_SOURCE_DIR是$(TARGET_OUT),也就是out/target/product/<product>/system/;

-         TARGET_OUT_DATA是$(PRODUCT_OUT)/data,也就是out/target/product/<product>/data;

-         INSTALLED_ANDROID_INFO_TXT_TARGET是$(PRODUCT_OUT)/android-info.txt,也就是out/target/product/<product>/android-info.txt;

-         PRIVATE_OTA_TOOLS是$(built_ota_tools),也就是包含了:

    out/target/product/<product>/obj/EXECUTABLES/applypatch_intermediates/applypatch

    out/target/product/<product>/obj/EXECUTABLES/applypatch_static_intermediates/applypatch_static

    out/target/product/<product>/obj/EXECUTABLES/check_prereq_intermediates/check_prereq

    out/target/product/<product>/obj/EXECUTABLES/updater_intermediates/updater

-         APKCERTS_FILE是out/target/product/<product>/obj/PACKAGING/apkcerts_intermediates/<product>-apkcerts-<eng>.haili.tian.txt

-         PRODUCT_OTA_PUBLIC_KEYS目前為空;

-         PRIVATE_RECOVERY_API_VERSION也就是RECOVERY_API_VERSION,RecoveryAPI的版本號;

-         tool_extensions如果定義了TARGET_RELEASETOOLS_EXTENSIONS,tool_extensions也就是TARGET_RELEASETOOLS_EXTENSIONS;

-         DEFAULT_SYSTEM_DEV_CERTIFICATE目前為空;

-         HOST_OUT_EXECUTABLES是$(HOST_OUT)/bin,也就是out/host/linux-x86/bin;

指令碼的執行

1.      #清理以前殘存的目錄和檔案,並建立$(zip_root)目錄(也就是out/target/product/<product>/obj/PACKAGING/target_files_intermediates/<product>-target_files-<eng>.haili.tian);

2.      建立$(zip_root)/RECOVERY目錄,把$(TARGET_RECOVERY_ROOT_OUT)(也就是out/target/product/<product>/recovery/root)下的所有檔案和目錄都拷貝到$(zip_root)/RECOVERY/RAMDISK/下;

3.      建立$(zip_root)/BOOT目錄,然後把$(TARGET_ROOT_OUT)(也就是out/target/product/<product>/root)下的所有檔案和目錄都拷貝到$(zip_root)/BOOT/RAMDISK/下;

4.      建立$(zip_root)/RADIO目錄,然後把$(INSTALLED_RADIOIMAGE_TARGET)下的所有檔案和目錄都拷貝到$(zip_root)/ RADIO/下;

5.      把$(SYSTEMIMAGE_SOURCE_DIR)【也就是out/target/product/<product>/system/】下的所有檔案和目錄都拷貝到$(zip_root)/SYSTEM/下;

6.      把$(TARGET_OUT_DATA)【也就是out/target/product/<product>/data】下的所有檔案和目錄都拷貝到$(zip_root)/DATA/下;

7.      建立$(zip_root)/OTA/bin目錄,然後把INSTALLED_ANDROID_INFO_TXT_TARGET【也就是out/target/product/<product>/android-info.txt】放到$(zip_root)/OTA/下;

8.      把PRIVATE_OTA_TOOLS【也就是applypatchapplypatch_staticcheck_prerequpdater】放到$(zip_root)/OTA/bin下。

9.      建立$(zip_root)/META目錄,蒐集META資訊並儲存到檔案裡:

    把APKCERTS_FILE【也就是out/target/product/<product>/obj/PACKAGING/apkcerts_intermediates/<product>-apkcerts-<eng>.haili.tian.txt】放到$(zip_root)/META/apkcerts.txt

    把$(PRODUCT_OTA_PUBLIC_KEYS)追加到$(zip_root)/META/otakeys.txt;【】

    把recovery_api_version=$(PRIVATE_RECOVERY_API_VERSION)這行追加到$(zip_root)/META/misc_info.txt

    把tool_extensions=$(tool_extensions)這行追加到$(zip_root)/META/misc_info.txt

    把default_system_dev_certificate=$(DEFAULT_SYSTEM_DEV_CERTIFICATE)這行追加到$(zip_root)/META/misc_info.txt

10.  拷貝Image檔案

    把$(PRODUCT_OUT)/ramdisk.img放到$(zip_root)/下;

    把$(PRODUCT_OUT)/ramdisk-recovery.img放到(zip_root)/

    把$(PRODUCT_OUT)/../../../../boot/out/u-boot.bin放到$(zip_root)/

    把$(PRODUCT_OUT)/../../../../kernel/out/uImage放到(zip_root)/

11.  進入$(zip_root),把$(zip_root)下所有的內容打進zip包<product>-target_files-<eng>.haili.tian.zip

12.  用$(HOST_OUT_EXECUTABLES)/fs_config獲取檔案系統資訊並儲存在檔案裡

     “SYSTEM/”下的檔案系統資訊儲存在$(zip_root)/META/filesystem_config.txt中;

     “BOOT/RAMDISK/”下的檔案系統資訊儲存在$(zip_root)/META/boot_filesystem_config.txt中;

     “RECOVERY/RAMDISK/”下的檔案系統資訊儲存在$(zip_root)/META/recovery_filesystem_config.txt中;

13.  把META/*filesystem_config.txt檔案打到zip包<product>-target_files-<eng>.haili.tian.zip裡。

三、ota_from_target_file分析

Ota_from_target_file是python指令碼,從main()開始分析。

3.1 生成總過程 – main()

Main()裡主要做下列工作:

1.      從引數和環境中解析OPTIONS;

2.      從中間zip包裡的META/misc_info.txt中解析出{<key>=<value>}OPTIONS對,比如recovery_api_version=3;tool_extensions=device/<company>/<product>/recovery;

3.      從OPTIONS對裡找到tool_extensions,賦值給OPTIONS.device_specific;

4.      input_zip為上文的中間zip包;output_zip為最終生成的臨時zip包;

5.      對於全包升級包,呼叫WriteFullOTAPackage(input_zip,output_zip);

6.      簽名臨時包,生成最終簽過名的最終全包升級包。

上述過程中,我們最關注的是WriteFullOTAPackage(input_zip,output_zip)做的工作。

3.2 生成全包 – WriteFullOTAPackage()

WriteFullOTAPackage[file:build/tools/releasetools/ota_from_target_files]的定義如下:

def WriteFullOTAPackage(input_zip, output_zip):
  # TODO:how to determine this?  We don't knowwhat version it will
  # beinstalled on top of.  For now, we expectthe API just won't
  # changevery often.
  script =edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
 
  metadata= {"post-build": GetBuildProp("ro.build.fingerprint",input_zip),
             "pre-device": GetBuildProp("ro.product.device",input_zip),
              "post-timestamp":GetBuildProp("ro.build.date.utc", input_zip),
             }
 
 device_specific = common.DeviceSpecificParams(
     input_zip=input_zip,
     input_version=OPTIONS.info_dict["recovery_api_version"],
     output_zip=output_zip,
     script=script,
     input_tmp=OPTIONS.input_tmp,
     metadata=metadata,
     info_dict=OPTIONS.info_dict,
     trusted_boot=OPTIONS.trusted_boot)
 
#  if not OPTIONS.omit_prereq:
#    ts =GetBuildProp("ro.build.date.utc", input_zip)
#   script.AssertOlderBuild(ts)
 
# AppendAssertions(script, input_zip)
# device_specific.FullOTA_Assertions()
 
 script.ShowProgress(0.9, 16)
 
  if OPTIONS.wipe_user_data:
   script.FormatPartition("/data")
 
 script.FormatPartition("/system")
 script.Mount("/system")
# script.UnpackPackageDir("recovery", "/system")
 script.UnpackPackageDir("system", "/system")
 
  symlinks= CopySystemFiles(input_zip, output_zip)
 script.MakeSymlinks(symlinks)
 
# boot_img = common.File("boot.img", common.BuildBootableImage(
#     os.path.join(OPTIONS.input_tmp, "BOOT")))
# recovery_img = common.File("recovery.img",common.BuildBootableImage(
#     os.path.join(OPTIONS.input_tmp, "RECOVERY")))
# MakeRecoveryPatch(output_zip, recovery_img, boot_img)
 Item.GetMetadata(input_zip)
 Item.Get("system").SetPermissions(script)
 
# common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
# common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
# script.ShowProgress(0.2, 0)
 
# script.ShowProgress(0.2, 10)
# script.WriteRawImage("/boot", "boot.img")
 
# script.ShowProgress(0.1, 0)
 device_specific.FullOTA_InstallEnd()
 
  if OPTIONS.extra_script is not None:
   script.AppendExtra(OPTIONS.extra_script)
 
 script.UnmountAll()
 script.AddToZip(input_zip, output_zip)
  WriteMetadata(metadata,output_zip)

OTA全包生成的過程

1.      script用來生成Edify指令碼,在edify_generator.py中實現;

2.      script中增加語句:顯示進度;

3.      script中增加語句:擦除“/system”分割槽;

4.      script中增加語句:安裝system分割槽到“/system”;

5.      script中增加語句:把system中的內容複製到/system下;

6.      把input_zip包/system中的內容,複製到output_zip包中,不包含其中的link檔案;

7.      對於link檔案,script中增加指向連結的語句;

8.      從input_zip包的META/filesystem_config.txt中獲取其中描述的/system下各個檔案的許可權資訊;

9.      script中增加語句:設定/system下檔案的許可權和屬主資訊;

10.  呼叫具體device特定的函式FullOTA_InstallEnd。

     main()函式中已經獲取OPTIONS.device_specific為/device/<company>/<product>/recovery;這裡的device_specific是common.DeviceSpecificParams。

     common實現在common.py中,在判斷出OPTIONS.device_specific為目錄時,用該目錄下的releasetools.py指令碼。所以這裡執行releasetools.py中的FullOTA_InstallEnd。

11.  如果特別指定了其他指令碼,script中加入;

12.  通過script.AddToZip()寫META-INF

    把前面所有的指令碼,寫入到output_zip裡的META-INF/com/google/android/updater-script這個edify指令碼中;

    把input_zip裡的OTA/bin/updater寫入到output_zip裡的META-INF/com/google/android/update-binary

13.  把metadata的內容寫入到output_zip裡的META-INF/com/android/metadata

總結

    本文分析了Android OTA全包的編譯生成過程。首先生成中間包,這個包是後續全包生成的基礎,也是差分包生成的基礎,接著解析了生成最終升級全包的過程。通過分析知道升級包中update-binary和updater-script的來源,對於後續分析OTA升級的過程至關重要。

    差分包的生成在介紹完整個OTA的過程之後,再進一步闡述。

相關推薦

Android OTA升級1編譯升級

2013-3-23     Android原生系統中就已經支援OTA升級。所謂OTA升級就是通過空中介面獲取升級包,然後更新系統韌體。一般地,升級包無論如何獲取,哪怕是直接TCard本地升級,也被稱為OTA升級。     OTA升級首要是生成OTA升級包,升級包又分為升級全包

Android NDK學習編譯腳本語法Android.mk和Application.mk

GC make files 文件的 包括 一次 opengl aries 基本語法 一、Android.mk Android.mk分為一下幾部分: LOCAL_PATH:= $(call my-dir), 返回當前文件在系統中的路徑,Android.mk文件開始時必須定義

Android元件系列1自動完成輸入內容的元件AutoCompleteTextView

本文為原創,如需轉載,請註明作者和出處,謝謝!     AutoCompleteTextView 和 EditText 元件類似,都可以輸入文字。但 AutoCompleteText

Android音訊開發1基礎知識

Android音訊開發(1):基礎知識 導讀 人的說話頻率基本上為300Hz~3400Hz,但是人耳朵聽覺頻率基本上為20Hz~20000Hz。 對於人類的語音訊號而言,實際處理一般經過以下步驟: 人嘴說話——>聲電轉換——>抽樣(模數轉換)——>量化(將數字訊號用適當的數值表示)——&g

Pro Android學習筆記 ActionBar1Home圖標區

ces tom 新的 方便 find rac vertica lba manifest ?? Pro Android學習筆記(四八):ActionBar(1):Home圖標區 2013年03月10日 ? 綜合 ? 共 3256字 ? 字號 小 中 大 ? 評論關閉

1Ngixn 編譯安裝 版本1.12.1

fix map img login 版本 查看 tool sbin yum 1.創建用戶和群組 groupadd nginx 創建一個用戶,不允許登陸和不創主目錄 useradd -s /sbin/nologin -g nginx -M n

雜七雜八1CentOS6.5 升級glibc至2.17版本

老闆跑測試的時候需要高版本的glibc,結果我手動改的時候不小心把系統弄崩潰了,當時備份的時候忘記備份usr文件,因此只能重灌系統,短暫重灌系統後,決定順便先把glibc升級一下,免得到時候老闆再親自升級。 1:先升級gcc 詳情可見: Elam的caffe筆記之配置篇(一

Redis1原始碼編譯安裝及入門

CentOS 6.9redis-3.0.7.tar.gz1.解壓 tar -zxvf redis-3.0.7.tar.gz 2.安裝    cd redis-3.0.7    make        編譯後在Redis原始碼目錄的src資料夾中可以找到若干個可執行程式make

Android系統原理與原始碼分析1利用Java反射技術阻止通過按鈕關閉對話方塊

本文為原創,如需轉載,請註明作者和出處,謝謝!     眾所周知,AlertDialog類用於顯示對話方塊。關於AlertDialog的基本用法在這裡就不詳細介紹了,網上有很多,讀者可以自己搜尋。那

建強的培訓課程1Android App企業級開發

Android企業級開發實戰 一.簡介 結合講師5年來一線工作經驗,打造千萬級使用者所需要的Android開發框架,以使用快速迭代的節奏和上百人的團隊協作。每一個知識點的總結,都是講師在踩過坑之後,血和淚的經驗教訓,十分有參考借鑑的意義。 本課程系列適合於從事Android開發1-2

android開發1底部導航條的實現 | navigation tab

tom git 這一 cocoapod https android中 rip launcher href 底部導航條,在iOS中叫tabbar,在android中叫bottombar或bottom navigation,是一個常用的切換頁面的導航條。 同樣,如果有良好的第三

Android1建立第一個android工程檔案

在啟動了android模擬器之後,便可以開始新建android工程了 (1)File--》New--》Android Application Project(或者File--》New--》Other--》Android--》Android Application Proje

ArcGIS for Android 10.2.91開發環境配置

官方文件 API 開發環境整合(2中方式) 一.Gradle (推薦) 使用以下程式碼示例將Maven儲存庫的URL和ArcGIS Runtime SDK for Android依賴項新增到專案中。Esri的儲存庫不是開源的,所以你必須指定一個

多媒體開發7編譯Android與iOS平臺的FFmpeg

編譯FFmpeg,一個古老的話題,但我還是介紹一遍,就當記錄。之前介紹怎麼給視訊新增水印時,就已經提到FFmpeg的編譯,並且在編譯時指定了濾鏡的功能。 但是,在手機盛行的時代,你可能更需要的是能在iOS或Android平臺上執行的FFmpeg,而對於命令列的ffmpeg,你可以在個人電腦上面使用(因為它簡

十二Hibernate中的多表操作1單向多對一

art 保存 int gen round t對象 情況 映射文件 拋出異常 由“多”方可知“一”方的信息,比如多個員工使用同一棟公寓,員工可以知道公寓的信息,而公寓無法知道員工的信息。 案例一: pojo類 public class Department {

Python學習手冊筆記1Python對象類型

python 在Python中一切皆對象,Python程序可以分解為模塊、語句、表達式及對象。如下所示:1 程序由模塊組成2 模塊包含語句3 語句包含表達式4 表達式建立並處理對象 內置對象(核心類型):1)數字:>>> 2+2 #整數加法4>>&g

Canvas入門1繪制矩形、圓、直線、曲線等基本圖形

dsm etc win cti b2c 創建 例如 .com courier 來源:http://www.ido321.com/968.html 一、Canvas的基礎知識 Canvas是HTML 5中新增的元素,專門用於繪制圖形。canvas元素就相當於一塊“畫布

python每日一類1pathlib

one pre 面向 iss open log python href reg 每天學習一個python的類(大多數都是第三方的),聚沙成金。 -----------------------------------------------------------------

【開源】OSharp框架學習系列1總體設計及系列導航

正是 html 組織 內聚性 權限 是什麽 enc 3-0 分發 OSharp是什麽?   OSharp是個快速開發框架,但不是一個大而全的包羅萬象的框架,嚴格的說,OSharp中什麽都沒有實現。與其他大而全的框架最大的不同點,就是OSharp只做抽象封裝,不做實現。依賴註

uboot命令1mmc命令

當前 .com 設備 十六進制 mci rgs 開發平臺 通過 硬件 版權聲明 更新:2017-06-07博主:LuckyAlan聯系:[email protected]/* */聲明:吃水不忘挖井人,轉載請註明出處! 1 文章介紹 今天在進行Android分區