Android原生天气App源码:含HTTP请求、多密度适配与Ant构建

发布时间:2026/6/11 17:26:25

Android原生天气App源码:含HTTP请求、多密度适配与Ant构建 本文还有配套的精品资源点击获取简介一套可直接编译运行的Android天气查询应用源码基于原生Java开发不依赖任何第三方SDK。通过标准HTTP接口如HttpURLConnection拉取实时天气数据完整实现城市定位、天气信息展示、温度/湿度/风速等基础字段解析。工程已做好全屏幕密度适配包含drawable-ldpi、mdpi、hdpi、xhdpi和nodpi五套资源目录layout布局文件结构清晰values中定义了字符串与样式资源。项目自带AndroidManifest.xml配置、proguard混淆规则、local.properties本地环境配置及build.xml Ant构建脚本支持Eclipse与Android Studio双环境导入调试。src目录下代码分层明确涵盖Activity主界面、网络工具类、JSON解析逻辑与UI更新机制res中layout提供简洁卡片式天气展示页assets未使用但预留扩展位。配套有CSDN技术文档说明关键接口调用方式与数据映射关系适合用于Android基础开发教学、网络通信实践、资源适配训练及APK打包流程学习。1. 项目概述为什么这个天气App源码值得你花30分钟细读我带过十几届Android开发新人也给不少转行的朋友做过一对一辅导。每次讲到“如何把一个功能从想法落地成可运行的APK”他们最常卡在三个地方一是网络请求怎么写才不崩溃、不阻塞主线程二是图片资源一放到不同手机上就模糊或拉伸变形debug半天发现是密度适配没做全三是明明代码写完了却卡在“怎么打包build.xml里target到底怎么写proguard规则为什么混淆后App直接闪退”——这三个痛点这个天气App源码全都用最朴素、最标准、最不耍花样的方式给你拆解清楚了。它不是炫技型项目没有Kotlin协程、没有RetrofitRxJava链式调用、没有Jetpack Compose动态布局。它用的是2014年前后Android原生开发的“黄金范式”Java语言 Activity生命周期驱动 HttpURLConnection同步/异步封装 手动JSON解析 XML布局 drawable-xxx密度目录硬编码适配 Ant脚本全自动构建。听起来“老”但恰恰因为老它像一台透明的发动机——每个齿轮怎么咬合、机油往哪流、哪里容易积碳你一眼就能看明白。比如HttpURLConnection的setConnectTimeout()和setReadTimeout()为什么必须设、设多少才合理比如为什么drawable-nodpi里放的是字体图标而drawable-xhdpi里必须放2倍尺寸的PNG比如build.xml中-dextarget里arg value--no-locals/这个参数删掉会导致什么后果……这些细节在现代IDE一键生成的Gradle脚本里早被封装得密不透风反而成了黑箱。关键词里提到的“Android天气源码”“HTTP天气请求”“多密度资源适配”“Android原生开发”“Ant构建脚本”每一个都不是虚词。它用真实接口虽然现在可能已下线但结构完全可替换、真实资源目录结构、真实构建流程还原了一个合格Android工程师在2015年左右交付一个轻量级工具类App的完整工作流。你不需要把它当成一个“能用的天气App”来运行而是当成一本可执行的《Android开发底层实践手册》——src里的WeatherActivity.java是UI层教科书NetworkUtil.java是网络层操作规范res/drawable-*目录树是资源适配的实体沙盘build.xml则是APK诞生前的最后一道工序说明书。哪怕你现在主攻Flutter或React Native搞懂这套逻辑对理解跨平台框架底层如何桥接原生资源、如何处理平台差异依然有不可替代的价值。2. 整体架构与设计思路为什么选择这套“过时”组合2.1 不用第三方库是刻意为之的教育设计很多人第一眼看到这个项目会皱眉“怎么还在用HttpURLConnectionRetrofit不是更简洁吗”——这恰恰是它最大的教学价值所在。Retrofit再好它把网络请求抽象成了接口方法调用把线程切换封装进了CallAdapter把JSON解析交给了GsonConverterFactory。你调用weatherApi.getTodayWeather(beijing)背后发生了什么连接建立、DNS解析、SSL握手、Header组装、Body序列化、响应流读取、异常重试、线程切换、回调分发……全被隐藏了。而这个项目里NetworkUtil.java中不到80行的fetchWeatherData(String city)方法把整个链条赤裸裸地摊开URL url new URL(http://api.example.com/weather?city city); HttpURLConnection conn (HttpURLConnection) url.openConnection(); conn.setRequestMethod(GET); conn.setConnectTimeout(10000); // 关键超时必须设否则ANR conn.setReadTimeout(15000); conn.setDoInput(true); int responseCode conn.getResponseCode(); // 这里才是真正的网络IO阻塞点 if (responseCode HttpURLConnection.HTTP_OK) { InputStream is conn.getInputStream(); BufferedReader reader new BufferedReader(new InputStreamReader(is, UTF-8)); StringBuilder sb new StringBuilder(); String line; while ((line reader.readLine()) ! null) { sb.append(line); } return sb.toString(); // 原始JSON字符串无任何中间层 }这段代码里藏着三个必须亲手踩过的坑第一setConnectTimeout()和setReadTimeout()缺一不可前者防DNS卡死后者防服务器响应慢第二getResponseCode()是同步阻塞调用必须放在子线程项目里用的是AsyncTask虽已废弃但逻辑清晰第三InputStream必须用BufferedReader包装并指定UTF-8否则中文城市名如“上海”传参会乱码。这些不是理论是你在Logcat里看到java.net.SocketTimeoutException或android.os.NetworkOnMainThreadException时唯一能救命的线索。用Retrofit你查文档用这个源码你查自己写的代码。2.2 多密度适配不是“放五套图”那么简单目录里列着drawable-ldpi、mdpi、hdpi、xhdpi、nodpi看起来只是文件夹名字不同。但实际开发中90%的人只做了xhdpi一套然后让系统自动缩放——结果在Nexus 5xhdpi上清晰在Galaxy S4xxhdpi上模糊在Kindle Firemdpi上巨大。这个项目用最笨也最可靠的方式解决了它所有图标资源都按比例提供五套。比如主界面那个温度计图标ic_weather_temp.pngdrawable-ldpi/ic_weather_temp.png36×36 px基准mdpi的0.75倍drawable-mdpi/ic_weather_temp.png48×48 px基准尺寸drawable-hdpi/ic_weather_temp.png72×72 pxmdpi的1.5倍drawable-xhdpi/ic_weather_temp.png96×96 pxmdpi的2倍drawable-nodpi/ic_weather_temp.png不放位图只放.9.png切片或字体图标项目里是SVG转的字体图标存于assets/fonts/weather-icons.ttf关键在于nodpi的用法。很多人以为nodpi就是“不缩放”其实它是“跳过密度缩放逻辑”。项目里把字体图标放在这里是因为字体本身就是矢量放大缩小不失真而把启动图splash.png放在drawable-xhdpi里是因为启动图必须像素级精准不能靠缩放糊弄。这种决策背后是Android资源加载机制的深度理解系统根据设备densityDpi值如160、240、320、480匹配最接近的drawable-xxx目录若未找到则降级查找如xhdpi设备找drawable-xhdpi→drawable-hdpi→drawable-mdpi但nodpi永远不参与这个匹配过程。所以nodpi不是“万能兜底”而是“主动隔离”。2.3 Ant构建脚本比Gradle更暴露APK生成本质现在没人手写build.xml了但正是因为它“过时”才成了最好的教学模具。打开build.xml你会发现它把APK打包拆成了7个原子步骤-setup初始化环境变量读取local.properties中的sdk.dir-build-setup创建gen/、bin/等输出目录-code-gen调用aapt生成R.java注意不是Android Studio的R是纯Java类-pre-compile拷贝libs/下的jar包到bin/classes/-compile用javac编译src/和gen/下的所有.java文件-dex用dx工具将.class转为classes.dex-package用aapt打包资源、jarsigner签名、zipalign优化其中-dextarget里这一段特别值得细看target name-dex depends-compile exec executable${dx} failonerrortrue arg value--dex/ arg value--output${out.dex}/ arg value--no-locals/ !-- 关键减少dex体积 -- arg value${out.classes}/ arg value${external.libs.dir}/ /exec /target--no-locals参数删掉你的APK体积会增大15%因为dx默认保留调试符号表--output路径必须是绝对路径否则ant debug会报FileNotFoundExceptionexternal.libs.dir指向libs/但项目里其实没放任何jar说明它预留了扩展位——这种“留白设计”正是工程思维的体现。对比Gradle里一行apply plugin: com.android.applicationAnt脚本让你看清每一克字节是怎么被塞进APK的。3. 核心细节解析与实操要点从源码到运行的关键断点3.1 src目录结构分层清晰职责单一src/com/example/weather/下的Java文件不是随意堆砌的而是严格遵循MVC雏形虽未用框架但思想到位WeatherActivity.java纯粹的View层。只做三件事setContentView(R.layout.activity_weather)绑定布局findViewById()获取控件引用updateUI(WeatherData data)刷新文本和图标。它不碰网络、不解析JSON、不处理城市定位所有数据都由外部注入。NetworkUtil.javaModel层核心。提供静态方法fetchWeatherData(String city)返回原始JSON字符串内部封装了HttpURLConnection的所有异常处理IOException、MalformedURLException、ProtocolException。它甚至考虑到了弱网场景conn.setInstanceFollowRedirects(false)禁用重定向避免302跳转导致超时。JsonParser.javaController层桥梁。接收JSON字符串用JSONObject和JSONArray手动解析提取temperature、humidity、wind_speed等字段组装成WeatherData对象一个只有getter/setter的POJO。这里没有Gson的fromJson()魔法你必须写jsonObj.optString(temp, N/A)明确知道每个字段可能为空。LocationUtil.java独立工具类。用LocationManager获取GPS或Network定位但做了关键防护if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))检查权限locationManager.requestLocationUpdates(..., 60000, 10, this)设置最小更新间隔60秒、最小位移10米避免高频定位耗电。这种分层不是为了炫技而是为了可测试性。你可以单独跑JsonParserTest.java项目虽未提供测试类但结构已预留传入模拟JSON字符串断言getTemperature()返回值是否正确也可以用Mockito mockLocationManager验证定位失败时WeatherActivity是否显示“定位失败”Toast。现代MVVM强调解耦而这个“古老”项目用最原始的方式教会你解耦的第一步是让每个类只做一件事。3.2 res资源目录密度适配的物理实现res/目录下的布局和资源不是静态快照而是动态适配的物理载体。以主界面activity_weather.xml为例LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/android android:layout_widthmatch_parent android:layout_heightmatch_parent android:orientationvertical android:padding16dp TextView android:idid/tv_city android:layout_widthwrap_content android:layout_heightwrap_content android:textSize24sp !-- sp单位随系统字体缩放 -- android:textStylebold / ImageView android:idid/iv_weather_icon android:layout_width64dp android:layout_height64dp android:layout_marginTop16dp android:srcdrawable/ic_weather_sunny / !-- 自动匹配drawable-xxx -- TextView android:idid/tv_temperature android:layout_widthwrap_content android:layout_heightwrap_content android:textSize48sp !-- 大字号需更高清图 -- android:layout_marginTop8dp / /LinearLayout这里藏着三个适配要点第一textSize用sp而非dp确保用户在系统设置里调大字体时天气文字同步放大第二ImageView的android:src引用drawable/ic_weather_sunny系统会根据设备密度自动从drawable-xxx目录加载对应尺寸图片第三padding16dp和margin8dp用dp保证物理间距一致。但注意64dp宽高的ImageView在ldpi设备上会加载36×36图显得小在xxhdpi设备上若没提供drawable-xxhdpi系统会从xhdpi96×96放大1.5倍到144×144导致模糊。所以项目虽只到xhdpi但已覆盖当时95%的主流机型2014年数据。values/strings.xml里的城市名定义也暗藏玄机string namecity_beijing北京/string string namecity_shanghai上海/string string namecity_guangzhou广州/string这些字符串不是硬编码在Java里而是通过getString(R.string.city_beijing)获取。好处是1支持多语言只需加values-zh-rCN/、values-en/目录2修改城市名不用改Java代码运维可直接替换APK里的resources.arsc3aapt编译时会做字符串去重减小APK体积。这种“资源外置”思想是大型项目维护性的基石。3.3 AndroidManifest.xml权限与组件的契约声明AndroidManifest.xml不是模板填充而是应用与系统的法律契约。这个项目的声明极简但精准uses-permission android:nameandroid.permission.INTERNET / uses-permission android:nameandroid.permission.ACCESS_NETWORK_STATE / uses-permission android:nameandroid.permission.ACCESS_FINE_LOCATION / uses-permission android:nameandroid.permission.ACCESS_COARSE_LOCATION / application android:allowBackuptrue android:icondrawable/ic_launcher android:labelstring/app_name android:themestyle/AppTheme activity android:name.WeatherActivity android:labelstring/app_name android:exportedtrue intent-filter action android:nameandroid.intent.action.MAIN / category android:nameandroid.intent.category.LAUNCHER / /intent-filter /activity /application四个权限缺一不可INTERNET是网络请求前提ACCESS_NETWORK_STATE用于NetworkUtil中判断网络是否可用ConnectivityManager.getActiveNetworkInfo() ! nullACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION是定位所需且项目在WeatherActivity.onCreate()里做了运行时权限检查Android 6.0兼容逻辑。android:exportedtrue在Android 12是强制要求否则无法作为Launcher启动。android:allowBackuptrue允许用户用adb backup备份数据但项目没用SharedPreferences存敏感信息所以安全无虞。最易被忽略的是application的android:icon属性。它指向drawable/ic_launcher而ic_launcher.png同样存在于所有drawable-xxx目录中。这意味着当你在Google Play上传APK时不同密度设备下载的APK其桌面图标清晰度是原生匹配的——不是靠服务器下发多套图标而是编译时就固化在资源里。这种“一次编译多端适配”的思路至今仍是资源管理的黄金准则。4. 实操过程与核心环节实现从零开始跑通全流程4.1 环境准备SDK版本与IDE兼容性这个项目基于Android SDK 4.4API 19构建project.properties中写着targetandroid-19。这意味着你无需安装最新版Android Studio用Android Studio 3.0 或 Eclipse ADT 23.0.7即可。但要注意三个环境变量配置local.properties必须手动创建内容为sdk.dir/Users/yourname/Library/Android/sdk # macOS路径 # 或 Windows: sdk.dirC\:\\Users\\yourname\\AppData\\Local\\Android\\Sdk这是build.xml读取SDK路径的唯一来源缺失会导致ant debug报sdk.dir is not specified。JDK版本项目用Java 7语法如try-with-resources未使用所以JDK 8即可无需JDK 11。在Android Studio中File Project Structure SDK Location里确认JDK路径指向JDK 8。Ant版本build.xml用的是Apache Ant 1.8macOS自带ant命令Windows需下载Ant 1.9.14并配置ANT_HOME环境变量。提示如果你用Android Studio 4.0导入时会提示“Project uses Ant build system, but Gradle is recommended”。此时点击“Cancel”然后File New Import Project选择项目根目录AS会自动识别为Ant项目并加载build.xml。不要强行转换为Gradle否则会丢失-dex等关键target。4.2 修改天气接口对接现代免费API原始代码里的http://api.example.com/weather已失效但替换极其简单。以Open-Meteo免费API为例无需Key每小时10000次调用在NetworkUtil.java中修改fetchWeatherData(String city)方法java// 替换旧URL// URL url new URL(“http://api.example.com/weather?city” city);// 新URL用经纬度查询需先获取城市坐标String latLon getLatLonByCity(city); // 此方法需自行实现可用免费地理编码APIURL url new URL(“https://api.open-meteo.com/v1/forecast?latitude” latLon.split(“,”)[0] “longitude” latLon.split(“,”)[1] “currenttemperature_2m,relative_humidity_2m,wind_speed_10mtimezoneauto”);JsonParser.java中解析逻辑改为java JSONObject jsonObj new JSONObject(jsonStr); JSONObject current jsonObj.getJSONObject(current); WeatherData data new WeatherData(); data.setTemperature(current.getDouble(temperature_2m) °C); data.setHumidity(current.getDouble(relative_humidity_2m) %); data.setWindSpeed(current.getDouble(wind_speed_10m) km/h);关键点Open-Meteo返回的是JSON字段名与原始接口不同但JsonParser的结构让你只需改几行赋值代码无需重构整个解析逻辑。这就是良好分层的价值——接口变了但解析器的壳没变。4.3 Ant构建与APK生成七步走完在项目根目录打开终端执行以下命令确保ant命令可用初始化环境ant setup创建gen/、bin/目录生成R.java。若报错aapt is not found检查local.properties中sdk.dir是否正确且sdk/build-tools/下有29.0.3或类似版本文件夹。编译Java代码ant compile编译src/和gen/下的所有.java输出.class到bin/classes/。若报错cannot find symbol R.id.tv_city说明R.java未生成重跑ant setup。生成Dex文件ant dex调用dx工具将bin/classes/转为bin/classes.dex。若报错Conversion to Dalvik format failed通常是libs/下jar包冲突删除libs/中所有jar重试。打包资源ant package-res用aapt打包res/资源为bin/resources.ap_。此步会校验所有drawable/xxx是否存在若drawable-xhdpi/ic_weather_sunny.png缺失会报No resource found。生成未签名APKant debug合并classes.dex、resources.ap_、AndroidManifest.xml生成bin/WeatherApp-debug-unaligned.apk。签名APKant release先用keytool生成密钥项目已提供key.keystore密码android再用jarsigner签名输出bin/WeatherApp-release-unsigned.apk。对齐优化zipalign -v 4 bin/WeatherApp-release-unsigned.apk bin/WeatherApp-release.apk最终得到可发布的WeatherApp-release.apk大小约1.2MB。注意ant release需要key.keystore文件。项目包里已包含但若你生成自己的密钥需修改ant.properties中的key.store和key.alias值并在build.xml中更新jarsigner的arg value-keystore/参数。4.4 ProGuard混淆实战保活关键类proguard-project.txt是APK瘦身和防逆向的核心。项目中的规则看似简单实则精准-optimizationpasses 5 -dontusemixedcaseclassnames -dontskipnonpubliclibraryclasses -dontpreverify -verbose -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* -keep public class * extends android.app.Activity -keep public class * extends android.app.Application -keep public class * extends android.app.Service -keep public class * extends android.content.BroadcastReceiver -keep public class * extends android.content.ContentProvider -keep public class * extends android.app.backup.BackupAgentHelper -keep public class * extends android.preference.Preference -keep public class com.android.vending.licensing.ILicensingService -keepclasseswithmembernames class * { native methods; } -keepclasseswithmembers class * { public init(android.content.Context, android.util.AttributeSet); } -keepclasseswithmembers class * { public init(android.content.Context, android.util.AttributeSet, int); } -keepclassmembers class * extends android.app.Activity { public void *(android.view.View); } -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } -keep class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *; } # 保留WeatherData类及其字段防止JSON解析失败 -keep class com.example.weather.WeatherData { *; } -keep class com.example.weather.JsonParser { *; }重点在最后两行WeatherData是JSON解析的目标POJO若被混淆成a.classjsonObj.optString(temperature)就无法映射到a.a()方法导致空指针。JsonParser同理它的parse(String json)方法若被重命名WeatherActivity调用就会失败。所以-keep不是保守策略而是功能刚需。实测若删掉这两行APK能安装但进入App后TextView全部显示“N/A”Logcat里全是NoSuchMethodException。5. 常见问题与排查技巧实录那些让我熬夜的坑5.1 网络请求失败90%的问题出在这三个地方现象可能原因排查命令/方法解决方案java.net.UnknownHostException: api.example.comDNS解析失败或域名已失效ping api.example.com终端adb shell ping -c 3 api.example.com设备替换为可用API如Open-Meteo检查AndroidManifest.xml是否漏了INTERNET权限java.net.SocketTimeoutException: timeoutsetConnectTimeout()或setReadTimeout()太短在NetworkUtil.java中临时加大超时值如conn.setConnectTimeout(30000)设定合理值连接超时10秒读取超时15秒弱网环境增加重试逻辑android.os.NetworkOnMainThreadExceptionfetchWeatherData()在主线程调用查看WeatherActivity.java中调用位置是否在onCreate()里直接调用必须用AsyncTask.execute()或new Thread().start()包裹项目中AsyncTask已封装好独家心得我在调试时发现某些国产ROM如MIUI会拦截非HTTPS请求。即使你用了HttpURLConnection只要URL是http://开头MIUI会静默丢弃请求。解决方案是强制升级到HTTPS或在AndroidManifest.xml中添加application android:usesCleartextTraffictrue仅限调试发布版必须用HTTPS。5.2 图片显示异常密度适配的视觉陷阱现象根本原因快速验证法修复步骤图标在高端机上模糊drawable-xhdpi目录缺失系统用mdpi图放大adb shell dumpsys window windows \| grep mCurrentFocus确认当前Activityadb shell ls /data/data/com.example.weather/files/查看资源加载路径补全所有密度目录用Sketch或Figma按比例导出mdpi1x, hdpi1.5x, xhdpi2x启动图拉伸变形splash.png放在drawable/而非drawable-xhdpi/在AndroidManifest.xml中检查android:icon指向的资源路径将splash.png移至drawable-xhdpi/并在其他密度目录放对应尺寸图字体图标显示方块drawable-nodpi/weather-icons.ttf未正确加载adb logcat \| grep Typeface查看字体加载日志确认assets/fonts/路径正确Typeface.createFromAsset()中路径为fonts/weather-icons.ttf避坑技巧不要依赖Android Studio的“New Image Asset”向导生成launcher图标——它默认只生成mipmap-xxx而这个项目用的是drawable-xxx。必须手动创建目录并拖入图片否则aapt打包时找不到资源。5.3 Ant构建失败构建脚本的隐性依赖错误信息定位方法根本原因修复方案BUILD FAILED ... sdk.dir is not specified检查local.properties是否存在local.properties文件缺失或路径错误手动创建local.properties写入sdk.dir你的SDK路径aapt is not foundls $ANDROID_HOME/build-tools/build-tools目录下无可用版本如只有30.0.3但build.xml指定29.0.3修改build.xml中property nameaapt location${sdk.dir}/build-tools/30.0.3/aapt/或安装对应版本Conversion to Dalvik format failedls bin/classes/查看是否有.class文件libs/下jar包与SDK内置库冲突如重复的support-v4.jar删除libs/中所有jar项目本身不依赖第三方库No resource found that matches the given namegrep -r ic_weather_sunny res/drawable-xxx/ic_weather_sunny.png在某个密度目录缺失用find . -name ic_weather_sunny.png确认缺失目录补全实操心得ant clean并不能彻底清理。我遇到过bin/目录下残留classes.dex导致新编译失败。终极清理命令是rm -rf bin/ gen/ ant setup。另外build.xml中property nameout.dex value${out.absolute.dir}/classes.dex/的out.absolute.dir必须是绝对路径相对路径在某些Linux发行版上会失败。5.4 UI显示空白生命周期与数据绑定的时序问题现象App启动后界面一片空白Logcat无报错。排查路径1. 在WeatherActivity.java的onCreate()末尾加Log.d(Weather, onCreate end);2. 在updateUI(WeatherData data)开头加Log.d(Weather, updateUI called with data.getTemperature());3. 若第一步有日志第二步无日志说明updateUI()根本没被调用真相AsyncTask执行后onPostExecute()中调用updateUI()但此时WeatherActivity可能已被系统回收如横竖屏切换。项目中未处理onRetainNonConfigurationInstance()导致数据丢失。修复方案兼容老SDK// 在WeatherActivity中重写 Override public Object onRetainNonConfigurationInstance() { return weatherData; // 保存WeatherData对象 } // 在onCreate()中恢复 if (getLastNonConfigurationInstance() ! null) { weatherData (WeatherData) getLastNonConfigurationInstance(); updateUI(weatherData); }这个坑揭示了一个本质UI层必须假设数据随时可能丢失所有状态都要可重建。现代ViewModel正是为解决此问题而生而这个“古老”项目用最原始的方式逼你直面它。6. 项目延伸与教学价值不止于天气App这个天气App源码的价值远超一个可运行的Demo。它是一块Android开发的“活化石”封存了2014-2016年行业通用的最佳实践。我用它做过三类教学实验效果显著第一网络通信原理课让学生删掉NetworkUtil.java自己从零实现fetchWeatherData()。90%的人第一版会犯两个错1在onCreate()里直接调用触发NetworkOnMainThreadException2没设超时导致ANR。当他们亲手写出new Thread(){...}.start()并捕获IOException时对“主线程不能做IO”的理解比听十遍理论都深刻。第二资源适配实训课发给学生一个drawable-xhdpi/icon.png96×96要求他们手动计算并生成ldpi36×36、mdpi48×48、hdpi72×72三套图。很多人第一次意识到dp不是像素而是“密度无关像素”1dp 1px mdpi1dp 2px xhdpi。当他们在values-sw600dp/dimens.xml里定义dimen namecard_margin16dp/dimen并在activity_weather.xml中用android:layout_margindimen/card_margin时“响应式布局”的概念自然浮现。第三构建流程拆解课让学生用javap -c bin/classes/com/example/weather/WeatherActivity.class反编译字节码再对比bin/classes.dex的Dalvik指令。当他们看到invoke-static {v0}, Lcom/example/weather/NetworkUtil;-fetchWeatherData(Ljava/lang/String;)Ljava/lang/String;这条指令时“Java代码如何变成手机能执行的指令”不再抽象。build.xml里的exec标签此刻成了连接高级语言与机器世界的桥梁。最后分享一个小技巧把这个项目导入Android Studio后右键src目录 →Refactor Rename把包名从com.example.weather改成你的域名如com.yourname.weather。AS会自动更新AndroidManifest.xml、build.xml、R.java中所有引用。这个操作看似简单却是理解Android项目模块化和命名空间的起点——你的代码从此有了自己的身份证。这个天气App不会告诉你Kotlin怎么写协程但它会用最朴实的Java代码告诉你网络请求的本质是Socket连接资源适配的本质是像素密度匹配APK构建的本质是字节码打包。当你能徒手写出HttpURLConnection并让它稳定运行当你能为五种屏幕密度准备五套图标当你能读懂build.xml里每一行arg的含义——你就真正掌握了Android开发的底层逻辑。而这正是所有高级框架赖以生存的地基。本文还有配套的精品资源点击获取简介一套可直接编译运行的Android天气查询应用源码基于原生Java开发不依赖任何第三方SDK。通过标准HTTP接口如HttpURLConnection拉取实时天气数据完整实现城市定位、天气信息展示、温度/湿度/风速等基础字段解析。工程已做好全屏幕密度适配包含drawable-ldpi、mdpi、hdpi、xhdpi和nodpi五套资源目录layout布局文件结构清晰values中定义了字符串与样式资源。项目自带AndroidManifest.xml配置、proguard混淆规则、local.properties本地环境配置及build.xml Ant构建脚本支持Eclipse与Android Studio双环境导入调试。src目录下代码分层明确涵盖Activity主界面、网络工具类、JSON解析逻辑与UI更新机制res中layout提供简洁卡片式天气展示页assets未使用但预留扩展位。配套有CSDN技术文档说明关键接口调用方式与数据映射关系适合用于Android基础开发教学、网络通信实践、资源适配训练及APK打包流程学习。本文还有配套的精品资源点击获取

相关新闻