Why don’t widgets have setAnimation and other methods for setting animations

RemoteViews is cross-process and communicates with Its Binder and AppWidgetServer, so its method parameters must implement the Parcelable interface, which can pass ints, Strings, bitmaps and Drawable. The animation cannot be passed because the Animator class does not implement Parcelable.

Why don’t widgets support custom Views

The widget receives a message such as ACTION_APPWIDGET_UPDATE and refreshes RemoteViews, similar to the following:

RemoteViews remoteViews = new RemoteViews(sContext.getPackageName(),R.layout.widget_container_layout);
remoteViews.setImageViewResource(R.id.center, R.drawable.ic_sunny);
ComponentName componentName = new ComponentName(sContext, SampleWidgetProvider.class);
sAppWidgetManager.updateAppWidget(componentName, remoteViews);
Copy the code

The refresh RemoteViews

02-07 09:25:41.161 10702 10702 W System. Err: An android. View. LayoutInflater. CreateView (LayoutInflater. Java: 635) 02-07 09:25:41. 161, 10702, 10702 W System. Err: At com. Android. Internal. Policy. PhoneLayoutInflater. OnCreateView (58) PhoneLayoutInflater. Java: 02-07 09:25:41. 161, 10702 10702 W System.err: An android. View. LayoutInflater. OnCreateView (LayoutInflater. Java: 734) 02-07 09:25:41. 161, 10702, 10702 W System. Err: An android. View. LayoutInflater. CreateViewFromTag (LayoutInflater. Java: 802) 02-07 09:25:41. 162, 10702, 10702 W System. Err: An android. View. LayoutInflater. CreateViewFromTag (LayoutInflater. Java: 744) 02-07 09:25:41. 162, 10702, 10702 W System. Err: An android. View. LayoutInflater. Inflate (LayoutInflater. Java: 493) 02-07 09:25:41. 162, 10702, 10702 W System. Err: An android. View. LayoutInflater. Inflate (LayoutInflater. Java: 424) 02-07 09:25:41. 162, 10702, 10702 W System. Err: . An android widget. RemoteViews. InflateView (3498) RemoteViews. Java: 02-07 09:25:41. 163, 10702, 10702 W System. Err: . An android widget. RemoteViews. Apply (3475) RemoteViews. Java: 02-07 09:25:41. 163, 10702, 10702 W System. Err: An android. Appwidget. AppWidgetHostView. ApplyRemoteViews (AppWidgetHostView. Java: 451) 02-07 09:25:41. 164, 10702, 10702 W System.err: An android. Appwidget. AppWidgetHostView. UpdateAppWidget (AppWidgetHostView. Java: 380) 02-07 09:25:41. 164, 10702, 10702 W System.err: at com.chehejia.m01.launcher.data.appwidget.LauncherAppWidgetHostView.updateAppWidget(LauncherAppWidgetHostView.java:96) 02-07 09:25:41.164 10702 10702 W System. Err: An android. Appwidget. AppWidgetHost. UpdateAppWidgetView (AppWidgetHost. Java: 437) 02-07 09:25:41. 165, 10702, 10702 W System.err: At android. Appwidget. AppWidgetHost $UpdateHandler. HandleMessage (132) AppWidgetHost. Java: 02-07 09:25:41. 165, 10702, 10702 W System. Err: an android. OS. Handler. DispatchMessage (107) Handler. Java: 02-07 09:25:41. 165, 10702, 10702 W System. Err: At Android.os.looper. loop(looper.java :164) 02-07 09:25:41.165 10702 10702 W System. Err: . An android app. ActivityThread. Main (6553) ActivityThread. Java: 02-07 09:25:41. 165, 10702, 10702 W System. Err: The at Java. Lang. Reflect. Method. Invoke (Native Method) 02-07 09:25:41. 166, 10702, 10702 W System. Err: At com. Android. Internal. OS. RuntimeInit $MethodAndArgsCaller. Run (444) RuntimeInit. Java: 02-07 09:25:41. 166, 10702, 10702 W System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)Copy the code

The createView method of the LayoutInflate:

if (constructor == null) { clazz = mContext.getClassLoader().loadClass( prefix ! = null ? (prefix + name) : name).asSubclass(View.class); if (mFilter ! = null && clazz ! = null) { boolean allowed = mFilter.onLoadClass(clazz); if (! allowed) { failNotAllowed(name, prefix, attrs); } } constructor = clazz.getConstructor(mConstructorSignature); constructor.setAccessible(true); sConstructorMap.put(name, constructor);Copy the code

MContext and McOntext.getclassloader () are printed as follows: mContext=android.widget.RemoteViews$RemoteViewsContextWrapper@5971f82, mContext.getClassLoader()=dalvik.system.PathClassLoader[DexPathList[[zip file “/system/framework/retrofit.jar”, Zip file “/ system/framework/gsonjar jar”,,,,,, zip file “/ system/framework/oss – android SDK. The jar”, zip file “/data/app/com.chehejia.m01.launcher-EQ50ml8ebm4iRLzqrUnLcg==/base.apk”],nativeLibraryDirectories=[/data/app/xxx.launche r-EQ50ml8ebm4iRLzqrUnLcg==/lib/arm64, /data/app/xxx.launcher-EQ50ml8ebm4iRLzqrUnLcg==/base.apk! /lib/arm64-v8a, /system/lib64, /vendor/lib64]]]

As you can see above, the class loader will load classes from the DexPathList directory, and if your class isn’t in there, it won’t load, so applying a custom View won’t load. The RemoteViews context is actually passed from the Launcher, so it’s actually loaded with the Launcher and its JAR classes

boolean allowed = mFilter.onLoadClass(clazz); This mFilter is defined in AppWidgetHostView

private static final LayoutInflater.Filter INFLATER_FILTER =
        (clazz) -> clazz.isAnnotationPresent(RemoteViews.RemoteView.class);
Copy the code

So, in order for a custom View to be loaded on the widget, you need a custom View class annotated by RemoteViews and in one of the directories above, such as framework. The system clock, TextClock, is placed in the framework directory

Second, the requirements of the method

RemoteViews has a set of set methods such as setInt, setString, and setBundle.

 public void setBundle(int viewId, String methodName, Bundle value) {
        addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value));
    }
Copy the code

Implementation in ReflectionAction:

private MethodHandle getMethod(View view, String methodName, Class<? > paramType, boolean async) { MethodArgs result; Class<? extends View> klass = view.getClass(); Synchronized (sMethods) {,,, if (! method.isAnnotationPresent(RemotableViewMethod.class)) { throw new ActionException("view: " + klass.getName() + " can't use method with RemoteViews: " + methodName + getParameters(paramType)); },,, return result.asyncMethod; }}Copy the code

So, if you have a custom class whose method parameters can be passed by RemoteViews set and annotated with RemotableViewMethod, it can be called across processes using RemoteViews setXX