Äú¿ÉÒÔ¾èÖú£¬Ö§³ÖÎÒÃǵĹ«ÒæÊÂÒµ¡£

1Ôª 10Ôª 50Ôª





ÈÏÖ¤Â룺  ÑéÖ¤Âë,¿´²»Çå³þ?Çëµã»÷Ë¢ÐÂÑéÖ¤Âë ±ØÌî



  ÇóÖª ÎÄÕ ÎÄ¿â Lib ÊÓÆµ iPerson ¿Î³Ì ÈÏÖ¤ ×Éѯ ¹¤¾ß ½²×ù Modeler   Code  
»áÔ±   
 
   
 
 
     
   
 ¶©ÔÄ
  ¾èÖú
ͨ¹ýÔ¤°²×°¸øMultiDex¼ÓËÙ
 
 À´Ô´£ºsegmentfault ·¢²¼ÓÚ£º 2016-12-21
  1520  次浏览      29
 

ÔÚAndroid Kikat¼°ÒÔǰµÄAndroidϵͳÉÏ£¬¹¹½¨»ò°²×°Apk»á³öÏÖ¡°65535·½·¨Êý³¬±ê¡±ÒÔ¼°¡°INSTALL_FAILED_DEXOPT¡±ÎÊÌ⣬MultiDexÊÇGoogleΪÁ˽â¾öÕâ¸öÎÊÌâÎÊÌâ¶ø¿ª·¢µÄÒ»¸öSupport¿â¡£MultiDex³öÏֵľßÌå±³¾°¡¢Ê¹Ó÷½Ê½¿ÉÒԲο¼¸øAppÆôÓà MultiDex¹¦ÄÜ£¬¶øMultiDex Support¿âµÄ¹¤×÷»úÖÆ¡¢Ô´Âë·ÖÎö¿ÉÒԲο¼MultiDex¹¤×÷Ô­Àí·ÖÎöºÍÓÅ»¯·½°¸¡£

MultiDexµÄʹÓÃËäÈ»ºÜ¼òµ¥±ã½Ý£¬µ«ÊÇÓиö±È½Ïµ°ÌÛµÄÎÊÌ⣬¾ÍÊÇÔÚAppµÚÒ»´ÎÀäÆô¶¯µÄʱºò»á²úÉúÃ÷ÏԵĿ¨¶ÙÏÖÏó¡£¾­¹ý²âÊÔºÍͳ¼Æ£¬¸ù¾ÝApk°üµÄ´óС¡¢Androidϵͳ°æ±¾µÄ²»Í¬£¬Õâ¸ö¿¨¶Ùʱ¼äÒ»°ãÊÇ2000µ½5000ºÁÃë×óÓÒ£¬¼«¶ËµÄÇé¿öÏÂÉõÖÁ¿ÉÒÔµ½20000+ºÁÃ롣ͨ¹ý֮ǰµÄ·ÖÎö£¬ÎÒÃÇÖªµÀ¾ßÌåµÄ¿¨¶Ù²úÉúÔÚMultiDex½âѹ¡¢ÓÅ»¯dexÕâÁ½¸ö¹ý³Ì£¬¶øÇÒÖ»ÔÚµÚÒ»´ÎÀäÆô¶¯µÄʱºò²Å»á´¥·¢ÕâÁ½¸ö¹ý³Ì¡£ÄÇôÓÅ»¯µÄ·½Ê½Ò²ºÜ¼òµ¥£¬ÔÚ°²×°ApkǰÏȶÔа汾µÄApk×öºÃ½âѹºÍÓÅ»¯¹¤×÷£¬¾ÍÄÜÔÚ°²×°ºóµÚÒ»´ÎÀäÆô¶¯µÄʱºò±Ü¿ªÕâÁ½¸öºÄʱµÄ¹ý³ÌÁË¡£

MultiDexÊÇÈçºÎÅжÏÊÇ·ñÐèÒªÖØÐ½âѹºÍÓÅ»¯dexµÄ

ÔÚ֮ǰµÄÕ½ÚÀïÃæ½²µ½£¬MultiDexÔÚµÚÒ»´Î×öÍê½âѹºÍÓÅ»¯dexÖ®ºó£¬»á±£Áôµ±Ç°ApkµÄһЩÐÅÏ¢£¬ÏÂÒ»´ÎÆô¶¯Ê±ºòºó¶ÁÈ¡ÕâЩÅäÖÃÐÅÏ¢ÔÙÅжÏÊÇ·ñÐèÒªÖØÐ½âѹºÍÓÅ»¯dexÎļþ¡£

Õâ¸öÅжÏÖ÷ÒªÊÇÔÚMultiDexExtractor#load(Context, ApplicationInfo, File, boolean)·½·¨Àï½øÐС£

static List<File> load
(Context context, ApplicationInfo applicationInfo, File dexDir,
boolean forceReload) throws IOException {

try {
...
if (!forceReload && !isModified(context, sourceApk, currentCrc))
{
try {
files = loadExistingExtractions(context, sourceApk, dexDir);
} catch (IOException ioe) {
...
files = performExtractions(sourceApk, dexDir);
putStoredApkInfo(context,
getTimeStamp(sourceApk), currentCrc, files.size() + 1);
}
} else {
...
files = performExtractions(sourceApk, dexDir);
putStoredApkInfo(context, getTimeStamp(sourceApk),
currentCrc, files.size() + 1);
}
}
...
return files;
}

µÚÒ»´Îµ÷ÓÃÕâ¸ö·½·¨µÄʱºò£¬forceReloadΪfalse£¬Ôò²»ÐèÒªÇ¿ÖÆÖØÐ½âѹdex¡£È»ºóµ÷ÓÃÁËisModifiedÕâ¸ö·½·¨Åжϵ±Ç°AppµÄApk°üÊÇ·ñ±»Ð޸Ĺý¡£

private static boolean isModified
(Context context, File archive, long currentCrc) {
SharedPreferences prefs = getMultiDexPreferences(context);
return (prefs.getLong
(KEY_TIME_STAMP, NO_VALUE) != getTimeStamp(archive))
|| (prefs.getLong(KEY_CRC, NO_VALUE)
!= currentCrc);
}

isModified·½·¨Ö÷ÒªÊÇÅжϵ±Ç°AppµÄApk°üµÄCRCÖµÊÇ·ñºÍÉÏÒ»´Î½âѹdexʱ¼Ç¼µÄApk°üCRCÒ»Ñù(CRCÖµ¿ÉÒÔÈÏΪÊÇÒ»¸öÏ¡ÊèµÄMD5Ëã·¨£¬ËüµÄʱ¼ä¸´ÔӶȵͺܶ࣬µ«ÊǼÆËã½á¹ûÈÝÒײúÉú³åÍ»)£¬ÒÔ¼°ApkÎļþµÄÐÞ¸Äʱ¼ä(ÎļþµÄLast Modified Time)ÊÇ·ñÒ»Ö¡£Èç¹ûÕâÁ½Ïî¶¼Ò»ÖµĻ°¾ÍÈÏΪApkÎļþûÓвúÉú±ä»¯(ûÓи²¸Ç°²×°¹ý)£¬Òò´ËÉÏÒ»´Î½âѹºÍÓÅ»¯dexµÃµ½µÄ»º´æÎļþ¿ÉÒÔ¸´Óá£

µ±È»£¬¹âApk°üûÓÐÐ޸ĹýÕâÒ»ÏîÌõ¼þ»¹²»¹»£¬½ÓÏÂÀ´µ÷ÓÃÁËÕâ¸öÅжÏÖ÷ÒªÊÇÔÚMultiDexExtractor#loadExistingExtractions(Context, File, File)¡£

private static List<File> loadExistingExtractions
(Context context, File sourceApk, File dexDir)
throws IOException {

final String extractedFilePrefix = sourceApk.getName()
+ EXTRACTED_NAME_EXT;
int totalDexNumber
= getMultiDexPreferences(context).getInt(KEY_DEX_NUMBER, 1);
final List<File> files = new ArrayList<File>(totalDexNumber);
for (int secondaryNumber = 2; secondaryNumber
<= totalDexNumber; secondaryNumber++) {
String fileName
= extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;
File extractedFile = new File(dexDir, fileName);
if (extractedFile.isFile()) {
files.add(extractedFile);
if (!verifyZipFile(extractedFile)) {
throw new IOException("Invalid ZIP file.");
}
} else {
throw new IOException("Missing extracted secondary dex file '" +
extractedFile.getPath() + "'");
}
}
return files;
}

ÕâÀïÏÈͨ¹ýSharePreference¶ÁÈ¡ÉÏÒ»´ÎMultiDex±£´æµÄApk°üµÄdexÊýÁ¿totalDexNumber£¬È»ºó°¤¸ö¼ÓÔØÔ¤¶¨µÄÎļþ·¾¶ÉϵÄdexÎļþ£¬¼ÓÔØÎļþµÄµÄͬʱ»¹Í¨¹ýverifyZipFile·½·¨ÅжÏdexÎļþµÄºÏ·¨ÐÔ¡£Èç¹ûÕâ¸ö¹ý³Ì³öÏÖÒì³£¾ÍÈÏΪ»ñÈ¡ÉÏÒ»´Î»º´æµÄdexÎļþʧ°Ü£¬ÐèÒªÖØÐ½âѹ¡£

static boolean verifyZipFile(File file) { 
try {
ZipFile zipFile = new ZipFile(file);
try {
zipFile.close();
return true;
} catch (IOException e) {
Log.w
(TAG, "Failed to close zip file: " + file.getAbsolutePath());
}
} catch (ZipException ex) {
Log.w(TAG, "File " + file.getAbsolutePath()
+ " is not a valid zip file.", ex);
} catch (IOException ex) {
Log.w(TAG,
"Got an IOException trying to open zip file: " + file.getAbsolutePath(), ex);
}
return false;
}

verifyZipFileÕâ¸ö·½·¨·Ç³£¼òµ¥£¬½âѹdexÎļþµÄʱºò£¬½âѹ³öÀ´µÄÎļþ±»±£´æ³ÉZip°ü£¬Õâ¸ö·½·¨ÕâÊǼì²é»º´æµÄdexÎļþÊÇ·ñÊÇZip°ü¡£¸Ð¾õ²»¿¿Æ×£¬ËäÈ»¼ì²éMD5Öµ±È½ÏºÄʱ²»ÊʺÏÕâÖÖÇé¾°£¬²»¹ýºÃ´õÒ²Ïñ¼ì²éApk°üµÄCRCÖµºÍÐÞ¸Äʱ¼äÒ»Ñù£¬¼ì²édex»º´æÎļþµÄCRCºÍÐÞ¸Äʱ¼ä°¡¡£²»¹ý¶ÁÈ¡SharePreferenceÅäÖÃÊÇÒ»¸öIO²Ù×÷£¬Èç¹û±£´æµÄÊýֵ̫¶àµÄ»°£¬Ò²ÊÇÓÐÔö¼ÓºÄʱºÍIOÒì³£µÄ·çÏյġ£

µ½ÕâÀïÎÒÃǵķ½°¸¾ÍÇåÎúÁË£º

ÔÚ°²×°ÐÂApkǰ£¬ÏÈ×öºÃdexµÄ½âѹºÍÓÅ»¯£¬µÃµ½dexѹËõ°ü(.zip)ÁбíºÍdexoptºóµÄodexÎļþ(.dex)ÁÐ±í¡£

°Ñdex/odexÎļþ±£´æµ½Ò»¸öÄÚ²¿´æ´¢Â·¾¶PATH_A£¬Í¬Ê±Ê¹ÓÃSP¼Ç¼Ð°汾ApkµÄCRC¡¢dexÊýÁ¿£¬ÒÔ¼°½âѹ³öÀ´µÄÿһ¸ödexµÄCRCÖµ¡£

°²×°Ð°汾Apkºó£¬Æô¶¯Ê±ÔÚÖ´ÐÐMultiDexǰ£¬°ÑPATH_A·¾¶ÉϵĻº´æÎļþÒÆ¶¯(rename)µ½MultiDexµÄ»º´æÂ·¾¶PATH_BÉÏ£¬Í¬Ê±±£´æµ±Ç°ApkµÄCRC¡¢ÐÞ¸Äʱ¼äÒÔ¼°dexÊýÁ¿µ½MultiDex¶ÔÓ¦µÄSPÅäÖÃÉÏ¡£

Ö´ÐÐÔ­ÓÐMultiDexÂß¼­£¬ÈÃMultiDexÒÔΪ֮ǰÒѾ­×ö¹ý½âѹºÍÓÅ»¯dex¹¤×÷£¬´Ó¶øÈÆ¿ªµÚÒ»´ÎMultiDexʱºòµÄºÄʱ¡£

µÚÒ»´Î³É¹¦Æô¶¯ÐÂApkºó£¬¶Ôdex½øÐÐУÑ鹤×÷£¬Èç¹ûУÑéʧ°ÜÔòÇå³ýdex»º´æ£¬Ç¿ÖÆÈÃAppÔÚÏÂÒ»´ÎÆô¶¯µÄʱºòÔÙÖ´ÐÐÒ»±éMultiDex¡£

Ô¤½âѹ(PreMultiDex)ÏêϸµÄÁ÷³Ìͼ

×¢£º

Á÷³ÌͼµÄÂÌÉ«²¿·ÖΪÎļþËø(FileLock)²Ù×÷£¬Ö÷ÒªÊÇΪÁË¶à½ø³Ìͬ²½¡£

ºìÉ«²¿·ÖΪºÄʱµÄ²Ù×÷¡£

Dex·¾¶ÎªMultiDex¹ý³ÌÖÐÓÃÓÚ´æ´¢½âѹ³öÀ´µÄdexÎļþµÄ·¾¶(/data/data/<package>/code_cache)¡£

PreDex·¾¶Îª´æ´¢Ô¤½âѹµÃµ½µÄ»º´æÎļþµÄÄÚ²¿Â·¾¶(/data/data/<package>/code_cache_pre)¡£

MultiDex´ÓApk°ü½âѹ³öÀ´µÄdexÎļþ»á±»Ñ¹Ëõ³ÉZip°ü(.zip)£¬¶øÖ´ÐÐdexopt²Ù×÷ºóÉú³ÉµÄodexÎļþÎļþÃûΪ.dex£¬ÕâÁ½¸öÈÝÒ׸ã»ì¡£

°²×°ÐÂApkǰÏȽâѹºÍÓÅ»¯dex

Õâ¸ö»·½Ú±ØÐëÔÚÉý¼¶Apkǰ£¬Óɾɰ汾µÄApk½øÐУ¬Ò²¾ÍÊÇÒªÇóAppÓµÓÐ×ÔÖ÷¸üеÄÂß¼­¡£

µÚÒ»´ÎÔËÐÐÐÂApkʱ£¬Òƶ¯Ô¤ÏȰ²×°ºÃµÄdexÎļþ

´Ó¾É°æµÄApk¸²¸Ç°²×°ÐµÄApkºó£¬µÚÒ»´ÎÔËÐÐAppʱMultiDexÖ÷ÒªµÄºÄʱ¹ý³Ì¡£ÕâʱÐèÒª°ÑÔھɰ汾ApkÔ¤°²×°µÃµ½µÄdex»º´æÎļþÒÆ¶¯µ½MultiDexʹÓõĴ洢·¾¶ÉÏ¡£

µÚÒ»´ÎÔËÐÐÐÂApkºó£¬¼ì²édexÎļþÊÇ·ñÕýÈ·

Ô­ÓеÄMultiDex£¬dexÎļþʱͬ²½´ÓApk°üÀï½âѹ³öÀ´µÄ£¬ËùÒÔ²»´æÔÚdexÎļþºÍApk°æ±¾¶Ô²»ÉϵÄÎÊÌâ¡£¶øPreMultiDexµÄ·½°¸µÄÒ»¸öÎÊÌâuiÊÇ£¬½âѹdexÎļþºÍʹÓÃdexÎļþÕâÁ½¸ö¹ý³ÌÊÇ·Ö¿ªµÄ£¬ÎÞÂÛ°æ±¾¿ØÖÆ×öµÃÔÙ¾«È·£¬ÀíÂÛÉÏÒ²´æÔÚ°æ±¾³ö´íµÄÎÊÌâ(±ÈÈç´ÓA°æ±¾½âѹµÃµ½ÁËdexÎļþ£¬¶øÓû§È´Ñ¡Ôñ¸²¸Ç°²×°ÁËB°æ±¾£¬ÕâʱºòÓÉÓÚ´úÂëÂß¼­µÄ²»ÑϽ÷µ¼ÖÂB°æ±¾µÄApkʹÓÃÁËA°æ±¾½âѹ³öÀ´µÄdexÎļþ)¡£Èç¹ûÏëҪȷ±£dexÎļþµÄÕýÈ·ÐÔ£¬ÐèÒª¶ÔApk°üÀïÃæµÄdexÎļþºÍ½âѹ³öÀ´µÄdexÎļþ×öÒ»ÏÂMD5ֵУÑ飬¶øÕâ¸ö¹ý³Ì±È½ÏºÄʱ£¬²»ÊʺÏÔÚAppÆô¶¯µÄʱºò×ö£¬²»È»PreMultiDex¾ÍʧȥÁËÒâÒå¡£Òò´Ë£¬ÐèÒªÔÚµÚÒ»´ÎÔËÐÐÐÂApkºó£¬Æô¶¯dexµÄУÑ鹤×÷£¬ÔÚWorkerÏ̶߳Ôdex½øÐÐУÑ飬Èç¹ûУÑéʧ°ÜÔòÇå³ýdex»º´æ£¬Ç¿ÖÆÈÃAppÔÚÏÂÒ»´ÎÆô¶¯µÄʱºòÔÙÖ´ÐÐÒ»±éMultiDex¡£

»Ö¸´MultiDex

ÔÚMultiDexУÑéʧ°Üºó£¬ÐèÒªÇå¿ÕMultiDexµÄ»º´æÎļþ£¬½ûÓÃPreMultiDex¹¦ÄÜ£¬²¢ÇÒÇ¿ÖÆÈÃAppÔÚÏÂÒ»´ÎÆô¶¯µÄʱºòÔÙÖ´ÐÐÒ»±éMultiDex¡£

һЩСϸ½Ú

dexÎļþ¡¢odexÎļþ?

dexÎļþÊÇAndroidÐéÄâ»úʹÓõĿÉÖ´ÐÐÎļþ(´ÓJavaÀà±àÒëµÃµ½)£¬Ï൱ÓÚJVMÐéÄâ»úÓõÄclassÎļþ¡£µ«ÊÇÓëclassÎļþ²»Í¬£¬Androidϵͳ²¢²»ÄÜÖ±½ÓʹÓÃdexÎļþ£¬ÐèÒªÏÈʹÓÃdexopt¹¤¾ß¶ÔdexÎļþ½øÐÐÒ»´ÎÓÅ»¯¹¤×÷(Optimize)£¬ÓÅ»¯µÃµ½µÄodexÎļþ²ÅÄܱ»ÐéÄâ»ú¼ÓÔØ¡£²»Í¬µÄAndroidÉ豸ÐèÒª²»Í¬¸ñʽµÄodexÎļþ£¬ËùÒÔÕâ¸ö¹ý³ÌÖ»ÄÜÔÚAndroidÉ豸ÉϽøÐУ¬¶ø²»ÄÜÔÚ¹¹½¨ApkµÄʱºò¾Í´¦ÀíºÃ¡£

dexÎļþÔÚApk°üÀïµÄÎļþºó׺ÃûÊÇ.dex£¬MultiDex´ÓApk°üÀï½âѹ³ödexÎļþºó»áѹËõ³ÉZip°ü£¬Îļþºó׺ÃûÊÇ.zip¡£¶ÔdexÎļþ½øÐÐdexopt²Ù×÷ºó£¬»áÉú³ÉÏàͬÎļþÃûµÄodexÎļþ£¬ºó׺ÃûÊÇ.dex£¬odexÎļþ»á±ÈdexÎļþ´óÐí¶à£¬²»Òª¸ã»ìÕâЩÎļþ¡£

ÖÁÓÚΪʲôMultiDex½âѹdexÎļþʱ»á½øÐÐѹËõ¹¤×÷£¬¿ÉÄÜÊÇÒòΪѹËõºóµÄѹËõ°ü»áÕ¼ÓñȽÏСµÄÄÚ²¿´æ´¢¿Õ¼ä£¬ÒòΪMultiDex±¾À´¾ÍÊǸø¾É°æ±¾µÄAndroidϵͳʹÓã¬Ò»Ð©ÔçÆÚµÄAndroidÉ豸ӵÓеÄÄÚ²¿´æ´¢¿Õ¼ä·Ç³£ÓÐÏÞ£¬¶øÕâЩdexÎļþ¶ÔÓÚAppµÄÔËÐÐʱ±ØÐëµÄ£¬ËùÒÔ²ÅÐèÒª¾¡Á¿Ñ¹ËõdexµÄÌå»ý¡£Ñ¹Ëõ¹ý³Ì»áÓÐÃ÷ÏԵĺÄʱ£¬¾­¹ý²âÊÔ£¬Èç¹û²»½øÐÐѹËõ£¬Ö±½Ó´ÓApkÀï½âѹdexÎļþ£¬ÔòMultiDex¹ý³Ì»áÓдóÔ¼1/3µÄ¼ÓËÙЧ¹û¡£

dexopt»º´æ

MultiDexÆäʵ²¢Ã»ÓпÌÒâ±£ÁôdexoptºóµÄ»º´æ£¬Èç¹ûÖ»±£ÁôdexÎļþ£¬¶ø²»±£ÁôodexÎļþ£¬ÄÇôÏÂÒ»´ÎÆô¶¯Ö´ÐÐMultiDexµÄʱºò£¬²»ÐèÒªÖØÐ½âѹdexÎļþ£¬µ«ÊÇÒÀÈ»ÐèÒªdexopt²¢²úÉúodexÎļþ£¬Õâ¸ö¹ý³Ì´ó¸Å»áÕ¼ÓÃMultiDex×ܺÄʱµÄÒ»°ã×óÓÒ¡£Èç¹ûodexÎļþ´æÔÚ£¬µ«ÊÇÒѾ­Ëð»µÁË£¬»òÕßÊÇÒ»¸ö·Ç·¨µÄodexÎļþ£¬ÒÀÈ»»á´¥·¢dexopt¹¤×÷¡£Ò²¾ÍÊÇ˵£¬¼ÓÔØdexÎļþ²¢´´½¨DexFile¶ÔÏóµÄʱºò£¬Androidϵͳ»áÅжÏodexµÄ»º´æ£¬ÒÔ¼°»º´æÎļþÊÇ·ñÕýÈ·£¬¾ßÌå¹ý³ÌÔÚdalvik_system_DexFile.cppÀïʵÏÖ£¬ÓÐÐËȤµÄͬѧ¿ÉÒÔÕÒÕÒdexÎļþ½á¹¹·ÖÎöµÄÎÄÕ£¬ÕâÀï¾Í²»ÍÚ¿ÓÁË¡£

¹ØÓÚdexÎļþУÑé

Æäʵ£¬Èç¹ûdexÎļþºÍApkµÄ°æ±¾¶Ô²»Éϵϰ£¬Ò»°ãÔÚÆô¶¯AppµÄʱºò¾Í»á³öÏÖClassNotFoundÒì³£¶øµ¼ÖÂApp±ÀÀ££¬½Ó×ÅÔÙ´ÎÆô¶¯ÓÉÓÚûÓÐÖØÐÂMultiDexÒ²»á¼ÌÐø±ÀÀ£¡£¶ø±ÀÀ£µÄʱºò£¬¿ÉÄÜApp±ÀÀ£Éϱ¨ÏµÍ³»¹Ã»À´µÃ¼°³õʼ»¯£¬ËùÒÔûÓа취·¢ÏÖ±ÀÀ£µÄÎÊÌâ¡£

ΪÁË·ÀÖ¹ÕâÖÖÎÊÌ⣬¿ÉÒÔ¿ª·¢Ò»¸ö»Ö¸´Ä£Ê½»òÕß°²È«Ä£Ê½µÄ¹¦ÄÜ£¬µ±App³öÏÖÁ¬ÐøµÄ±ÀÀ£µÄʱºò£¬»á½øÈë»Ö¸´Ä£Ê½µÄ״̬£¬Çå¿ÕһЩ¿ÉÄܵ¼ÖÂÒì³£µÄÊý¾Ý(±ÈÈçPreMultiDexµÄ»º´æ)£¬ÕâÑù¾ÍÄܱÜÃâAppÒòΪÁ¬Ðø±ÀÀ£¶ø²»ÄÜʹÓá£ÖÁÓÚÔõôʵÏÖ»Ö¸´£¬ÕâÒѾ­ÊÇÁíÒ»¸öÁìÓòµÄ¹¦ÄÜÁË£¬ÕâÀï²»ÔÙÕ¹¿ª¡£

 

   
1520 ´Îä¯ÀÀ       29
 
Ïà¹ØÎÄÕÂ

ÊÖ»úÈí¼þ²âÊÔÓÃÀýÉè¼ÆÊµ¼ù
ÊÖ»ú¿Í»§¶ËUI²âÊÔ·ÖÎö
iPhoneÏûÏ¢ÍÆËÍ»úÖÆÊµÏÖÓë̽ÌÖ
AndroidÊÖ»ú¿ª·¢£¨Ò»£©
 
Ïà¹ØÎĵµ

Android_UI¹Ù·½Éè¼Æ½Ì³Ì
ÊÖ»ú¿ª·¢Æ½Ì¨½éÉÜ
androidÅÄÕÕ¼°ÉÏ´«¹¦ÄÜ
Android½²ÒåÖÇÄÜÊÖ»ú¿ª·¢
Ïà¹Ø¿Î³Ì

Android¸ß¼¶Òƶ¯Ó¦ÓóÌÐò
Androidϵͳ¿ª·¢
AndroidÓ¦Óÿª·¢
ÊÖ»úÈí¼þ²âÊÔ
×îл¼Æ»®
DeepSeekÔÚÈí¼þ²âÊÔÓ¦ÓÃʵ¼ù 4-12[ÔÚÏß]
DeepSeek´óÄ£ÐÍÓ¦Óÿª·¢Êµ¼ù 4-19[ÔÚÏß]
UAF¼Ü¹¹ÌåϵÓëʵ¼ù 4-11[±±¾©]
AIÖÇÄÜ»¯Èí¼þ²âÊÔ·½·¨Óëʵ¼ù 5-23[ÉϺ£]
»ùÓÚ UML ºÍEA½øÐзÖÎöÉè¼Æ 4-26[±±¾©]
ÒµÎñ¼Ü¹¹Éè¼ÆÓ뽨ģ 4-18[±±¾©]

androidÈË»ú½çÃæÖ¸ÄÏ
AndroidÊÖ»ú¿ª·¢£¨Ò»£©
AndroidÊÖ»ú¿ª·¢£¨¶þ£©
AndroidÊÖ»ú¿ª·¢£¨Èý£©
AndroidÊÖ»ú¿ª·¢£¨ËÄ£©
iPhoneÏûÏ¢ÍÆËÍ»úÖÆÊµÏÖ̽ÌÖ
ÊÖ»úÈí¼þ²âÊÔÓÃÀýÉè¼ÆÊµ¼ù
ÊÖ»ú¿Í»§¶ËUI²âÊÔ·ÖÎö
ÊÖ»úÈí¼þ×Ô¶¯»¯²âÊÔÑо¿±¨¸æ


Android¸ß¼¶Òƶ¯Ó¦ÓóÌÐò
AndroidÓ¦Óÿª·¢
Androidϵͳ¿ª·¢
ÊÖ»úÈí¼þ²âÊÔ
ǶÈëʽÈí¼þ²âÊÔ
AndroidÈí¡¢Ó²¡¢ÔÆÕûºÏ


ÁìÏÈIT¹«Ë¾ android¿ª·¢Æ½Ì¨×î¼Ñʵ¼ù
±±¾© Android¿ª·¢¼¼Êõ½ø½×
ijÐÂÄÜÔ´ÁìÓòÆóÒµ Android¿ª·¢¼¼Êõ
ijº½Ì칫˾ Android¡¢IOSÓ¦ÓÃÈí¼þ¿ª·¢
°¢¶û¿¨ÌØ LinuxÄÚºËÇý¶¯
°¬Ä¬Éú ǶÈëʽÈí¼þ¼Ü¹¹Éè¼Æ
Î÷ÃÅ×Ó Ç¶Èëʽ¼Ü¹¹Éè¼Æ