我有此代码:
public class MyWidgetProvider : AppWidgetProvider
{
public override void OnUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
Log.Debug("WIDGET", "Updating the widget");
// Open app on click
RemoteViews views = new RemoteViews(context.PackageName, Resource.Layout.MyWidget);
Intent launchAppIntent = new Intent(context, typeof(MainActivity));
PendingIntent launchAppPendingIntent = PendingIntent.GetActivity(context, 0, launchAppIntent, PendingIntentFlags.UpdateCurrent);
views.SetOnClickPendingIntent(Resource.Id.main, launchAppPendingIntent);
appWidgetManager.UpdateAppWidget(appWidgetIds[0], views);
// Start timer
System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = 1000;
timer.Elapsed += OnTimedEvent;
timer.Enabled = true;
}
private void OnTimedEvent(object sender, ElapsedEventArgs e)
{
Log.Debug("WIDGET", "Updating status...");
new Handler(Looper.MainLooper).Post(() =>
{
//Run my code to periodically update the widget
});
}
}
我想知道为什么会发生以下情况:
我想了解这里的基本知识,因为我找不到任何相关信息。为什么计时器在我第一次将小部件放到屏幕上(不运行应用程序)时运行,而在应用程序被终止时停止运行?
是的,我已经阅读了有关小部件基础知识的几乎所有内容,然后阅读了有关使用AlarmManager,Service,JobService,JobIntentService,JobScheduler等的所有内容。但是我对带有计时器的此解决方案感兴趣,因为它非常简单,并且可以在所有当前的Android版本中使用(甚至最新的奥利奥(Oreo)。解决的办法是在屏幕关闭时停止计时器,在屏幕打开时再次启动计时器。为了节省手机电池电量。
答案 0 :(得分:0)
首先,您可以尝试使Widget应用不熟练。
小部件本身不会被杀死。该小部件最初是一个广播接收器,并且是静态的。这意味着可以随时接收已订阅的广播窗口小部件,并且将调用onReceive()方法。 无法运行窗口小部件的原因是应该为相应的服务将其杀死。如果希望窗口小部件一直运行,则应在终止服务并重新启动该服务之后。
Service是Android系统的一个组件,它类似于Activity的级别,但是他不能自己运行,只能在后台运行,并且可以与其他组件进行交互。 在Android开发过程中,每次调用startService(Intent)时,都会调用Service对象的OnStartCommand(Intent,int,int)方法,然后在onStartCommand方法中进行一些处理。
1,创建不被杀死的仆人
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
return START_STICKY_COMPATIBILITY;
//return super.onStartCommand(intent, flags, startId);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
flags = START_STICKY;
return super.onStartCommand(intent, flags, startId);
// return START_REDELIVER_INTENT;
}
@Override
public void onStart(Intent intent, int startId)
{
// again regsiter broadcast
IntentFilter localIntentFilter = new IntentFilter("android.intent.action.USER_PRESENT");
localIntentFilter.setPriority(Integer.MAX_VALUE);// max int
myReceiver searchReceiver = new myReceiver();
registerReceiver(searchReceiver, localIntentFilter);
super.onStart(intent, startId);
}
2,在服务的onDestroy()中重新启动服务。
public void onDestroy()
{
Intent localIntent = new Intent();
localIntent.setClass(this, MyService.class); // restart Service
this.startService(localIntent);
}
3,以XML创建广播和注册人
public class myReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
context.startService(new Intent(context, Google.class));
}
}
<receiver android:name=".myReceiver" >
<intent-filter android:priority="2147483647" ><!--Priority plus highest-->
<!-- when applicayion lauch invoke -->
<action android:name="android.intent.action.BOOT_COMPLETED" />
<!-- unlock invole -->
<action android:name="android.intent.action.USER_PRESENT" />
<!--context switch -->
<action android:name="android.media.RINGER_MODE_CHANGED" />
</intent-filter>
</receiver>
<service android:name=".MyService" >
注意:解锁,启动,切换场景激活广播需要添加权限,例如启动完成和手机状态。
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
================================================ ==================
第二,如果Widget应用程序不熟练,则可以收听屏幕锁定或解锁。
自定义 ScreenListener 并添加 ScreenBroadcastReceiver
private class ScreenBroadcastReceiver extends BroadcastReceiver {
private String action = null;
@Override
public void onReceive(Context context, Intent intent) {
action = intent.getAction();
if (Intent.ACTION_SCREEN_ON.equals(action)) { // screen on
mScreenStateListener.onScreenOn();
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // screen off
mScreenStateListener.onScreenOff();
} else if (Intent.ACTION_USER_PRESENT.equals(action)) { // screen unlock
mScreenStateListener.onUserPresent();
}
}
}
以便您可以使用计时器或与客户进行其他展示。
================================================ ==============================
更多信息:
这种方法不是最好的,还有很多地方可以改进,只是提出建议。
答案 1 :(得分:0)
这是我解决的方法:
public static class WidgetConsts
{
public const string DebugTag = "com.myapp.WIDGET";
public const string ActionWakeup = "com.myapp.WIDGET_WAKEUP";
public const string ActionWidgetUpdate = "android.appwidget.action.APPWIDGET_UPDATE";
public const string ActionWidgetDisabled = "android.appwidget.action.APPWIDGET_DISABLED";
}
[BroadcastReceiver]
[IntentFilter(new string[] { WidgetConsts.ActionWakeup })]
public class AlarmReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
if (intent.Action.Equals(WidgetConsts.ActionWakeup))
{
Log.Debug(WidgetConsts.DebugTag, "Wakeup alarm called");
if (MyWidgetProvider.widgetTimer == null)
{
Log.Debug(WidgetConsts.DebugTag, "Widget updating does not run, enforcing update...");
MyWidgetProvider.UpdateAppWidget(context);
}
else
{
Log.Debug(WidgetConsts.DebugTag, "Widget updating runs, no action needed");
}
}
}
}
[BroadcastReceiver]
[IntentFilter(new string[] { WidgetConsts.ActionWidgetUpdate })]
[IntentFilter(new string[] { WidgetConsts.ActionWidgetDisabled })]
[MetaData("android.appwidget.provider", Resource = "@xml/widget_info")]
public class MyWidgetProvider : AppWidgetProvider
{
public static System.Timers.Timer widgetTimer = null;
public override void OnUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
Log.Debug(WidgetConsts.DebugTag, "Updating the widget");
// Open app on click
RemoteViews views = new RemoteViews(context.PackageName, Resource.Layout.MyWidget);
Intent launchAppIntent = new Intent(context, typeof(MainActivity));
PendingIntent launchAppPendingIntent = PendingIntent.GetActivity(context, 0, launchAppIntent, PendingIntentFlags.UpdateCurrent);
views.SetOnClickPendingIntent(Resource.Id.main, launchAppPendingIntent);
appWidgetManager.UpdateAppWidget(appWidgetIds[0], views);
// set timer for updating the widget views each 5 sec
if (widgetTimer == null)
{
widgetTimer = new System.Timers.Timer();
widgetTimer.Interval = 5000;
widgetTimer.Elapsed += OnTimedEvent;
}
widgetTimer.Enabled = true;
// set alarm to wake up the app when killed, each 60 sec
// needs a fresh BroadcastReceiver because AppWidgetProvider.OnReceive is
// not virtual and overriden method in this class would not be called
AlarmManager am = (AlarmManager)context.GetSystemService(Context.AlarmService);
Intent ai = new Intent(context, typeof(AlarmReceiver));
ai.SetAction(WidgetConsts.ActionWakeup);
PendingIntent pi = PendingIntent.GetBroadcast(context, 0, ai, PendingIntentFlags.CancelCurrent);
am.SetRepeating(AlarmType.ElapsedRealtime, SystemClock.ElapsedRealtime(), 1000 * 60, pi);
}
public override void OnDisabled(Context context)
{
Log.Debug(WidgetConsts.DebugTag, "Disabling the widget");
if (widgetTimer != null)
{
Log.Debug(WidgetConsts.DebugTag, "Stopping timer");
widgetTimer.Enabled = false;
}
else
Log.Debug(WidgetConsts.DebugTag, "Timer is null");
base.OnDisabled(context);
}
private void OnTimedEvent(object sender, ElapsedEventArgs e)
{
Log.Debug(WidgetConsts.DebugTag, "Updating status...");
new Handler(Looper.MainLooper).Post(() =>
{
//Run my code to periodically update the widget
RemoteViews views = new RemoteViews(Application.Context.PackageName, Resource.Layout.MyWidget);
AppWidgetManager manager = AppWidgetManager.GetInstance(Application.Context);
ComponentName thisWidget = new ComponentName(Application.Context, Java.Lang.Class.FromType(typeof(MyWidgetProvider)));
int[] appWidgetIds = manager.GetAppWidgetIds(thisWidget);
views.SetTextViewText(Resource.Id.myText, "my text");
manager.UpdateAppWidget(appWidgetIds[0], views);
});
}
static public void UpdateAppWidget(Context context)
{
Intent intent = new Intent(context, typeof(MyWidgetProvider));
intent.SetAction(WidgetConsts.ActionWidgetUpdate);
int[] ids = AppWidgetManager.GetInstance(context).GetAppWidgetIds(new ComponentName(context, Java.Lang.Class.FromType(typeof(MyWidgetProvider))));
intent.PutExtra(AppWidgetManager.ExtraAppwidgetIds, ids);
context.SendBroadcast(intent);
}
}
优点: 简单的解决方案,适用于所有Android系统(已在3.2、4.3、8.1上测试)。 在打do模式下大于等于6.0的Android系统上,电池友好(通过GSam电池监视器测量)。不受> = 8.0中新的后台执行限制的限制。
缺点: 在不使用休眠模式的情况下,消耗低于6.0的系统上的电池,但是今天没人在乎这些。