Android時計のwidget【アンドロイド進化三十七】

11422 ワード

この間、widgetのバグを解決しました.具体的な分析は「appWidgetManager.updateAppWidget(THIS_APPWIDGET、views);この方法updateAppWidget()は工場出荷時の設定を復元してから更新しないので、原因を調べた結果、logの検証で発見されたときsdkのバグ:
step 1:
public void updateAppWidget(int[] appWidgetIds, RemoteViews views) {
        try {
            sService.updateAppWidgetIds(appWidgetIds, views);
        }
        catch (RemoteException e) {
            throw new RuntimeException("system server dead?", e);
        }
    }

Step 2:このサービスupdateAppWidgetIds(appWIdgetIds, views);sServiceはAppWidgetServiceのオブジェクトです.このクラスでは次のようになります.
public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
        if (appWidgetIds == null) {
            return;
        }
        if (appWidgetIds.length == 0) {
            return;
        }
        final int N = appWidgetIds.length;

        synchronized (mAppWidgetIds) {
            for (int i=0; i

Step 3:分析コード:updateAppWidgetInstanceLocked()この方法で発生した問題;コードを見てください:
void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
        // allow for stale appWidgetIds and other badness
        // lookup also checks that the calling process can access the appWidgetId
        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
            id.views = views;

            // is anyone listening?
            if (id.host.callbacks != null) {
                try {
                    // the lock is held, but this is a oneway call
                    id.host.callbacks.updateAppWidget(id.appWidgetId, views);
                } catch (RemoteException e) {
                    // It failed; remove the callback. No need to prune because
                    // we know that this host is still referenced by this instance.
                    id.host.callbacks = null;
                }
            }
        }
    }

Step 4:log分析により、この値id.host.callbacks==nullによるupdateAppWidget(id.appWidgetId,views)への移行はありません.このcallbacksは、このクラスのstartListeningのときに付与されます.次に、この方法を見てみましょう.
 public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
            List updatedViews) {
        int callingUid = enforceCallingUid(packageName);
        synchronized (mAppWidgetIds) {
            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
            host.callbacks = callbacks;

            updatedViews.clear();

            ArrayList instances = host.instances;
            int N = instances.size();
            int[] updatedIds = new int[N];
            for (int i=0; i

このcallbacksが空になったのは2つの原因があります.1つはhostです.callbacks = callbacks;与えられた後、このhost.callbacksは別の条件で空に設定されています.もう1つの理由は、このパラメータが伝達されるとcallbacksが空の値を伝達するためです.
Step 5:IAppWidgetHost callbackが割り当てた場所を見つける.AppWidgetHostにもstartListeneeringメソッド()コードがあります.
 /**
     * Start receiving onAppWidgetChanged calls for your AppWidgets.  Call this when your activity
     * becomes visible, i.e. from onStart() in your Activity.
     */
    public void startListening() {
        int[] updatedIds;
        ArrayList updatedViews = new ArrayList();
        
        try {
            if (mPackageName == null) {
                mPackageName = mContext.getPackageName();
            }
            updatedIds = sService.startListening(mCallbacks, mPackageName, mHostId, updatedViews);
        }
        catch (RemoteException e) {
            throw new RuntimeException("system server dead?", e);
        }

        final int N = updatedIds.length;
        for (int i=0; i

このmCallbacksはStep 4のcallbacksが渡した値ですが、今このmCallbacksを探してどうやって値をつけますか?
検索ではCallbacks mCallbacks=new Callbacks();このmCallbacksはnewです.わかりましたね.
sService.startListening(mCallbacks, mPackageName, mHostId, updatedViews);判断して、mCallbacksが空であれば、またnewします.転送後、このcallbacksの値をグローバル変数に割り当て、Step 3のときに判断を加え、空のときにid.hostに割り当てる.callbacks.これで空っぽの状況が解決しました!本当の原因は調べていませんが、なぜ空いていますか?解決策を見つけただけだ!
以上の検索で、AppWidgetProviderについてある程度理解しました.これに基づいてクロックのwidgetを書きました.アナログクロックの効果と同じように、クロックをクリックすると目覚まし時計のインタフェースに入ります.スクリーンショットは以下の通りです.
赤い部分は時計です:デスクトップをクリックして時計widgetを追加します:それぞれをクリックして目覚まし時計のインターフェースに入ります:
AppWidgetProviderはextends BroadcastReceiverを継承し、App Widgetブロードキャストを簡単なクラスで処理するためです.AppWidgetProviderは、このApp Widgetに関連するイベントブロードキャストのみを受信します.例えば、このApp Widgetが更新され、削除され、有効になり、無効になります.これらのブロードキャストイベントが発生すると、AppWidgetProviderは次のメソッド呼び出しを受信します.
一、onEnabled(Context context):
appwidgetを初めてインスタンス化するとaction_を受け入れますappwidget_Enabledブロードキャストは、この方法を書き換えて独自のappwidget機能を実現します.
二、onUpdate(Context context,AppWidgetManager appWidgetManager,int[]appWidgetIds)
このメソッドは、AppWidgetProviderInfoのupdatePeriodMillisプロパティ定義(AppWidgetProviderInfoメタデータの追加を参照)を使用して、AppWidgetProviderInfoを間隔で更新するために呼び出されます.この方法は、ユーザーがApp Widgetを追加するときに呼び出されるため、ビューにイベントプロセッサを定義し、必要に応じて一時的なサービスサービスサービスサービスを起動するなどの基礎的な設定を実行する必要があります.ただし、構成アクティビティを宣言した場合、このメソッドはユーザーがApp Widgetを追加したときに呼び出されず、後続の更新時にのみ呼び出されます.構成アクティビティは、構成が完了したときに最初の更新を実行する責任を負う必要があります.
三、onDisabled(Context)App Widgetの最後のインスタンスがホストから削除されたときに呼び出されます.shareparenceやデータベースを削除するなど、onEnabled(Context)でクリーンアップを行う必要があります.
四、onReceive(Context,Intent)これは、各ブロードキャストを受信すると呼び出され、上記のコールバック関数の前に呼び出されます.デフォルトのAppWidgetProviderは、すべてのAppWidgetブロードキャストをフィルタリングし、上記の方法を適切に呼び出すため、この方法を実装する必要はありません.注意:Android 1.5では、onDeleted()メソッドが呼び出し時に呼び出されないという既知の問題があります.この問題を回避するために、あなたは
Grouppostに記載されているように、このonDeleted()コールバックを受信するためにonReceive()が実装される.
詳細については、sdkヘルプドキュメントを参照してください.
次に、スクリーンショットのコードを簡単に整理します.
一、manifestにreceiverイベントを登録する:

            
                
            
            
            
        
        
        
        
            
                
            
            
            
        
        
        
            
                
            
            
            
        
    

このフォーマットは固定されており、appwidgetにdmling_を定義します.appwidget.xmlのファイル
二、resディレクトリの下でxmlフォルダを創立して、xmlフォルダの中でdmling_を創立しますappwidget.xmlファイル:





このappwidget-providerフォーマットは固定されています.
三、「一」のreceiverに「一」のxmlの文のように受容クラスを確立する.
receiver android:name="com.cn.daming.provider.DMAlarmAppWidgetProvider"com.cn.daming.providerパッケージ、
com.cn.daming.providerパッケージにDMAlarmAppWidgetProviderを作成する.JAvaクラスは、次のとおりです.
package com.cn.daming.provider;

import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.RemoteViews;

import com.cn.daming.deskclock.DeskClockMainActivity;
import com.cn.daming.deskclock.R;

public class DMAlarmAppWidgetProvider extends BroadcastReceiver {

    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
            RemoteViews views = new RemoteViews(context.getPackageName(),
                    R.layout.damling_appwidget);

            views.setOnClickPendingIntent(R.id.damling_appwidget,
                    PendingIntent.getActivity(context, 0,
                        new Intent(context, DeskClockMainActivity.class),
                        PendingIntent.FLAG_UPDATE_CURRENT));//FLAG_NO_CREATE

            int[] appWidgetIds = intent.getIntArrayExtra(
                    AppWidgetManager.EXTRA_APPWIDGET_IDS);

            AppWidgetManager gm = AppWidgetManager.getInstance(context);
            Log.v("wdaming", "DMAlarmAppWidgetProvider ---> appWidgetIds == "+appWidgetIds+"  gm == "+gm+
            		"  views == "+views);
            gm.updateAppWidget(appWidgetIds, views);
        }
    }
}

四、例えば「二」のコードはlayoutファイルdamlingを創立するappwidget.xml:




これで基本的に時計のwidgetができました!もちろんこれはAppWidgetProviderに従って書かれていません.私が参考にしたのは目覚まし時計の時計widgetです.これも簡単だと思います.extends BroadcastReceiver、onReceiverに時計のクリックイベントを傍受する方法を書きます.このアナログ時計は
AnalylogClockはframeworksですでに処理されているので、dial【表盤】、hand_hour:【時計回り】,hand_minute:
【分針】で、時計が実現!
说明:问题があって、良い意见のあるいはソースコードの伝言を望むことができます!各界の人々がレンガを撮ることを歓迎します!