系统启动后,添加新的Appwidget到手机更新不同步问题解决

在Android系统中,通过长按launcher空白处,会弹出“添加到主屏幕”界面,选择窗口小部件项,弹出“选择窗口小部件”界面,选择任一widget就可将此widget添加到桌面上,截图如下:

系统启动后,添加新的Appwidget到手机更新不同步问题解决_第1张图片

此添加AppWidget过程比较繁琐,我想在Launcher屏幕上添加一个按钮,通过点击此按钮直接实现添加widget功能,并使用gallery显示所有的widget icon。通过查看源代码,发现上述添加AppWidget的逻辑写在Setting工程中,于是想自己在Launcher中实现这个功能,下面是获取所有已安装widget信息代码:

   public void initData(Context context)
    {
        tempInfo.clear();
        
        AppWidgetManager am = AppWidgetManager.getInstance(context);
        tempInfo = (ArrayList<AppWidgetProviderInfo>)am.getInstalledProviders();
               }
                
        Collections.sort(tempInfo, new Comparator<AppWidgetProviderInfo>()
        {
            Collator collator = Collator.getInstance();
            @Override
            public int compare(AppWidgetProviderInfo arg0, AppWidgetProviderInfo arg1) {
                // TODO Auto-generated method stub                
                return collator.compare(arg0.label, arg1.label);
            }            
        });
    }

关键代码就是tempInfo = (ArrayList<AppWidgetProviderInfo>)am.getInstalledProviders();

数据源TempInfo获取到了,后面的事情就比较简单。将数据源传入gallery的自定义adpter中,并将此gallery绑定到写好的Launcher按钮响应事件上,一切就大功告成。成果如下:

系统启动后,添加新的Appwidget到手机更新不同步问题解决_第2张图片

但是在测试时发现,当在系统启动好后,安装新的widget(down APK),gallery不会更新新安装的appwidget,同样,删除一个widget,gallery也不会更新。通过查看Android管理AppWidget的AppWidgetService.java,发现Android在启动时注册了四个BroadcastReceiver:

public void systemReady(boolean safeMode) {
        mSafeMode = safeMode;

        loadAppWidgetList();
        loadStateLocked();

        // Register for the boot completed broadcast, so we can send the
        // ENABLE broacasts.  If we try to send them now, they time out,
        // because the system isn't ready to handle them yet.
        mContext.registerReceiver(mBroadcastReceiver,
                new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);

        // Register for configuration changes so we can update the names
        // of the widgets when the locale changes.
        mContext.registerReceiver(mBroadcastReceiver,
                new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);

        // Register for broadcasts about package install, etc., so we can
        // update the provider list.
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        filter.addDataScheme("package");
        mContext.registerReceiver(mBroadcastReceiver, filter);
        // Register for events related to sdcard installation.
        IntentFilter sdFilter = new IntentFilter();
        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
        mContext.registerReceiver(mBroadcastReceiver, sdFilter);

    }


注意18-24行代码:

// Register for broadcasts about package install, etc., so we can
// update the provider list.

这段注释很重要,表示Android在安装/删除APK时,AppWidgetService接收系统传过来的ACTION_PACKAGE_ADDED和ACTION_PACKAGE_REMOVED广播,继续看AppWidgetService代码,看Android是在哪里处理这两个广播的(下面以处理ACTION_PACKAGE_ADDED广播为例,ACTION_PACKAGE_REMOVED与其相似)。
  

   BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            //Slog.d(TAG, "received " + action);
            if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
                sendInitialBroadcasts();
                mLocale = Locale.getDefault();
                mSkin = context.getResources().getConfiguration().skin;
            } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
                Locale revised = Locale.getDefault();
                if (revised == null || mLocale == null ||
                    !(revised.equals(mLocale))) {
                    mLocale = revised;
                    updateAllWidgets();
                }

                String newSkin = context.getResources().getConfiguration().skin;
                if (newSkin == null || mSkin == null || 
                        !(newSkin.equals(mSkin))) {
                    mSkin = newSkin;
                    updateAllWidgets();
                }
            } else {
                boolean added = false;
                String pkgList[] = null;
                if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                    added = true;
                } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                    added = false;
                } else  {
                    Uri uri = intent.getData();
                    if (uri == null) {
                        return;
                    }
                    String pkgName = uri.getSchemeSpecificPart();
                    if (pkgName == null) {
                        return;
                    }
                    pkgList = new String[] { pkgName };
                    added = Intent.ACTION_PACKAGE_ADDED.equals(action);
                }
                if (pkgList == null || pkgList.length == 0) {
                    return;
                }
                if (added) {
                    synchronized (mAppWidgetIds) {
                        Bundle extras = intent.getExtras();
                        if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
                            for (String pkgName : pkgList) {
                                // The package was just upgraded
                                updateProvidersForPackageLocked(pkgName);
                            }
                        } else {
                            // The package was just added
                            for (String pkgName : pkgList) {
                                addProvidersForPackageLocked(pkgName);
                            }
                        }
                        saveStateLocked();
                    }
                } else {
                    Bundle extras = intent.getExtras();
                    if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
                        // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
                    } else {
                        synchronized (mAppWidgetIds) {
                            for (String pkgName : pkgList) {
                                removeProvidersForPackageLocked(pkgName);
                                saveStateLocked();
                            }
                        }
                    }
                }
            }
        }
    };


注意47行到59行代码,当down一个新的widget到手机,会走addProvidersForPackageLocked()方法:

 void addProvidersForPackageLocked(String pkgName) {
        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
        intent.setPackage(pkgName);
        List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
                PackageManager.GET_META_DATA);

        final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
        for (int i=0; i<N; i++) {
            ResolveInfo ri = broadcastReceivers.get(i);
            ActivityInfo ai = ri.activityInfo;
            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
                continue;
            }
            if (pkgName.equals(ai.packageName)) {
                addProviderLocked(ri);
            }
        }
    }

addProvidersForPackageLocked()方法通过PackageManager从Android系统中查找所有已经被安装的AppWidget(包含"android.appwidget.action.APPWIDGET_UPDATE"的Action和meta-data标签),解析AppWidget的配置信息,通过addProviderLocked()方法封闭成对象,添加到mIntalledProviders变量中。下面是addProviderLocked()方法代码:

    boolean addProviderLocked(ResolveInfo ri) {
        Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
                    ri.activityInfo.name), ri);
        if (p != null) {
            mInstalledProviders.add(p);
            return true;
        } else {
            return false;
        }
    }

mInstalledProviders变量就是SDK中通过AppWidgetManager.getInstalledProvider()获取的list。

Down widget APK到手机framework处理逻辑就走完了,简单的说就是Down APK到手机这个动作会发出ACTION_PACKAGE_ADDED广播,系统服务AppWidgetService注册广播监听器监听这个广播,当监听到此广播,AppWidgetService会重新遍历所有已经被安装的package(包含"android.appwidget.action.APPWIDGET_UPDATE"的Action和meta-data标签,这两个标签其实就是表明此package为appwidget),然后将得到的结果放入存放已安装的AppWidget的list中。

现在需要做的就是在自己写的代码中加入两个广播接收器监听ACTION_PACKAGE_ADDED和ACTION_PACKAGE_REMOVED广播,接收到这两个广播后,重新通过AppWidgetManager.getInstalledProvider()获取系统已安装的AppWidget列表,重新更新下gallery。代码如下:

1)新建一个广播接收器,并重写onReceive方法:

//begin:add by caovae   
BroadcastReceiver br2 = new BroadcastReceiver() {
        
        @Override
        public void onReceive(Context arg0, Intent arg1) {
                    initData(context);
                    widgetsAdapter.notifyDataSetChanged();
                    }
    };
    //end


2)在代码中注册该广播接收器:

        //begin:add by caovae 
        IntentFilter iFilter = new IntentFilter();
        iFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
        iFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        iFilter.addDataScheme("package");
        launcher.registerReceiver(br2, iFilter);
        //end

本以为这样所有的代码应该没有问题了,但是实际效果确还是不能更新gallery。通过打log分析了下原因,在声明的广播接收器中,所得到的已安装的appwidget当down了新的widget时并没有改变,分析原因,可能是AppWidgetService与我自己写的代码同时接收到ACTION_PACKAGE_ADDED广播,而AppWidgetService在接收到此广播后还有一段处理时间。所以我在广播接收器br2的onReceive通过新建线程并让其sleep两秒钟,然后在执行重新获取已安装的AppWidget列表,更新gallery。重写的br2代码如下:

 

//begin:add by caovae 
    BroadcastReceiver br2 = new BroadcastReceiver() {
        
        @Override
        public void onReceive(Context arg0, Intent arg1) {
            
            Runnable runnable = new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(2 * 1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    initData(context);
                    widgetsAdapter.notifyDataSetChanged();
                    
                }
            };
            
            runnable.run();
            
//            System.out.println("--------onreceiver: " + widgetInfo.size());
            
        }
    };
    //end

OK,添加新的Appwidget到手机更新不同步问题解决。



 

 


 

你可能感兴趣的:(系统启动后,添加新的Appwidget到手机更新不同步问题解决)