You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android加密货币价格Widget开发故障排查求助

解决Android Widget加密货币实时价格不显示问题

核心问题分析

你的代码在Activity中正常,但移植到Widget后失效,主要有以下几个关键问题:

  • 多RemoteViews实例无效:你创建了views、nom、pourcentage、price四个独立的RemoteViews对象,只有最开始的views被传给AppWidgetManager更新,后续对其他实例的修改完全不会作用到Widget上。
  • 异步回调未触发Widget更新:网络请求是异步操作,回调中修改了RemoteViews,但没有调用appWidgetManager.updateAppWidget()刷新Widget,导致数据无法展示。
  • Handler循环不可靠:Widget运行在系统托管的进程中,用Handler做循环更新很容易因进程被系统回收而中断,不符合Android Widget的设计规范。
  • 字符串截取解析JSON风险高:直接用substring解析API返回的JSON,一旦接口返回结构微调就会解析失败,且无错误提示。

解决方案

1. 统一RemoteViews实例

所有UI修改都基于同一个RemoteViews对象,避免创建多个实例分散操作。

2. 用AlarmManager实现定时自动更新

Widget无法像Activity一样持续运行,必须通过系统定时任务主动触发数据拉取和更新,确保进程被回收后仍能正常工作。

3. 用JSON解析库替代字符串截取

使用JSONObject或Gson解析API返回数据,保证解析稳定性。

4. 回调后立即更新Widget

网络请求成功/失败后,立即调用AppWidgetManager更新Widget界面。

修改后的示例代码

static String serveur_url = "https://api.binance.com/api/v3/ticker/24hr?symbol=BTCBUSD";
static String serveur_urlPrice = "https://api.binance.com/api/v3/ticker/price?symbol=BTCBUSD";

// 定时更新间隔(这里设为1分钟,可根据需求调整)
private static final long UPDATE_INTERVAL = 60 * 1000;

static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
    // 只创建一个RemoteViews实例
    RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.new_app_widget);
    views.setTextViewText(R.id.appwidget_text, context.getString(R.string.appwidget_text));

    // 初始化显示加载状态
    views.setTextViewText(R.id.textView1, "加载中...");
    views.setTextViewText(R.id.pourcentage, "--%");
    views.setTextViewText(R.id.price, "--$");
    appWidgetManager.updateAppWidget(appWidgetId, views);

    // 拉取24小时数据
    RequestQueue requestQueue = Volley.newRequestQueue(context);
    StringRequest stringRequest = new StringRequest(Request.Method.GET, serveur_url,
            response -> {
                try {
                    JSONObject jsonObject = new JSONObject(response);
                    String symbol = jsonObject.getString("symbol");
                    String pourcentage1 = jsonObject.getString("priceChangePercent");

                    // 处理币种名称
                    String cleanSymbol = symbol.replace("USDT", "").replace("BUSD", "");
                    views.setTextViewText(R.id.textView1, cleanSymbol);
                    views.setTextViewText(R.id.pourcentage, pourcentage1 + "%");

                    // 更新Widget
                    appWidgetManager.updateAppWidget(appWidgetId, views);
                } catch (JSONException e) {
                    e.printStackTrace();
                    views.setTextViewText(R.id.appwidget_text, "解析错误");
                    appWidgetManager.updateAppWidget(appWidgetId, views);
                }
                requestQueue.stop();
            }, error -> {
                views.setTextViewText(R.id.appwidget_text, "网络错误");
                appWidgetManager.updateAppWidget(appWidgetId, views);
                error.printStackTrace();
                requestQueue.stop();
            });

    // 拉取当前价格
    StringRequest priceRequest = new StringRequest(Request.Method.GET, serveur_urlPrice,
            responsePrice -> {
                try {
                    JSONObject jsonObject = new JSONObject(responsePrice);
                    String price1 = jsonObject.getString("price");
                    views.setTextViewText(R.id.price, price1 + "$");
                    appWidgetManager.updateAppWidget(appWidgetId, views);
                } catch (JSONException e) {
                    e.printStackTrace();
                    views.setTextViewText(R.id.appwidget_text, "价格解析错误");
                    appWidgetManager.updateAppWidget(appWidgetId, views);
                }
                requestQueue.stop();
            }, error -> {
                views.setTextViewText(R.id.appwidget_text, "价格请求错误");
                appWidgetManager.updateAppWidget(appWidgetId, views);
                error.printStackTrace();
                requestQueue.stop();
            });

    requestQueue.add(stringRequest);
    requestQueue.add(priceRequest);
}

// 在AppWidgetProvider的onUpdate方法中启动定时任务
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    // 启动定时更新
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(context, YourWidgetProvider.class);
    intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

    // 设置重复定时任务
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), UPDATE_INTERVAL, pendingIntent);

    // 立即更新一次
    for (int appWidgetId : appWidgetIds) {
        updateAppWidget(context, appWidgetManager, appWidgetId);
    }
}

额外注意事项

  • 确保在AndroidManifest.xml中声明网络权限:<uses-permission android:name="android.permission.INTERNET" />
  • 对于Android 12及以上版本,AlarmManager的setRepeating有严格限制,可改用setExactAndAllowWhileIdle结合手动重置的方式实现定时更新
  • 避免过于频繁的更新(比如1秒一次),既浪费资源也可能被系统限制

内容的提问来源于stack exchange,提问作者Syl Fly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 09:57:38