Widget定时刷新,导致launcher卡机

本文探讨了Android小部件(Widget)定时刷新可能导致Launcher应用程序卡顿的三种常见方法:Timer、Alarm和Handler,并指出这些方法在更新时会与Launcher运行在同一线程,造成卡顿。为解决此问题,作者提出通过启动Service并在新线程中执行更新语句来避免影响Launcher的滑屏效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近公司要编写一个定时刷新的widget,到网上找了下,发现好多人都已经总结出来了,无非都是以下几种方法

第一种:Timer

Timer这种方式最为常见,起个定时刷新的任务,不用的时候cancel掉,置为空即可。

示例代码:

Timer timer = new Timer( );

TimerTask task = new TimerTask( ) {

       public void run ( ) {

       Message message = new Message( );

       message.what = 1;

       handler.sendMessage(message);

   }

};

final Handler handler = new Handler( ) {

   public void handleMessage(Message msg) {

      switch (msg.what) {

          case 1:

               log.e("Timer","Timer");

               update( );

               break;

}

第二种:Alarm

示例代码:

开始计时

Intent intent = new Intent(widgetUpdate);

refreshIntent = PendingIntent.getBroadcast(pContext, 0, intent, 0);

alarm = (AlarmManager)pContext.getSystemService(Context.ALARM_SERVICE);

alarm.setRepeating(AlarmManager.RTC, 0, 1000, refreshIntent);//每秒刷新1次

停止计时

if (alarm!=null) {

    alarm.cancel(refreshIntent);

    refreshIntent.cancel();

    refreshIntent = null;

 alarm = null;

}


第三种:handler

private Handler handler = new Handler();

    private Runnable runnable = new Runnable() {

         public void run () {

             update();

         handler.postDelayed(this,1000); 

      }

    };


开始计时

handler.removeCallbacks(runnable);

handler.postDelayed(runnable,1000); 

停止计时

handler.removeCallbacks(runnable);

当时我也没多想,拷过去直接使用,但是测试了才知道,真正用了这几种方法去更新都会导致launcher有卡壳的现象,而且更新时在执行appWidgetManager.updateAppWidget(thisWidget, rv);这条语句是会随着时间的增加,执行的时间也增加。由开始的几毫秒到几百毫秒。

后来想想这种卡壳会不会是应该更新语句跟launcher在同一线程中的原因呢,按这个思路我启动了一个Service,然后在启动一个线程,让更新语句在这个线程中执行。貌似launcher的滑屏效果还真得到解决了。

以下是service类的源码:


package com.fxun.app.suggest.main;

import java.util.LinkedList;
import java.util.Queue;

import com.fxun.app.suggest.R;
import com.fxun.app.suggest.widget.FxunAppWidgetProvider;
import com.fxun.app.suggest.widget.FxunApplication;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.IBinder;
import android.os.SystemClock;
import android.text.format.Time;
import android.widget.RemoteViews;

public class UpdateService extends Service implements Runnable {

	private static boolean sThreadRunning = false;

	private static Object sLock = new Object();

	public static final String ALARM_UPDATA_ACTION = "com.fxun.app.suggest.action.APPWIDGET_ALARM_UPDATE";

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}


	@Override
	public void onCreate() {
		super.onCreate();
	}

	@Override
	public void onStart(Intent intent, int startId) {
		super.onStart(intent, startId);
		MLog.d(this, "onStart startId = " + startId);
		synchronized (sLock) {
			if (!sThreadRunning) {
				sThreadRunning = true;
				new Thread(this).start();
			}
		}
	}

	@Override
	public void run() {

		updataMiddleLayout();
		
		startAlarmFlash();

	}
	
	/**
	 * 启动一个定时器,定时器只启动一次,下次启动服务时有会再次启动,从而实现循环,所以只用了am.set()做处理
	 */
	public void startAlarmFlash() {
		FxunApplication fxunApplication = (FxunApplication) this
				.getApplicationContext();
		if (fxunApplication.mPageInfos != null
				&& fxunApplication.mPageInfos.size() > 0) {
			long flashTime = 0;
			if (fxunApplication.mPageInfos.get(0).flashTime != null) {
				flashTime = Long
						.parseLong(fxunApplication.mPageInfos.get(0).flashTime);
			}
			MLog.d(this, "startAlarmFlash start = " + flashTime);
			AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
			Intent intent = new Intent(this, UpdateService.class);
			intent.setAction(ALARM_UPDATA_ACTION);
			PendingIntent pi = PendingIntent.getService(this, 1, intent,
					PendingIntent.FLAG_UPDATE_CURRENT);

			am.set(AlarmManager.RTC_WAKEUP,System.currentTimeMillis()+flashTime, pi);
			MLog.d(this, "startAlarmFlash end = " + flashTime);
		}

	}
	/**
	 * 取消定时器,这个方法可以在provide里面的onDisabled里面执行
	 * @param context
	 */
	public static void cancelAlarm(Context context){
		AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
		Intent intent = new Intent(context, UpdateService.class);
		intent.setAction(ALARM_UPDATA_ACTION);
		PendingIntent pi = PendingIntent.getService(context, 1, intent,
				PendingIntent.FLAG_UPDATE_CURRENT);
		am.cancel(pi);
		pi.cancel();
		pi=null;
		
	}
	/*
	 * 此方法是widget界面更新
	 */
	public void updataMiddleLayout() {
		FxunApplication fxunApplication = (FxunApplication) this
				.getApplicationContext();
		
		if (fxunApplication.mPageInfos != null
				&& fxunApplication.mPageInfos.size() > 0) {
			if ((++fxunApplication.pageInfoIndex) >= fxunApplication.mPageInfos
					.size()) {
				fxunApplication.pageInfoIndex = 0;
			}
			AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
			PageInfo pageInfo=fxunApplication.mPageInfos.get(fxunApplication.pageInfoIndex);
			MLog.d(this, "updataMiddleLayout pageInfo=" + pageInfo);
			long t = System.currentTimeMillis();
			// MLog.d(this,
			// "updataMiddleLayout pageInfo.bannerBitmapUri="+pageInfo.bannerBitmapUri);
			RemoteViews rv = new RemoteViews(getPackageName(),
					R.layout.widget_layout);
			rv.setImageViewUri(R.id.banner, pageInfo.bannerBitmapUri);
			Intent intent = new Intent();
			intent.setClassName(FxunAppWidgetProvider.appApkPackage,
					FxunAppWidgetProvider.appApkClass);
			intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
			intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);// 只启动一个
			Bundle extras = new Bundle();
			extras.putString("action", pageInfo.bannerParam);
			intent.putExtras(extras);
			PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
					intent, PendingIntent.FLAG_UPDATE_CURRENT);
			rv.setOnClickPendingIntent(R.id.banner, pendingIntent);
			int id = R.id.app_1_icon;
			int count = pageInfo.itemInfos.size();
			IconInfo iconInfo;
			for (int i = 0; i < count; i++) {
				iconInfo = pageInfo.itemInfos.get(i);
				intent.setClassName(FxunAppWidgetProvider.appApkPackage,
						FxunAppWidgetProvider.appApkClass);
				intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);// 只启动一个
				extras = new Bundle();
				extras.putString("action", iconInfo.paramString);
				intent.putExtras(extras);
				pendingIntent = PendingIntent.getActivity(this, 0, intent,
						PendingIntent.FLAG_UPDATE_CURRENT);

				rv.setOnClickPendingIntent(id, pendingIntent);
				// MLog.d(this,
				// "updataMiddleLayout iconInfo.iconBitmaUri="+iconInfo.iconBitmaUri);
				rv.setImageViewUri(id++, iconInfo.iconBitmaUri);
				// MLog.d(this,
				// "updataMiddleLayout iconInfo.titleString="+iconInfo.titleString);
				rv.setTextViewText(id++, iconInfo.titleString);

			}
			MLog.d(this,
					"updataMiddleLayout time0="
							+ (System.currentTimeMillis() - t));
			MLog.d(this,
					"updataMiddleLayout time1="
							+ (System.currentTimeMillis() - t) );
			appWidgetManager.updateAppWidget(new ComponentName(
					this, FxunAppWidgetProvider.class), rv);
			MLog.d(this,
					"updataMiddleLayout time2="
							+ (System.currentTimeMillis() - t));
			sThreadRunning = false;
		}
	}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值