AndroidデスクトップコントローラーappwidgetのストーリーI

36834 ワード

Androidバージョン:8.1
appwidgetデスクトップギズモは、ブロードキャスト受信機の方式で、リモートコールviewにより、appがデスクトップにviewを表示するコントロールを実現する.
APPが小さなコントロール機能を実現するには、次の手順を実行する必要があります.
ステップ1:AppWidgetProviderを継承するクラスを新規作成し、そのメソッド、onEnabled、onReceive、onUpdate、onDeleted、onDisabledを実装します.ステップ2:appのAndroidManifast.xmlでこのreceiverを宣言し、receiverにmeta-dataを追加します.たとえば、次のようにします.
      <meta-data
        android:name="android.appwidget.provider"
        android:resource="@xml/dialer_calllog_widget_xml">
      </meta-data>

resourceでxmlリソースファイルを追加します.フォーマットは次のとおりです.
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/widget_dialer_calllog_layout"   //         
    android:minHeight="160dip"		//      
    android:minWidth="100dip"		//      
    android:resizeMode="horizontal|vertical" //            
    android:widgetCategory="home_screen|keyguard" 
    android:previewImage="@drawable/empty_call_log"   //    launcher      
    android:updatePeriodMillis="86400000"> 			//      
  <!-- 70*n-30   width = 180  height = 250-->     
</appwidget-provider>

このような設定により、launcherデスクトップに小さなコントロールを追加するときにカスタマイズした小さなコントロールを見ることができます.
Appwidgetは、デスクトップ上のコントロールviewをブロードキャストで受信して更新する方法ですか?
ブロードキャストを送信すると、AppwidgetProviderのonreceiveがブロードキャストを受信します.
public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
            Bundle extras = intent.getExtras();
            if (extras != null) {
                int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
                if (appWidgetIds != null && appWidgetIds.length > 0) {
                    this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
                }
            }
        } 
    }

onupdateメソッドを再ロードするため、サブクラスのonupdateが呼び出されます.3つのパラメータはcontext、appwidgetmanager、appwidgetIDsです.私たちのコントロールviewを更新するには、remoteviewを作成する必要があります.つまり、私たちが指定したレイアウトのviewを送信し、appwidgetmanagerにリモートデスクトップのviewを更新させます.
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        mRemoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_dialer_calllog_layout);
        appWidgetManager.updateAppWidget(appWidgetIds,mRemoteViews);
    }

AppWidgetManagerで
    public void updateAppWidget(int[] appWidgetIds, RemoteViews views) {
        if (mService == null) {
            return;
        }
        try {
            mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
---
private final IAppWidgetService mService;

リモートAIDLを呼び出し、追跡して発見することができます.
AppWidgetServiceImpl extends IAppWidgetService.Stub

前のAppwidgetProviderのupdateで2番目のパラメータをAppWidgetManagerに渡しました.getInstance(context); AppWidgetManagerはここで初期化を行い、使用する単例モードです.ソースコードはこうです
    public static AppWidgetManager getInstance(Context context) {
        return (AppWidgetManager) context.getSystemService(Context.APPWIDGET_SERVICE);
    }

システムサービスを利用しています
public class AppWidgetService extends SystemService

このサービスはSystemserverで起動し、他のシステムサービスとともに起動します.
   if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)
       || context.getResources().getBoolean(R.bool.config_enableAppWidgetService)) {
       traceBeginAndSlog("StartAppWidgerService");
       mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
       traceEnd();
   }

具体的にプロセスを開始し、追跡しません.AppWidgetServiceの実装クラスは
AppWidgetServiceImpl extends IAppWidgetService.Stub

そこで、私たちのupdateの更新はAppWidgetServiceImplで実現されました.
    @Override
    public void updateAppWidgetIds(String callingPackage, int[] appWidgetIds,
            RemoteViews views) {
        if (DEBUG) {
            Slog.i(TAG, "updateAppWidgetIds() " + UserHandle.getCallingUserId());
        }

        updateAppWidgetIds(callingPackage, appWidgetIds, views, false);
    }

---
private void updateAppWidgetIds(String callingPackage, int[] appWidgetIds,
            RemoteViews views, boolean partially) {
            ...
        synchronized (mLock) {
            ensureGroupStateLoadedLocked(userId);
            final int N = appWidgetIds.length;
            for (int i = 0; i < N; i++) {
                final int appWidgetId = appWidgetIds[i];
                Widget widget = lookupWidgetLocked(appWidgetId,
                        Binder.getCallingUid(), callingPackage);

                if (widget != null) {
                    updateAppWidgetInstanceLocked(widget, views, partially);
                }
            }
        }
    }
---
private void updateAppWidgetInstanceLocked(Widget widget, RemoteViews views,
            boolean isPartialUpdate) {
        if (widget != null && widget.provider != null
                && !widget.provider.zombie && !widget.host.zombie) {
			...
            scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
        }
    }
---
    private void scheduleNotifyUpdateAppWidgetLocked(Widget widget, RemoteViews updateViews) {
        SomeArgs args = SomeArgs.obtain();
        args.arg1 = widget.host;
        args.arg2 = widget.host.callbacks;
        args.arg3 = (updateViews != null) ? updateViews.clone() : null;
        args.arg4 = requestId;
        args.argi1 = widget.appWidgetId;
        mCallbackHandler.obtainMessage(
                CallbackHandler.MSG_NOTIFY_UPDATE_APP_WIDGET,   //  message handler
                args).sendToTarget();
    }

CallbackHandlerで
       case MSG_NOTIFY_UPDATE_APP_WIDGET: {
           SomeArgs args = (SomeArgs) message.obj;
           Host host = (Host) args.arg1;
           IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
           RemoteViews views = (RemoteViews) args.arg3;
           long requestId = (Long) args.arg4;
           final int appWidgetId = args.argi1;
           args.recycle();

           handleNotifyUpdateAppWidget(host, callbacks, appWidgetId, views, requestId);
       } break;
---
    private void handleNotifyUpdateAppWidget(Host host, IAppWidgetHost callbacks,
            int appWidgetId, RemoteViews views, long requestId) {
        try {
            callbacks.updateAppWidget(appWidgetId, views);
            host.lastWidgetUpdateRequestId = requestId;
        } catch (RemoteException re) {
            synchronized (mLock) {
                Slog.e(TAG, "Widget host dead: " + host.id, re);
                host.callbacks = null;
            }
        }
    }

最後に、IAppWidgetHostのリモートオブジェクトを呼び出して、対応するidのviewを更新します.前に渡されたwidgetのhostです.
IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;

Messageの2番目のパラメータはAppWidgetHostです.ローカルオブジェクトはAppWidgetHostです.AppWidgetHostでは続けています.
static class Callbacks extends IAppWidgetHost.Stub {
        private final WeakReference<Handler> mWeakHandler;

        public Callbacks(Handler handler) {
            mWeakHandler = new WeakReference<>(handler);
        }

        public void updateAppWidget(int appWidgetId, RemoteViews views) {
            if (isLocalBinder() && views != null) {
                views = views.clone();
            }
            Handler handler = mWeakHandler.get();
            if (handler == null) {
                return;
            }
            Message msg = handler.obtainMessage(HANDLE_UPDATE, appWidgetId, 0, views);
            msg.sendToTarget();
        }
    }
---case HANDLE_UPDATE: {
             updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj);
             break;
         }void updateAppWidgetView(int appWidgetId, RemoteViews views) {
        AppWidgetHostView v;
        synchronized (mViews) {
            v = mViews.get(appWidgetId);
        }
        if (v != null) {
            v.updateAppWidget(views);
        }
    }

最後にAppWidgetHostViewのupdateを呼び出しました
    public void updateAppWidget(RemoteViews remoteViews) {
        applyRemoteViews(remoteViews, true);
    }

つまり、私たちが最初に更新を伝えるremoteviewを新しいviewに置き換え、次にviewの絵画更新段階を行います.AppWidgetHostViewは実はFrameLayoutです
public class AppWidgetHostView extends FrameLayout

だから、私たちがviewを更新するときは、最初の受信ブロードキャストでRemoteviewを作成し、appWidgetManagerを呼び出します.updateAppWidget(appWidgetIds,mRemoteView)では、小さなコントロールの表示内容が更新されます.