一 : Ubuntu Linux下实现QQ的三种方式
1. 步骤最简单,功能最少,非常稳定。二 : PVPlayer的实现方式 - mfrbuaa
关于opencore下多媒体播放,在mediaserver进程里面仅仅有一行代码:
MediaPlayerService::instantiate();
这行代码的作用是初始化一个MediaPlayerService类的实例,并接把他增加到系统的serveceManager中。[www.61k.com]
MediaPlayerService的详细实如今目录frameworks/base/media/libmediaplayerservice中。
在涉及到要播放一个详细的媒体文件时,调用的函数是:
sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url){int32_t connId = android_atomic_inc(&mNextConnId);sp<Client> c = new Client(this, pid, connId, client);LOGV("Create new client(%d) from pid %d, url=%s, connId=%d", connId, pid, url, connId);if (NO_ERROR != c->setDataSource(url)){c.clear();return c;}wp<Client> w = c;Mutex::Autolock lock(mLock);mClients.add(w);return c;}
这个new 了一个Client 而且函数将它返回为sp<IMediaPlayer>。
Client对象什么在文件MediaPlayerService.h中,而且是private类,说明它仅仅被MediaPlayerService对象使用。Client对象继承自BnMediaPlayer,而BnMediaPlaye又继承自BnInterface<IMediaPlayer>,看来是用来响应binder的IPC的函数。
而IMediaPlayer又是何许东东。
IMediaPlayer.cpp在目录frameworks/base/media/中,而IMediaPlayer.h 在目录frameworks/base/include/media中。IMediaPlayer.h中声明了一个IMediaPlayer的类,而它的函数又都是virtual,一看就是用来申明接口的。
MediaPlayerService::create函数调用以后,立即调用Client:setDataSource,事实上如今MediaPlayerService.cpp中,
status_t MediaPlayerService::Client::setDataSource(const char *url){if (strncmp(url, "content://", 10) == 0) { //不太明确,留着以后在研究吧// get a filedescriptor for the content Uri and// pass it to the setDataSource(fd) methodString16 url16(url);int fd = android::openContentProviderFile(url16);if (fd < 0){LOGE("Couldn't open fd for %s", url);return UNKNOWN_ERROR;}setDataSource(fd, 0, 0x7fffffffffLL); // this sets mStatusclose(fd);return mStatus;} else {player_type playerType = getPlayerType(url); //通过url来取得playertype,比如对于midi,就用SONIVOX_PLAYER,mp3,mp4等就用PVPLAYERLOGV("player type = %d", playerType);// create the right type of playersp<MediaPlayerBase> p = createPlayer(playerType); //依据不同的playertype来创建不同的player实例if (p == NULL) return NO_INIT;if (!p->hardwareOutput()) {mAudioOutput = new AudioOutput();static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);}// now set data sourceLOGV(" setDataSource");mStatus = p->setDataSource(url);if (mStatus == NO_ERROR) {mPlayer = p;} else {LOGE(" error: %d", mStatus);}return mStatus;}}
注意这行代码:sp<MediaPlayerBase> p = createPlayer(playerType);
他的作用是依据不同的playerType来创建player实例,我们这里主要关注PVPlayer。说了这么多,最终到达opencore那一层了。有时候会认为android这种设计实在太复杂了,调用起来太麻烦,直接实现一个IMediaPlayer的类不就完了吗。可是细致一样,androide的这种设计方式事实上有它在扩展性和可维护性上才这样做的。说究竟就是一句话,减少模块间的耦合性。
涉及到详细的操作,都是通过实现一个接口类来实现,这样详细的实例在创建的时候就能够通过工厂模式来简单的进行扩展。比如上面所提到的createPlayer(playerType)这个函数,当你须要加入自己的特殊格式的播放器的时候,就不用来改它本来的代码,而仅仅用在createPlayer(playerType)的实现以下几行代码:
case XXX_PLAYER: LOGV(" create XXXFile"); p = new XXXPlayer(); break;
很方便,而这样的扩展性和是由接口和实现的分离带来的,createPlayer 返回的是sp<MediaPlayerBase>类,而去看这个类的代码,发现这个类都是由virtual函数组成的,当你要实现详细实现时候,你能够继承它,然后写好这些virtual函数的实现。
扯远了。设计模式的厉害,可能须要我花整个的职业生涯来体会。
废话不多说,让我们来看PVPlayer的实现,我刚才说过,PVPlayer才是opencore真正的内容。
PVPlayer的申明在frameworks/base/include/media/PVPlayer.h中,而实如今external/opencore/android/playerdriver.cpp。
为什么要这样做?我不懂,我推測还是为了实现和接口的分离,仅仅只是这次的分离就仅仅能简单的通过把头文件和实现文件放到不同目录下来实现。
OK,那么让我们来看看PVPlayer是干嘛的。
PVPlayer继承自MediaPlayerInterface,而MediaPlayerInterface是Opencore对媒体播放的抽象接口的声明类。
那么我们去看看PVPlayer的各个接口的实现吧。PVPlayer的非常多借口都是通过向它的成员mPlayerDriver发送命令来实现,而mPlayerDriver通过调用mPVPlayer的sendEvent函数来告诉PVPlayer,命令是否运行成功了。PlayerDriver是PVPlayer的一个内部成员,它是PVPlayer的命令运行者。
sendEvent的实现例如以下:
void sendEvent(int msg, int ext1=0, int ext2=0) { MediaPlayerBase::sendEvent(msg, ext1, ext2); }
MediaPlayerBase::sendEvent的实现例如以下:
virtual void sendEvent(int msg, int ext1=0, int ext2=0) { if (mNotify) mNotify(mCookie, msg, ext1, ext2); }
mNotify是MediaPlayerBase的一个成员变量,它是一个函数指针,原型例如以下:
typedef void (*notify_callback_f)(void* cookie, int msg, int ext1, int ext2);
这个成员变量在调用createPlayer的时候就调用setNotifyCallback来赋值的。
所以,能够看到,底层的事件会一层一层的往上调用,直至返回给用户层。
这里涉及到2个设计模式:
命令模式和观察者模式。
这两个模式都是设计模式中的基本模式之中的一个,功能强大,逻辑清晰。详细的内容不是本文的重点,在此略过。
三 : WDS的两种实现方式
WDS(Wireless DistributionSystem),无线分发系统是指AP将BSS内的数据发往BSS外或者将BSS外将数据收入BSS内。通过WDS,AP能够将BSS的范围进行扩展。
WDS链路的两端均为AP,如果把AP与BSS内的STATION之间的接口看做Access接口,那么可以把AP与其他AP之间的接口看做WDS接口。一个AP如果与多个外部的AP建立联系,则它就拥有多个WDS接口。
在Access链路上传输的数据帧只有3个地址域:DA,SA和BSSID。在WDS链路上传输的数据帧具有4个地址域:RA,TA,DS和SA。考虑AP内部的数据转发,总共有四种情况:
可见,关键的问题在于数据帧需要从WDS发出时,怎么确定RA,也就是怎么选择WDS接口的问题。有以下两种实现方法。
1. 二层桥接AP
一个数据帧进入AP后,究竟是通过Access接口发出还是通过WDS接口发出是根据DA来决定的。如果DA是本BSS中的某个STATION,则从Access口发出;否则说明这个数据帧发往其它BSS中的STATION的,因此需要由WDS口发出。
如果能够知道每个WDS接口的对端AP中的STATION列表,那么就可以根据DA来决定该数据帧应该通过哪个WDS接口发出,从而就能够确定RA。
这种AP要求STA在向AP发送数据时,必须准确地指定DA。对于本BSS中的其他STA,这个地址很容易获得;而如果是其他BSS中的DA,这个地址就比较难以得到。因此要求AP具备网桥功能,将同一IP子网中其他BSS中的STA的信息也传递到本BSS中。
按照802.11f,每个AP在STATION关联、离开后都会通知其他AP,因此理论上网络中的AP都可以获得所有其它AP的完整STATION列表,从而可以实现桥接转发。
此外,AP之间还应该能够转发APR请求、ARP响应等。
按照这种方式实现的AP为网桥AP。
网桥AP需要支持802.1D规定的特性,具备学习能力、STP等。
2. 三层路由AP
上面一种方法是从横向来考虑,如果从纵向来考虑,则可以采用路由的方法。一个数据帧进入AP后,如果能在本BSS内转发,则通过Ac[www.61k.com]cess口发出;否则需要通过WDS接口发出,但选择哪个WDS接口由更上层的功能决定。
为每个WDS接口设定一个IP子网地址范围(A.B.C.D/M),每个进入的数据帧根据其IP地址来决定应该往哪个WDS接口发出。
按照这种方案实现的WDS转发是三层的路由转发,这种AP是路由AP。
这种AP并不要求STA在向AP发送数据时准确指定DA。尤其是当该数据要发往DS时,STA可能只知道对方IP地址,却不知道MAC地址。
以上两种方案各有优缺点。
桥接转发的好处是效率较高,直接在MAC层就可以转发了。其缺点是灵活性差,只能适用于小规模网络。为了实现比较完备的桥接功能,需要在AP中实现802.1d网桥,具备智能式学习能力和生成树算法STP。
另外,桥接只能保证链路层通,而不能保证网络层通。因此,如果是不同的网段相连,需要路由。
路由转发的好处是可以采用成熟高效的路由算法,而且灵活性高,适用于大规模网络。不过对于小规模网络,采用路由AP可能有些浪费。
四 : Android AsyncTask 的实现及 cancel 方式
最近做一个功能需要用到AsyncTask。(www.61k.com)实现的过程很容易,但是在cancel的时候遇到了一点麻烦。找了很多地方终于找到了比较好的方法,这里跟大家分享一下。
根据Android Developer的介绍 :
AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
AsyncTask其实是Android给开发者提供的一个简单轻量级的多线程类,通过它我们可以很容易新建一个线程做一些耗时的操作,并在这个过程中更新UI。之所以说它轻量级,是因为缺少了直接使用Thread的灵活性。如果是很复杂的操作,还是建议通过Thread来操作,然后通过Broadcast的方式来更新UI。
使用AsyncTask的时候还需要注意,如果AsyncTask在运行过程中使用了某个activity,那么我们就需要在保证AsyncTask的运行过程中这个activity不被destory,或者是在activity的onDestory()方法里面cancel掉这个AsyncTask。但是,如果是一直不让activity被destory的话,相当于不让用户切换界面,这个体验其实时不好的。所以,我们最好使用ProgressDialog,让用户知道我们在努力完成他们的操作,为了让用户可以cancel这个操作,我们可以检测ProgressDialog有没有取消,如果取消了,那就cancel整个AsyncTask。
所以,我们总是会需要cancel AsyncTask。这里贴一段我写的代码,有点点改动。
AsyncTask
1privateclassOpenWebPortalTaskextendsAsyncTask<Void, Void, Void>{2privatestaticfinalString DEFAULT_URL = "https://www.google.com";3privateProgressDialog mProgressDialog =null;4privateActivity mActivity;56publicOpenWebPortalTask(Activity activity){7mActivity =activity;8mProgressDialog =newProgressDialog(mActivity);9mProgressDialog.setMessage(getString(R.string.open_aweb_portal));10mProgressDialog.setCancelable(true);11mProgressDialog.setOnCancelListener(newDialogInterface.OnCancelListener() {1213@Override14publicvoidonCancel(DialogInterface dialog) {15cancel(true);16}17});18}1920@Override21protectedvoidonPreExecute(){22mProgressDialog.show();23}2425@Override26protectedVoid doInBackground(Void... params) {27String schemeUrl =PropertyMgr.getInstance().getPortalURI();28if(TextUtils.isEmpty(schemeUrl)) {29schemeUrl =DEFAULT_ANTI_THEFT_URL;30}31try{32String st =CredentialManager.getInstance().getServiceTicket();33if(st !=null) {34schemeUrl += "?st=" +st;35}36}catch(Exception e) {37SymLog.e(TAG, "Get Service Ticket failed.");38}39if(isCancelled()) {40returnnull;41}42Intent i =newIntent(Intent.ACTION_VIEW);43i.setData(Uri.parse(schemeUrl));44startActivity(i);45returnnull;46}4748@Override49protectedvoidonPostExecute(Void v) {50mActivity.runOnUiThread(newRunnable() {51@Override52publicvoidrun() {53if(mProgressDialog !=null) {54mProgressDialog.dismiss();55mProgressDialog =null;56}57}58});59}60}
这段代码里面,
String st = CredentialManager.getInstance().getServiceTicket();
是个耗时操作,所以需要放在AsyncTask里面。例子中,在onPreExecute中显示ProgressDialog,在onPostExecute中销毁。
注意cancel AsyncTask的方法:
为什么在doInBackgroud中判断?因为AsyncTask的重要工作都是在doInBackgroud中完成,它如果退出了,那么就意味着AsyncTask将被销毁了。
具体在doInBackgroud的哪部分判断是否要退出呢?这就需要根据程序的逻辑。一般在耗时操作的后面加上判断!代码中可以根据需要加多个这样的判断。
AsyncTask用于实现一些简单的多线程任务确实非常简单和好用,希望对大家有所帮助。
本文标题:线程实现的两种方式-Ubuntu Linux下实现QQ的三种方式61阅读| 精彩专题| 最新文章| 热门文章| 苏ICP备13036349号-1