
在开发依赖网络的Android应用时,处理无网络连接的情况至关重要,否则可能导致应用崩溃或用户体验下降。本节将介绍如何实现一个网络检查工具,以预防因网络中断引发的问题。
为了避免在无网络连接时应用崩溃,我们可以创建一个独立的工具类来检测设备的网络状态。
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.provider.Settings;
import android.widget.Toast;
public class NetWorkChecker {
// 静态变量用于存储网络信息,避免每次检查都重新获取
private static NetworkInfo wifiInfo, mobileInfo;
/**
* 检查设备是否有可用的网络连接。
* 如果没有网络,会弹出一个对话框提示用户开启网络设置。
*
* @param context 当前应用的上下文。
* @return 如果有网络连接则返回true,否则返回false。
*/
public static Boolean check(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
try {
// 获取Wi-Fi和移动网络的NetworkInfo
wifiInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
mobileInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
} catch (Exception e) {
// 捕获可能发生的异常,例如权限问题或API版本不兼容
e.printStackTrace();
// 发生异常时,默认认为无网络连接
return false;
}
// 检查Wi-Fi是否连接且可用
if (wifiInfo != null && wifiInfo.isConnected() && wifiInfo.isAvailable()) {
return true;
}
// 检查移动数据是否连接且可用
else if (mobileInfo != null && mobileInfo.isAvailable() && mobileInfo.isConnected()) {
return true;
}
// 如果两者都不可用,则提示用户并返回false
else {
displayMobileDataSettingsDialog(context, "无网络连接", "当前设备没有可用的网络连接,请检查网络设置。");
return false;
}
}
/**
* 显示一个对话框,提示用户无网络连接,并提供跳转到网络设置的选项。
*
* @param context 当前应用的上下文。
* @param title 对话框的标题。
* @param message 对话框显示的消息。
* @return 创建的AlertDialog实例。
*/
public static AlertDialog displayMobileDataSettingsDialog(final Context context, String title, String message) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(title);
builder.setMessage(message);
builder.setCancelable(false); // 用户必须点击按钮才能关闭对话框
builder.setPositiveButton("设置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 跳转到移动数据设置界面
Intent intent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
context.startActivity(intent);
}
});
// 可以添加一个“取消”按钮,如果应用允许用户不开启网络继续操作
// builder.setNegativeButton("取消", (dialog, which) -> dialog.dismiss());
builder.show();
return builder.create();
}
}在进行任何网络请求(例如在 refresh() 方法中)之前,都应该先调用 NetWorkChecker.check() 方法。
// 在 MapActivity 的 refresh() 方法或其他需要网络的操作开始处调用
private void refresh() {
// 1. 在执行网络请求前,先检查网络连接
if (!NetWorkChecker.check(this)) {
// 如果没有网络,则不执行后续的网络请求操作
isRefreshLoced = false; // 释放刷新锁
// 可以显示一个Toast提示用户
Toast.makeText(this, "无网络连接,无法刷新地图数据。", Toast.LENGTH_SHORT).show();
// 也可以隐藏加载视图,显示无数据视图
loading_layout.setVisibility(View.GONE);
nodata_layout.setVisibility(View.VISIBLE);
return;
}
if (isRefreshLoced)
return;
isRefreshLoced = true;
lastRefreshTime = System.currentTimeMillis();
// 后续的网络请求代码...
// API.getApiInterface(this).getDevices(...)
}为了能够检测网络状态,需要在 AndroidManifest.xml 中添加以下权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
用户希望地图或标记能够每秒刷新一次,以提供近乎实时的位置更新。现有代码中 TimerTask 每秒执行一次,但实际的数据刷新 refresh() 却每10秒才调用一次。
要实现每秒刷新一次 refresh() 方法,只需修改 onResume 中的条件判断:
@Override
protected void onResume() {
super.onResume();
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
// 更新计时器显示,这部分可以继续每秒更新
float timeleft = 10 - Math.round(System.currentTimeMillis() - lastRefreshTime) / 1000f;
if (timeleft < 0) timeleft = 0;
updatetimer.setText(String.format("%.0f", timeleft));
// 将刷新间隔从10秒改为1秒
if (System.currentTimeMillis() - lastRefreshTime >= 1 * 1000) // 核心修改点
if (map != null)
refresh();
}
});
}
}, 0, 1000); // TimerTask 仍然每秒运行一次
}重要提示: 将 refresh() 方法的调用频率提高到每秒一次,可能会导致严重的性能问题和高昂的数据消耗,原因如下:
优化建议:
分离数据获取与UI更新:
数据缓存:
增量更新:
当前的 MapActivity 存在加载缓慢的问题,主要原因在于 refresh() 方法中的低效操作。以下是针对性优化方案:
代码中在 AsyncTask 的 doInBackground 中通过 BitmapFactory.decodeStream(new URL(...)) 同步下载并处理每个设备图标,这是最大的性能瓶颈。
优化方案:
使用图片加载库: 强烈推荐使用 Glide 或 Picasso 等成熟的图片加载库。它们提供了:
使用 Glide 示例(假设已添加依赖):
// 在 doInBackground 或 onPostExecute 中,替换原来的图片下载逻辑
// ...
// String server_base = (String) DataSaver.getInstance(MapActivity.this).load("server_base");
// String imageUrl = server_base + mapIcon.path;
// 使用Glide加载图片并转换为BitmapDescriptor
// 注意:Glide的同步加载需要在后台线程进行
Bitmap bmp = Glide.with(MapActivity.this)
.asBitmap()
.load(imageUrl)
.submit(dp100, dp100) // 预加载到指定尺寸
.get(); // 同步获取Bitmap,确保在doInBackground完成
if (bmp != null) {
// ... (根据需要进行额外缩放,但Glide已经做了初步缩放)
// int dstWidth = (int) (srcWidth * ratio);
// int dstHeight = (int) (srcHeight * ratio);
// bmp = Bitmap.createScaledBitmap(bmp, dstWidth, dstHeight, true); // 如果Glide的尺寸不够,再手动缩放
MarkerOptions m = new MarkerOptions();
m.position(new LatLng(item.lat, item.lng));
m.icon(BitmapDescriptorFactory.fromBitmap(bmp)); // 直接使用Glide加载的Bitmap
markers.add(m);
deviceIds.add(item.id);
}
// ...使用 Handler 替代 Timer: 在Android中,Handler.postDelayed() 通常是比 java.util.Timer 更推荐的周期性任务执行方式,因为它与UI线程的生命周期结合更紧密,且避免了 Timer 可能导致的内存泄漏问题。
// 示例:使用Handler实现每秒刷新
private Handler handler = new Handler(Looper.getMainLooper());
private Runnable refreshRunnable = new Runnable() {
@Override
public void run() {
// 更新计时器显示
float timeleft = 10 - Math.round(System.currentTimeMillis() - lastRefreshTime) / 1000f;
if (timeleft < 0) timeleft = 0;
updatetimer.setText(String.format("%.0f", timeleft));
// 调用刷新方法
if (System.currentTimeMillis() - lastRefreshTime >= 1 * 1000) { // 刷新间隔
if (map != null) {
refresh();
}
}
// 再次调度自己,实现循环
handler.postDelayed(this, 1000); // 每1秒执行一次
}
};
@Override
protected void onResume() {
super.onResume();
handler.post(refreshRunnable); // 首次启动
}
@Override
protected void onPause() {
super.onPause();
handler.removeCallbacks(refreshRunnable); // 停止调度
}Android Profiler: 使用Android Studio的Profiler工具分析CPU、内存和网络使用情况,精确找出性能瓶颈。
构建一个高性能、健壮的Android地图应用需要细致的网络处理和性能优化。通过实现网络状态检查,可以有效避免无网络环境下的应用崩溃。对于地图的实时刷新需求,务必权衡实时性与性能开销,优先采用轻量级、增量式的更新策略,并利用图片加载库、异步编程和数据缓存等技术来优化数据获取和UI渲染过程。定期使用性能分析工具,将有助于持续改进应用的响应速度和用户体验。
以上就是增强Android地图应用的健壮性与性能优化指南的详细内容,更多请关注php中文网其它相关文章!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号