編輯:關於Android編程
/** * 相當於調用TextView.setText * */ public void setTextViewText(int viewId, CharSequence text) { setCharSequence(viewId, "setText", text); }
/** * 調用一個Remoteviews上一個控件參數為CharSequence的方法 */ public void setCharSequence(int viewId, String methodName, CharSequence value) { addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value)); }
/** * 添加一個Action ,它會在遠程進程調用apply方法的時候執行 * * @param a The action to add */ private void addAction(Action a) { if (hasLandscapeAndPortraitLayouts()) { throw new RuntimeException("RemoteViews specifying separate landscape and portrait" + " layouts cannot be modified. Instead, fully configure the landscape and" + " portrait layouts individually before constructing the combined layout."); } if (mActions == null) { mActions = new ArrayList(); } mActions.add(a); // update the memory usage stats a.updateMemoryUsageEstimate(mMemoryUsageCounter); }上面我們可以看到,在RemoteViews中 有一個叫mActions的列表在維護Action的信息,需要注意的是,這裡靜靜是將Action對象保存了起來了。並未對View進行實際的操作。 接下來我們看RefletctionAction,可以看到,這個表示的是一個反射動作,通過它對View的操作會以反射的方式來調用,其中getMethod就是根據方法名來獲取所需要的Method對象。
@Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final View view = root.findViewById(viewId); if (view == null) return; Class param = getParameterType(); if (param == null) { throw new ActionException("bad type: " + this.type); } try { getMethod(view, this.methodName, param).invoke(view, wrapArg(this.value)); } catch (ActionException e) { throw e; } catch (Exception ex) { throw new ActionException(ex); } } 接下來我們看RemoteViews的apply方法: public View apply(Context context, ViewGroup parent, OnClickHandler handler) { RemoteViews rvToApply = getRemoteViewsToApply(context); View result; Context c = prepareContext(context); LayoutInflater inflater = (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater = inflater.cloneInContext(c); inflater.setFilter(this); result = inflater.inflate(rvToApply.getLayoutId(), parent, false); rvToApply.performApply(result, parent, handler); return result; }
private void performApply(View v, ViewGroup parent, OnClickHandler handler) { if (mActions != null) { handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler; final int count = mActions.size(); for (int i = 0; i < count; i++) { Action a = mActions.get(i); a.apply(v, parent, handler); } } }上面的實現就是遍歷mAction中的Action對象,並且執行它們的apply方法。我們前面看到ReflectionAction的apply就是利用反射機制執行方法,所以,我們可以知道,action#apply其實就是真正執行我們想要的行為的地方。 RemoteViews在通知欄和桌面小部件中的工作過程和上面描述的過程是一樣的,當我們調用RemoteViews的set方法的時候,我們不會更新它們的界面,而是要通過NotificationManager和notify方法和AppWidgetManager的updateAppWidget方法才能更新它們的界面。實際上在AppWigetAManager的updateAppWidget的內部視線中,它們是通過RemoteViews的apply和reapply方法來加載和更新界面的。app會加載並且更新界面,而reapply只會更新界面。通知欄和桌面小插件會在初始化界面的時候調用apply方法,而在後續的更新界面時候會調用reapply方法。 RemoteViews中只支持發起PendingIntent,不支持OnClickListener那種模式,另外,我們需要注意setOnClickPendingIntent、setPendingIntentTemplate以及setOnClickFillIntent它們之前的區別和聯系。首先setOnClickPendingIntent用於給普通View設置單擊事件,因為開銷比較大,所以系統進制了這種方式。其次,如果要給ListView和StackView中的item添加事件,必須要將setPendingIntentTemplate和setOnClickFillIntent組合使用才可以。(使用RemoteViews的setRemoteAdapter 綁定 RemoteViewService) 其他: 前面是使用系統自帶的NotificationManager和AppWidgetManager來使用RemoteViews,那麼除了這兩種情況,我們就不能使用了嗎?肯定不是,我們完全可以自己做NotificationManager和AppWidgetManager一樣的工作。我們可以通過AIDL使用Binder來傳遞RemoteView,也可以通過廣播來傳遞RemoteViews對象。比如我們有2個進程A和B。B可以發送消息給A,然後在A中顯示B所需要顯示的控件。 我們可以創建一個RemoteViews對象,然後把它放入Intent當中,這樣,在廣播接收器我們就能收到這個RemoteViews了。 不過,我們創建RemoteViews的時候,不能直接使用我們的進程上下文來創建。我們可以查看AppWigetHostView的getDefaultView方法:
protected View getDefaultView() { if (LOGD) { Log.d(TAG, "getDefaultView"); } View defaultView = null; Exception exception = null; try { if (mInfo != null) { Context theirContext = mContext.createPackageContextAsUser( mInfo.provider.getPackageName(), Context.CONTEXT_RESTRICTED, mUser); mRemoteContext = theirContext; LayoutInflater inflater = (LayoutInflater) theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater = inflater.cloneInContext(theirContext); inflater.setFilter(sInflaterFilter); AppWidgetManager manager = AppWidgetManager.getInstance(mContext); Bundle options = manager.getAppWidgetOptions(mAppWidgetId); int layoutId = mInfo.initialLayout; if (options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) { int category = options.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY); if (category == AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) { int kgLayoutId = mInfo.initialKeyguardLayout; // If a default keyguard layout is not specified, use the standard // default layout. layoutId = kgLayoutId == 0 ? layoutId : kgLayoutId; } } defaultView = inflater.inflate(layoutId, this, false); } else { Log.w(TAG, "can't inflate defaultView because mInfo is missing"); } } catch (PackageManager.NameNotFoundException e) { exception = e; } catch (RuntimeException e) { exception = e; } if (exception != null) { Log.w(TAG, "Error inflating AppWidget " + mInfo + ": " + exception.toString()); } if (defaultView == null) { if (LOGD) Log.d(TAG, "getDefaultView couldn't find any view, so inflating error"); defaultView = getErrorView(); } return defaultView; }由於不在同一個進程中,往往是兩個APP,因此資源是不能直接找到的,所以,我們想要通過id ,解析出布局對象,那麼就需要我們先獲取遠程進程的進程上下文,通過Context的createPackageContextAsUser來獲取Context對象。之後再解析成對應的布局對象。然後就可以使用了。
Volley簡介我們平時在開發Android應用的時候不可避免地都需要用到網絡技術,而多數情況下應用程序都會使用HTTP協議來發送和接收網絡數據。Androi
經過前面幾篇文章的分析,我們了解到了Google原生是如何拆分apk的,並且我們自己可以通過解析manifest文件,通過創建插件ClassLoader,Resource
一、 Service簡介 Service是android 系統中的四大組件之一(Activity、Service、BroadcastReceiver、ContentPr
在Monkeyrunner做自動化測試時,可以使用模擬器,當然也可以選擇用真機。不過,要想通過電腦來安裝軟件,操作手機,則必須先安裝手機驅動,而且一般手機連接電腦之後,電