CSipSimple程序之基本功能(二)

3 添加用户

在Sip_Home中,有个添加用户的菜单项。对应的Class为AccountsEditList.class,而目的Class为AccountEditList-Fragment。添加完用户之后显示情况如下图4


图4:AccountsEditListFragmentacitivty界面

而这个class中,对添加用户这一项,如图5示:

    

图5添加用户界面

这里先说明进入AccountEditListFragment之后,程序是如何进入到图5这两步的。首先,添加按钮,然后程序startActivityForResult(new Intent(getActivity(),WizardChooser.class), CHOOSE_WIZARD),进入WizardChooser,WizardChooser是使用ExpandableListView实现的,对于ExpandableListView其实现很简单,其分成多个Group,每个Group又要一些Child数据。然后通过List<Map<>>和List<List<Map<>>>完成数据封装,接着使用适配器把相应的key和value适配到相应的layout上,从而就有图5(左)的情况。

在ExpandableListView中会根据GroupPosition和ChildPosition定位某个组的某个child。通过onChildClickListener来完成事件监听。这里,我们一般点击Generic wizards组下的Basic Child数据。点击完成之后,执行一切步奏之后,进入到这一步setResult(RESULT_OK, result)。从WizardChooser中携带action结果回到AccountsEditListFragment。在AccountsEditListFragment中,会根据前面的action结果,在onActivityResult函数中做action判断,判断完之后跳到BasePrefsWizard这个class,该BasePrefsWizard是继承自GenericPrefs。另外使用了Intent携带extract数据进入BasePrefsWizard的,所携带的数据为wizardId某一类型(这个类型即对应前面的点击Basic的类型),所以相关的Class为Basic.class.注意这里的Basic继承一个称为BaseImplementation的abstract类,该类又继承了WizardIface接口。而上面所说到的wizardId既可以通过这个Id,经过程序的某一函数来获取接口WizardIface实例化。其实这里之所以这样进行,是考虑到多态问题。我们可从左图5中可看出,这里有好多Group,Group下又有好多Child数据,即相应的wizardId很多,每个Id对应的实例有基本相同的地方,但又要区别,要完成所有的wizard显示,所以实现了WizardIface接口,然后虚拟类BaseImplementation又继承自WizardIface接口,而具体的wizard类又继承自BaseImplementation(可看Basicc.class)。这样,在Id传入之后,就可根据Id进行相应wizard的实例显示。

经过了上面的分析之后,我们知道,右图5主要和Basic这个类相关。当设置好上面的数据之后,点击保存按钮之后进入BasePrefsWizard下的saveAccount函数,在该函数中进行了getContentResolver().insert语句,即关于操作ContentProvider的语句。这里根据Uri查询后进入DBProvider的insert方法,进而执行广播命令getContext().sendBroadcast(publishIntent).

对于在系统执行的广播,我们知道只要先在系统注册了广播命令都可以接受广播信息,然后根据广播信息进行相应的操作。在这里,系统注册了很多广播信息,有动态注册的也有静态注册的。静态注册的可查看AndriodMainfest个xml文件中,如图6,动态注册的查看在service注册的页面,在CSipSimple中service为SipService,在registerBroadcasts()函数中完成广播接收注册,如图7。


<!-- Widget 
        	1.AccountWidgetProvider是AppWidgetProvider的一个子类,表明是一个receiver,并接受android.appwidget.action.APPWIDGET_UPDATE
        	2.meta-data为android.appwidget.provider
        	3.用一个xml文件来描述布局属性。这里xml文件为appwidget_info
        -->
        <receiver
            android:name="com.csipsimple.widgets.AccountWidgetProvider"
            android:icon="@drawable/appwidget_preview"
            android:label="@string/gadget_title" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
                <action android:name="com.csipsimple.service.ACCOUNT_CHANGED" />
                <action android:name="com.csipsimple.service.REGISTRATION_CHANGED" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/appwidget_info" />
        </receiver>

图6.静态注册Reciever

/**
	 * 在SipService注册了DynamicReceiver4广播接收器
	 * Register broadcast receivers.
	 */
	private void registerBroadcasts() {
		// Register own broadcast receiver
		if (deviceStateReceiver == null) {
			IntentFilter intentfilter = new IntentFilter();
			/*
			 * 只要是下面这些地址的广播,deviceStateReceiver都能接收得到
			 */
			intentfilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
			intentfilter.addAction(SipManager.ACTION_SIP_ACCOUNT_CHANGED);
			intentfilter.addAction(SipManager.ACTION_SIP_CAN_BE_STOPPED);
			intentfilter.addAction(SipManager.ACTION_SIP_REQUEST_RESTART);
			intentfilter.addAction(DynamicReceiver4.ACTION_VPN_CONNECTIVITY);
			if(Compatibility.isCompatible(5)) {
			    deviceStateReceiver = new DynamicReceiver5(this);
			}else {
			    deviceStateReceiver = new DynamicReceiver4(this);
			}
			/*
			 * 在这里动态注册broadcastReceiever。
			 */
			registerReceiver(deviceStateReceiver, intentfilter);
			deviceStateReceiver.startMonitoring(); //从这里启动检测器
		}
		// Telephony
		if (phoneConnectivityReceiver == null) {
			Log.d(THIS_FILE, "Listen for phone state ");
			phoneConnectivityReceiver = new ServicePhoneStateReceiver();
			
			telephonyManager.listen(phoneConnectivityReceiver, /*PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
					| */PhoneStateListener.LISTEN_CALL_STATE );
		}
		// Content observer
		if(statusObserver == null) {
			/*
			 * 注册内容观察者,所观察的URI为content://com.sipsimple.db/account_status
			 */
        	statusObserver = new AccountStatusContentObserver(serviceHandler);
    		getContentResolver().registerContentObserver(SipProfile.ACCOUNT_STATUS_URI, true, statusObserver);
		}
		
	}

图7,动态注册Reciever

当上面分析的发出广播信息后,主要有两个reciever会做出相应的操作,分别DynamicReceiver4和AccountWidgetProvider。

在DynamicReceiver4中重写了onReceive,即也是接收信息的函数。然后进入判断语句,上面发送的广播所携带的信息为ACTION_SIP_ACCOUNT_CHANGED这样的action,根据这个action,执行了SipService.setAccountRegistration()这个函数,从而跳转到service中执行。这里也就是用户开始注册的位置。

在AccountWidgetProvider这个类中,当接受到ACCOUNT_CHANGED这样的action时,就会进行相应的操作。这里,AccountWidgetProvider是继承了AppWidgetProvider的子类。讲到AppWidgetProvider就必须涉及AppWidget,AppWidgetService以及AppWidgetManager。

AppWidget是个窗口小部件,实现了桌面(Luanch)上显示控件的机制,并能响应用户的点击请求操作,但提供显示的UI元素和对点击事件的相应由Remote端的AppWidgetProvider实现,具体显示是Local的AppWidgetHost通过AppWidgetHostView实现。显示的UI界面如图8所示:


图8 AppWidgetHostView显示

         至于AppWidget系统框架可以查看相关的资料。这里用通俗的语言说下整体的步骤。首先一般情况下本地端有个触发按钮或者触发事件,如一个Button按钮,该按钮在XML文件夹下的某个xml文件中定义。然后如图6所示的,在AndriodManifest文件静态注册。因为是远程更新本地Local的UI界面的,所以可想而知是在远端通过一些操作来进行更新的,而这个远程更新就在继承了AppWidgetProvider的子类中实现的,其一般情况下重写了onUpdate()和onReciever()方法。一般情况下,在首次把view显示在界面的时候,会调用onUpdate(),在本地触发事件的时候,会调用onReciever()方法,该方法通过action判断进行相应界面的RemoteView操作,实现更新。某个事情触发之后,更新后的界面如下图9:


图9 远程更新UI

         另外对于上图的更新,在CSipSimple中也使用了ContentObserver进行更新。ContentObserver是个内容观察者。看过HeadFirst模式的话就知道,其实ContentObserver正是基于观察者模式实现的。所谓观察者,目的就是观察(捕捉)特定的Uri引起的数据库变化,当其所观察的Uri发生改变了,便进行触发。正是因为ContentObserver的实现是基于观察者模式的,所以其使用显得尤其简单,我们只需创建一个特定的ContentObserver派生类(如图10),重载父类构造函数以及onChang()方法处理回调后的功能实现(即观察者模式中某一事件发生变化之后进行notify),然后要进行相应的注册,即注册内容观察者和取消注册的步骤即基本完成操作。如图11:

        

class AccountStatusContentObserver extends ContentObserver {
		
		public AccountStatusContentObserver(Handler h) {
			super(h);
		}

		/*
		 * 当观察到的Uri发生变化时,回调该方法去处理
		 * 使用ContentObserver的情况主要有一下两者情况:
		 * 1、需要频繁检测的数据库或者某个数据是否发生改变,如果使用线程去操作,很不经济而且很耗时 ;
		 * 2、在用户不知晓的情况下对数据库做一些事件,比如:悄悄发送信息、拒绝接受短信黑名单等;
		 * @see android.database.ContentObserver#onChange(boolean)
		 */
		public void onChange(boolean selfChange) {
			System.out.println("to check if the ContentObserver recieve the change signal");
			Log.d(THIS_FILE, "Accounts status.onChange( " + selfChange + ")");
			((BaseAdapter) getListAdapter()).notifyDataSetChanged(); //观察者模式
		}
	}

图10 ContentObserver派生类

// Content observer
		if(statusObserver == null) {
			/*
			 * 注册内容观察者,所观察的URI为content://com.sipsimple.db/account_status
			 */
        	statusObserver = new AccountStatusContentObserver(serviceHandler);
    		getContentResolver().registerContentObserver(SipProfile.ACCOUNT_STATUS_URI, true, statusObserver);
		}

图11ContentObserver注册

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值