当前位置:   article > 正文

app widget添加流程_widget host dead: hostid{user:0, app:10024, hostid

widget host dead: hostid{user:0, app:10024, hostid:4096, pkg:com.avatr.ivi.f

先看一下app widget的添加代码,此处直接引用http://www.cnblogs.com/wanqieddy/archive/2012/05/05/2484533.html的代码

  1. package com.qin.addappwidget;
  2. import android.app.Activity;
  3. import android.appwidget.AppWidgetHost;
  4. import android.appwidget.AppWidgetHostView;
  5. import android.appwidget.AppWidgetManager;
  6. import android.appwidget.AppWidgetProviderInfo;
  7. import android.content.Intent;
  8. import android.os.Bundle;
  9. import android.util.Log;
  10. import android.view.View;
  11. import android.widget.Button;
  12. import android.widget.ImageView;
  13. import android.widget.LinearLayout;
  14. import android.widget.TextView;
  15. import android.widget.Toast;
  16. public class MainActivity extends Activity
  17. {
  18. private static String TAG = "AddAppWidget" ;
  19. private Button btAddShortCut;
  20. private LinearLayout linearLayout ; // 装载Appwidget的父视图
  21. private static final int MY_REQUEST_APPWIDGET = 1;
  22. private static final int MY_CREATE_APPWIDGET = 2;
  23. private static final int HOST_ID = 1024 ;
  24. private AppWidgetHost mAppWidgetHost = null ;
  25. AppWidgetManager appWidgetManager = null;
  26. /** Called when the activity is first created. */
  27. @Override
  28. public void onCreate(Bundle savedInstanceState)
  29. {
  30. super.onCreate(savedInstanceState);
  31. setContentView(R.layout.main);
  32. btAddShortCut = (Button) findViewById(R.id.bt_addShortcut);
  33. linearLayout = (LinearLayout)findViewById(R.id.linearLayout) ;
  34. //其参数hostid大意是指定该AppWidgetHost 即本Activity的标记Id, 直接设置为一个整数值吧 。
  35. mAppWidgetHost = new AppWidgetHost(MainActivity.this, HOST_ID) ;
  36. //为了保证AppWidget的及时更新 , 必须在Activity的onCreate/onStar方法调用该方法
  37. // 当然可以在onStop方法中,调用mAppWidgetHost.stopListenering() 停止AppWidget更新
  38. mAppWidgetHost.startListening() ;
  39. //获得AppWidgetManager对象
  40. appWidgetManager = AppWidgetManager.getInstance(MainActivity.this) ;
  41. btAddShortCut.setOnClickListener(new View.OnClickListener()
  42. {
  43. @Override
  44. public void onClick(View v)
  45. {
  46. //显示所有能创建AppWidget的列表 发送此 ACTION_APPWIDGET_PICK 的Action
  47. Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK) ;
  48. //向系统申请一个新的appWidgetId ,该appWidgetId与我们发送Action为ACTION_APPWIDGET_PICK
  49. // 后所选择的AppWidget绑定 。 因此,我们可以通过这个appWidgetId获取该AppWidget的信息了
  50. //为当前所在进程申请一个新的appWidgetId
  51. int newAppWidgetId = mAppWidgetHost.allocateAppWidgetId() ;
  52. Log.i(TAG, "The new allocate appWidgetId is ----> " + newAppWidgetId) ;
  53. //作为Intent附加值 , 该appWidgetId将会与选定的AppWidget绑定
  54. pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, newAppWidgetId) ;
  55. //选择某项AppWidget后,立即返回,即回调onActivityResult()方法
  56. startActivityForResult(pickIntent , MY_REQUEST_APPWIDGET) ;
  57. }
  58. });
  59. }
  60. // 如果
  61. protected void onActivityResult(int requestCode, int resultCode, Intent data)
  62. {
  63. //直接返回,没有选择任何一项 ,例如按Back键
  64. if(resultCode == RESULT_CANCELED)
  65. return ;
  66. switch(requestCode){
  67. case MY_REQUEST_APPWIDGET :
  68. Log.i(TAG, "MY_REQUEST_APPWIDGET intent info is -----> "+data ) ;
  69. int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID , AppWidgetManager.INVALID_APPWIDGET_ID) ;
  70. Log.i(TAG, "MY_REQUEST_APPWIDGET : appWidgetId is ----> " + appWidgetId) ;
  71. //得到的为有效的id
  72. if(appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID){
  73. //查询指定appWidgetId的 AppWidgetProviderInfo对象 , 即在xml文件配置的<appwidget-provider />节点信息
  74. AppWidgetProviderInfo appWidgetProviderInfo = appWidgetManager.getAppWidgetInfo(appWidgetId) ;
  75. //如果配置了configure属性 , 即android:configure = "" ,需要再次启动该configure指定的类文件,通常为一个Activity
  76. if(appWidgetProviderInfo.configure != null){
  77. Log.i(TAG, "The AppWidgetProviderInfo configure info -----> " + appWidgetProviderInfo.configure ) ;
  78. //配置此Action
  79. Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE) ;
  80. intent.setComponent(appWidgetProviderInfo.configure) ;
  81. intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
  82. startActivityForResult(intent , MY_CREATE_APPWIDGET) ;
  83. }
  84. else //直接创建一个AppWidget
  85. onActivityResult(MY_CREATE_APPWIDGET , RESULT_OK , data) ; //参数不同,简单回调而已
  86. }
  87. break ;
  88. case MY_CREATE_APPWIDGET:
  89. completeAddAppWidget(data) ;
  90. break ;
  91. }
  92. }
  93. //向当前视图添加一个用户选择的
  94. private void completeAddAppWidget(Intent data){
  95. Bundle extra = data.getExtras() ;
  96. int appWidgetId = extra.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID , -1) ;
  97. //等同于上面的获取方式
  98. //int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID , AppWidgetManager.INVALID_APPWIDGET_ID) ;
  99. Log.i(TAG, "completeAddAppWidget : appWidgetId is ----> " + appWidgetId) ;
  100. if(appWidgetId == -1){
  101. Toast.makeText(MainActivity.this, "添加窗口小部件有误", Toast.LENGTH_SHORT) ;
  102. return ;
  103. }
  104. AppWidgetProviderInfo appWidgetProviderInfo = appWidgetManager.getAppWidgetInfo(appWidgetId) ;
  105. AppWidgetHostView hostView = mAppWidgetHost.createView(MainActivity.this, appWidgetId, appWidgetProviderInfo);
  106. //linearLayout.addView(hostView) ;
  107. int widget_minWidht = appWidgetProviderInfo.minWidth ;
  108. int widget_minHeight = appWidgetProviderInfo.minHeight ;
  109. //设置长宽 appWidgetProviderInfo 对象的 minWidth 和 minHeight 属性
  110. LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams(widget_minWidht, widget_minHeight);
  111. //添加至LinearLayout父视图中
  112. linearLayout.addView(hostView,linearLayoutParams) ;
  113. }
  114. }

大致流程如下:

1. 实例化AppWidgetHost, 具体实例化代码如下:

  1. public AppWidgetHost(Context context, int hostId) {
  2. this(context, hostId, null, context.getMainLooper());
  3. }
  4. /**
  5. * @hide
  6. */
  7. public AppWidgetHost(Context context, int hostId, OnClickHandler handler, Looper looper) {
  8. mContext = context;
  9. mHostId = hostId;
  10. mOnClickHandler = handler;
  11. mHandler = new UpdateHandler(looper);
  12. mDisplayMetrics = context.getResources().getDisplayMetrics();
  13. bindService();
  14. }

该AppWidgetHost保存应用的Context对象,hostId,同时实例化一个Handler(和Launcher主线程共用一个MessageQueue),实例化一个IAppWidgetServer对象用于与AppWidgetService服务通信。

2. 通知AppWidgetService自身可以接收消息

  1. /**
  2. * Start receiving onAppWidgetChanged calls for your AppWidgets. Call this when your activity
  3. * becomes visible, i.e. from onStart() in your Activity.
  4. */
  5. public void startListening() {
  6. Log.d(TAG, "startListening");
  7. int[] updatedIds;
  8. ArrayList<RemoteViews> updatedViews = new ArrayList<RemoteViews>();
  9. final int userId = mContext.getUserId();
  10. try {
  11. if (mPackageName == null) {
  12. mPackageName = mContext.getPackageName();
  13. }
  14. updatedIds = sService.startListening(
  15. mCallbacks, mPackageName, mHostId, updatedViews, userId);
  16. }
  17. catch (RemoteException e) {
  18. throw new RuntimeException("system server dead?", e);
  19. }
  20. final int N = updatedIds.length;
  21. for (int i=0; i<N; i++) {
  22. if (updatedViews.get(i) != null) {
  23. updatedViews.get(i).setUser(new UserHandle(userId));
  24. }
  25. updateAppWidgetView(updatedIds[i], updatedViews.get(i), userId);
  26. }
  27. }

sService.startListening最终调用在AppWidgetServiceImpl中,
  1. public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
  2. List<RemoteViews> updatedViews) {
  3. log("startListening");
  4. if (!mHasFeature) {
  5. return new int[0];
  6. }
  7. int callingUid = enforceCallingUid(packageName);
  8. synchronized (mAppWidgetIds) {
  9. ensureStateLoadedLocked();
  10. Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
  11. host.callbacks = callbacks;
  12. updatedViews.clear();
  13. ArrayList<AppWidgetId> instances = host.instances;
  14. int N = instances.size();
  15. int[] updatedIds = new int[N];
  16. for (int i = 0; i < N; i++) {
  17. AppWidgetId id = instances.get(i);
  18. updatedIds[i] = id.appWidgetId;
  19. updatedViews.add(cloneIfLocalBinder(id.views));
  20. }
  21. return updatedIds;
  22. }
  23. }

在AppWidgetService中mHost保存所有已注册的Host,此处会查找已经存在的或者添加一个新Host对象,同时记录AppWidgetHost的callback,更新updateViews列表为所有已添加到Host的插件的RemoteViews,最后返回该Host中所有插件的id。

appWidgetHost在取得自身所有已经添加的插件 id后,会先为对应的RemoteViews设定UserHandle,然后更新对应的AppWidgetHostView。

3. 获取新分配的appWidget id

  1. /**
  2. * Get a appWidgetId for a host in the calling process.
  3. *
  4. * @return a appWidgetId
  5. */
  6. public int allocateAppWidgetId() {
  7. try {
  8. if (mPackageName == null) {
  9. mPackageName = mContext.getPackageName();
  10. }
  11. return sService.allocateAppWidgetId(mPackageName, mHostId, mContext.getUserId());
  12. }
  13. catch (RemoteException e) {
  14. throw new RuntimeException("system server dead?", e);
  15. }
  16. }
AppWidgetServiceImpl会分配一个appWidget id,然后实例化一个AppWidgetId对象,保存到该Host和mAppWidgetIds中,同时也保存到xml文件中(缺少provider元素)。

4. bind appwidgetid

在上例代码中是通过发送intent的方式完成,当用户选择需要添加的widget后则会调用AppWidgetManager.bindAppWidgetIdIfAllowed(int appWidgetId, ComponentName provider, Bundle options) 方法,具体的实现在AppWidgetServiceImpl中

  1. public boolean bindAppWidgetIdIfAllowed(
  2. String packageName, int appWidgetId, ComponentName provider, Bundle options) {
  3. if (!mHasFeature) {
  4. return false;
  5. }
  6. try {
  7. mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET, null);
  8. } catch (SecurityException se) {
  9. if (!callerHasBindAppWidgetPermission(packageName)) {
  10. return false;
  11. }
  12. }
  13. bindAppWidgetIdImpl(appWidgetId, provider, options);
  14. return true;
  15. }
  16. private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) {
  17. if (DBG) log("bindAppWidgetIdImpl appwid=" + appWidgetId
  18. + " provider=" + provider);
  19. final long ident = Binder.clearCallingIdentity();
  20. try {
  21. synchronized (mAppWidgetIds) {
  22. AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
  23. Provider p = lookupProviderLocked(provider);
  24. // ...检查数据
  25. id.provider = p;
  26. id.options = options;
  27. p.instances.add(id);
  28. int instancesSize = p.instances.size();
  29. if (instancesSize == 1) {
  30. // tell the provider that it's ready
  31. sendEnableIntentLocked(p);
  32. }
  33. // send an update now -- We need this update now, and just for this appWidgetId.
  34. // It's less critical when the next one happens, so when we schedule the next one,
  35. // we add updatePeriodMillis to its start time. That time will have some slop,
  36. // but that's okay.
  37. sendUpdateIntentLocked(p, new int[] { appWidgetId });
  38. // schedule the future updates
  39. registerForBroadcastsLocked(p, getAppWidgetIds(p));
  40. saveStateAsync();
  41. }
  42. } finally {
  43. Binder.restoreCallingIdentity(ident);
  44. }
  45. }

可以看到bindAppWidgetId主要是保存新Provider信息,同时发送enable和update广播通知应用Provider处理,最后将插件信息写入磁盘。

5. configure appWidget

此处略过

6. 显示appWidget

bind和configure成功后就可以显示appWidget到AppWidgetHostView中,整个添加过程结束。

  1. AppWidgetProviderInfo appWidgetProviderInfo = appWidgetManager.getAppWidgetInfo(appWidgetId) ;
  2. AppWidgetHostView hostView = mAppWidgetHost.createView(MainActivity.this, appWidgetId, appWidgetProviderInfo);
  3. //linearLayout.addView(hostView) ;
  4. int widget_minWidht = appWidgetProviderInfo.minWidth ;
  5. int widget_minHeight = appWidgetProviderInfo.minHeight ;
  6. //设置长宽 appWidgetProviderInfo 对象的 minWidth 和 minHeight 属性
  7. LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams(widget_minWidht, widget_minHeight);
  8. //添加至LinearLayout父视图中
  9. linearLayout.addView(hostView,linearLayoutParams) ;

下一篇再介绍createView的具体流程以及应用如何通知桌面来刷新数据。


前面的步骤3中有一个疑问:在AppWidgetServiceImpl中分配了一个widget id,同时保存了数据,但是如果后期用户后悔,取消添加插件会如何?基本做法应该为在接收到RESULT_CANCELED结果时通过AppWidgetHost删除已分配但是未添加的插件。



声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号