赞
踩
先看一下app widget的添加代码,此处直接引用http://www.cnblogs.com/wanqieddy/archive/2012/05/05/2484533.html的代码
- package com.qin.addappwidget;
-
-
- import android.app.Activity;
- import android.appwidget.AppWidgetHost;
- import android.appwidget.AppWidgetHostView;
- import android.appwidget.AppWidgetManager;
- import android.appwidget.AppWidgetProviderInfo;
- import android.content.Intent;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.View;
- import android.widget.Button;
- import android.widget.ImageView;
- import android.widget.LinearLayout;
- import android.widget.TextView;
- import android.widget.Toast;
-
- public class MainActivity extends Activity
- {
-
- private static String TAG = "AddAppWidget" ;
-
- private Button btAddShortCut;
- private LinearLayout linearLayout ; // 装载Appwidget的父视图
-
- private static final int MY_REQUEST_APPWIDGET = 1;
- private static final int MY_CREATE_APPWIDGET = 2;
-
- private static final int HOST_ID = 1024 ;
-
- private AppWidgetHost mAppWidgetHost = null ;
- AppWidgetManager appWidgetManager = null;
-
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
- btAddShortCut = (Button) findViewById(R.id.bt_addShortcut);
-
- linearLayout = (LinearLayout)findViewById(R.id.linearLayout) ;
-
- //其参数hostid大意是指定该AppWidgetHost 即本Activity的标记Id, 直接设置为一个整数值吧 。
- mAppWidgetHost = new AppWidgetHost(MainActivity.this, HOST_ID) ;
-
- //为了保证AppWidget的及时更新 , 必须在Activity的onCreate/onStar方法调用该方法
- // 当然可以在onStop方法中,调用mAppWidgetHost.stopListenering() 停止AppWidget更新
- mAppWidgetHost.startListening() ;
-
- //获得AppWidgetManager对象
- appWidgetManager = AppWidgetManager.getInstance(MainActivity.this) ;
-
-
- btAddShortCut.setOnClickListener(new View.OnClickListener()
- {
- @Override
- public void onClick(View v)
- {
- //显示所有能创建AppWidget的列表 发送此 ACTION_APPWIDGET_PICK 的Action
- Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK) ;
-
- //向系统申请一个新的appWidgetId ,该appWidgetId与我们发送Action为ACTION_APPWIDGET_PICK
- // 后所选择的AppWidget绑定 。 因此,我们可以通过这个appWidgetId获取该AppWidget的信息了
-
- //为当前所在进程申请一个新的appWidgetId
- int newAppWidgetId = mAppWidgetHost.allocateAppWidgetId() ;
- Log.i(TAG, "The new allocate appWidgetId is ----> " + newAppWidgetId) ;
-
- //作为Intent附加值 , 该appWidgetId将会与选定的AppWidget绑定
- pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, newAppWidgetId) ;
-
- //选择某项AppWidget后,立即返回,即回调onActivityResult()方法
- startActivityForResult(pickIntent , MY_REQUEST_APPWIDGET) ;
-
- }
- });
- }
-
- // 如果
- protected void onActivityResult(int requestCode, int resultCode, Intent data)
- {
- //直接返回,没有选择任何一项 ,例如按Back键
- if(resultCode == RESULT_CANCELED)
- return ;
-
- switch(requestCode){
- case MY_REQUEST_APPWIDGET :
- Log.i(TAG, "MY_REQUEST_APPWIDGET intent info is -----> "+data ) ;
- int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID , AppWidgetManager.INVALID_APPWIDGET_ID) ;
-
- Log.i(TAG, "MY_REQUEST_APPWIDGET : appWidgetId is ----> " + appWidgetId) ;
-
- //得到的为有效的id
- if(appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID){
- //查询指定appWidgetId的 AppWidgetProviderInfo对象 , 即在xml文件配置的<appwidget-provider />节点信息
- AppWidgetProviderInfo appWidgetProviderInfo = appWidgetManager.getAppWidgetInfo(appWidgetId) ;
-
- //如果配置了configure属性 , 即android:configure = "" ,需要再次启动该configure指定的类文件,通常为一个Activity
- if(appWidgetProviderInfo.configure != null){
-
- Log.i(TAG, "The AppWidgetProviderInfo configure info -----> " + appWidgetProviderInfo.configure ) ;
-
- //配置此Action
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE) ;
- intent.setComponent(appWidgetProviderInfo.configure) ;
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
-
-
- startActivityForResult(intent , MY_CREATE_APPWIDGET) ;
- }
- else //直接创建一个AppWidget
- onActivityResult(MY_CREATE_APPWIDGET , RESULT_OK , data) ; //参数不同,简单回调而已
- }
- break ;
- case MY_CREATE_APPWIDGET:
- completeAddAppWidget(data) ;
- break ;
- }
-
- }
-
- //向当前视图添加一个用户选择的
- private void completeAddAppWidget(Intent data){
- Bundle extra = data.getExtras() ;
- int appWidgetId = extra.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID , -1) ;
- //等同于上面的获取方式
- //int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID , AppWidgetManager.INVALID_APPWIDGET_ID) ;
-
- Log.i(TAG, "completeAddAppWidget : appWidgetId is ----> " + appWidgetId) ;
-
- if(appWidgetId == -1){
- Toast.makeText(MainActivity.this, "添加窗口小部件有误", Toast.LENGTH_SHORT) ;
- return ;
- }
-
- AppWidgetProviderInfo appWidgetProviderInfo = appWidgetManager.getAppWidgetInfo(appWidgetId) ;
-
- AppWidgetHostView hostView = mAppWidgetHost.createView(MainActivity.this, appWidgetId, appWidgetProviderInfo);
-
- //linearLayout.addView(hostView) ;
-
- int widget_minWidht = appWidgetProviderInfo.minWidth ;
- int widget_minHeight = appWidgetProviderInfo.minHeight ;
- //设置长宽 appWidgetProviderInfo 对象的 minWidth 和 minHeight 属性
- LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams(widget_minWidht, widget_minHeight);
- //添加至LinearLayout父视图中
- linearLayout.addView(hostView,linearLayoutParams) ;
- }
- }

1. 实例化AppWidgetHost, 具体实例化代码如下:
- public AppWidgetHost(Context context, int hostId) {
- this(context, hostId, null, context.getMainLooper());
- }
-
- /**
- * @hide
- */
- public AppWidgetHost(Context context, int hostId, OnClickHandler handler, Looper looper) {
- mContext = context;
- mHostId = hostId;
- mOnClickHandler = handler;
- mHandler = new UpdateHandler(looper);
- mDisplayMetrics = context.getResources().getDisplayMetrics();
- bindService();
- }
2. 通知AppWidgetService自身可以接收消息
- /**
- * Start receiving onAppWidgetChanged calls for your AppWidgets. Call this when your activity
- * becomes visible, i.e. from onStart() in your Activity.
- */
- public void startListening() {
- Log.d(TAG, "startListening");
- int[] updatedIds;
- ArrayList<RemoteViews> updatedViews = new ArrayList<RemoteViews>();
-
- final int userId = mContext.getUserId();
- try {
- if (mPackageName == null) {
- mPackageName = mContext.getPackageName();
- }
- updatedIds = sService.startListening(
- mCallbacks, mPackageName, mHostId, updatedViews, userId);
- }
- catch (RemoteException e) {
- throw new RuntimeException("system server dead?", e);
- }
-
- final int N = updatedIds.length;
- for (int i=0; i<N; i++) {
- if (updatedViews.get(i) != null) {
- updatedViews.get(i).setUser(new UserHandle(userId));
- }
- updateAppWidgetView(updatedIds[i], updatedViews.get(i), userId);
- }
- }

- public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
- List<RemoteViews> updatedViews) {
- log("startListening");
- if (!mHasFeature) {
- return new int[0];
- }
- int callingUid = enforceCallingUid(packageName);
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
- host.callbacks = callbacks;
-
- updatedViews.clear();
-
- ArrayList<AppWidgetId> instances = host.instances;
- int N = instances.size();
- int[] updatedIds = new int[N];
- for (int i = 0; i < N; i++) {
- AppWidgetId id = instances.get(i);
- updatedIds[i] = id.appWidgetId;
- updatedViews.add(cloneIfLocalBinder(id.views));
- }
- return updatedIds;
- }
- }

在AppWidgetService中mHost保存所有已注册的Host,此处会查找已经存在的或者添加一个新Host对象,同时记录AppWidgetHost的callback,更新updateViews列表为所有已添加到Host的插件的RemoteViews,最后返回该Host中所有插件的id。
appWidgetHost在取得自身所有已经添加的插件 id后,会先为对应的RemoteViews设定UserHandle,然后更新对应的AppWidgetHostView。
3. 获取新分配的appWidget id
- /**
- * Get a appWidgetId for a host in the calling process.
- *
- * @return a appWidgetId
- */
- public int allocateAppWidgetId() {
- try {
- if (mPackageName == null) {
- mPackageName = mContext.getPackageName();
- }
- return sService.allocateAppWidgetId(mPackageName, mHostId, mContext.getUserId());
- }
- catch (RemoteException e) {
- throw new RuntimeException("system server dead?", e);
- }
- }

AppWidgetServiceImpl会分配一个appWidget id,然后实例化一个AppWidgetId对象,保存到该Host和mAppWidgetIds中,同时也保存到xml文件中(缺少provider元素)。
4. bind appwidgetid
在上例代码中是通过发送intent的方式完成,当用户选择需要添加的widget后则会调用AppWidgetManager.bindAppWidgetIdIfAllowed(int appWidgetId, ComponentName provider, Bundle options) 方法,具体的实现在AppWidgetServiceImpl中
- public boolean bindAppWidgetIdIfAllowed(
- String packageName, int appWidgetId, ComponentName provider, Bundle options) {
- if (!mHasFeature) {
- return false;
- }
- try {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET, null);
- } catch (SecurityException se) {
- if (!callerHasBindAppWidgetPermission(packageName)) {
- return false;
- }
- }
- bindAppWidgetIdImpl(appWidgetId, provider, options);
- return true;
- }
-
- private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) {
- if (DBG) log("bindAppWidgetIdImpl appwid=" + appWidgetId
- + " provider=" + provider);
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mAppWidgetIds) {
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- Provider p = lookupProviderLocked(provider);
- // ...检查数据
- id.provider = p;
- id.options = options;
-
- p.instances.add(id);
- int instancesSize = p.instances.size();
- if (instancesSize == 1) {
- // tell the provider that it's ready
- sendEnableIntentLocked(p);
- }
-
- // send an update now -- We need this update now, and just for this appWidgetId.
- // It's less critical when the next one happens, so when we schedule the next one,
- // we add updatePeriodMillis to its start time. That time will have some slop,
- // but that's okay.
- sendUpdateIntentLocked(p, new int[] { appWidgetId });
-
- // schedule the future updates
- registerForBroadcastsLocked(p, getAppWidgetIds(p));
- saveStateAsync();
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }

5. configure appWidget
此处略过
6. 显示appWidget
bind和configure成功后就可以显示appWidget到AppWidgetHostView中,整个添加过程结束。
- AppWidgetProviderInfo appWidgetProviderInfo = appWidgetManager.getAppWidgetInfo(appWidgetId) ;
-
- AppWidgetHostView hostView = mAppWidgetHost.createView(MainActivity.this, appWidgetId, appWidgetProviderInfo);
-
- //linearLayout.addView(hostView) ;
-
- int widget_minWidht = appWidgetProviderInfo.minWidth ;
- int widget_minHeight = appWidgetProviderInfo.minHeight ;
- //设置长宽 appWidgetProviderInfo 对象的 minWidth 和 minHeight 属性
- LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams(widget_minWidht, widget_minHeight);
- //添加至LinearLayout父视图中
- linearLayout.addView(hostView,linearLayoutParams) ;
前面的步骤3中有一个疑问:在AppWidgetServiceImpl中分配了一个widget id,同时保存了数据,但是如果后期用户后悔,取消添加插件会如何?基本做法应该为在接收到RESULT_CANCELED结果时通过AppWidgetHost删除已分配但是未添加的插件。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。