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を追加します.たとえば、次のようにします.
resourceでxmlリソースファイルを追加します.フォーマットは次のとおりです.
このような設定により、launcherデスクトップに小さなコントロールを追加するときにカスタマイズした小さなコントロールを見ることができます.
Appwidgetは、デスクトップ上のコントロールviewをブロードキャストで受信して更新する方法ですか?
ブロードキャストを送信すると、AppwidgetProviderのonreceiveがブロードキャストを受信します.
onupdateメソッドを再ロードするため、サブクラスのonupdateが呼び出されます.3つのパラメータはcontext、appwidgetmanager、appwidgetIDsです.私たちのコントロールviewを更新するには、remoteviewを作成する必要があります.つまり、私たちが指定したレイアウトのviewを送信し、appwidgetmanagerにリモートデスクトップのviewを更新させます.
AppWidgetManagerで
リモートAIDLを呼び出し、追跡して発見することができます.
前のAppwidgetProviderのupdateで2番目のパラメータをAppWidgetManagerに渡しました.getInstance(context); AppWidgetManagerはここで初期化を行い、使用する単例モードです.ソースコードはこうです
システムサービスを利用しています
このサービスはSystemserverで起動し、他のシステムサービスとともに起動します.
具体的にプロセスを開始し、追跡しません.AppWidgetServiceの実装クラスは
そこで、私たちのupdateの更新はAppWidgetServiceImplで実現されました.
CallbackHandlerで
最後に、IAppWidgetHostのリモートオブジェクトを呼び出して、対応するidのviewを更新します.前に渡されたwidgetのhostです.
Messageの2番目のパラメータはAppWidgetHostです.ローカルオブジェクトはAppWidgetHostです.AppWidgetHostでは続けています.
最後にAppWidgetHostViewのupdateを呼び出しました
つまり、私たちが最初に更新を伝えるremoteviewを新しいviewに置き換え、次にviewの絵画更新段階を行います.AppWidgetHostViewは実はFrameLayoutです
だから、私たちがviewを更新するときは、最初の受信ブロードキャストでRemoteviewを作成し、appWidgetManagerを呼び出します.updateAppWidget(appWidgetIds,mRemoteView)では、小さなコントロールの表示内容が更新されます.
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)では、小さなコントロールの表示内容が更新されます.