編輯:關於Android編程
在前面的UI分析的文章中我們已經發現,其實不管是設置中的開關和fragment之後的開關最終都是關聯到BluetoothEnabler中去的,所以,我們直接去看這個裡面對於開關的處理,開關的處理當然就是onCheckedChanged這個函數了,哈哈~~直接分析。。
1、藍牙打開的按鍵處理
public void>1.1 setBluetoothEnabled分析
在按鈕打開的時候,就是通過這個函數來和framework層的adapter進行交互的。
public void setBluetoothEnabled(boolean enabled) { //根據傳入的enabled值,決定是打開還是關閉 //我們這邊必然就是調用enable了 //具體分析見1.1.1 boolean success = enabled ? mAdapter.enable() : mAdapter.disable(); //這裡就是設置狀態為正在打開 //注意的是這裡success是enable或者disable的返回值,不是enabled的值哦,呵呵~~ if (success) { //所以,會在enable或者disable返回後面設置狀態。 //當然這個返回並不是說藍牙打開成功了,因為藍牙打開的操作是異步的 //詳細見1.1.2 setBluetoothStateInt(enabled ? BluetoothAdapter.STATE_TURNING_ON : BluetoothAdapter.STATE_TURNING_OFF); } else { if (Utils.V) { Log.v(TAG, "setBluetoothEnabled call, manager didn't return " + "success for enabled: " + enabled); } //若是失敗了,還是要同步一下狀態的。 //就是把adapter的狀態和當前狀態進行一下同步 syncBluetoothState(); } }
1.1.1 bluetoothAdapter的enable函數分析
其實我們會發現,這個函數最終還是調用的bluetoothService中的enable函數,所以,這裡就不詳細說了,直接去看bluetoothService中的enable
/** Bring up BT and persist BT>1.1.2 setBluetoothStateInt
這個函數是上層對藍牙狀態改變的操作,就是設置成不同的狀態:
synchronized void setBluetoothStateInt(int state) { //其實針對turning>2.深入理解藍牙狀態變換所做的工作
我們在藍牙狀態變換的那篇文章中仔細的分析了藍牙打開所涉及的一些操作,然而當時的重點還是分析了狀態之間的變換,並沒有分析真正去做了些什麼,這篇文章下面的內容將會深入去分析一下這些內容。
現在我們先假設quick swtich是關閉的,也就是說在我們打開之前藍牙的狀態是位於poweroff的,我們就從這邊開始來分析好了:
private class PowerOff extends State { @Override public void enter() { if (DBG) log("Enter PowerOff: " + getCurrentMessage().what); } @Override public boolean processMessage(Message message) { log("PowerOff process message: " + message.what); boolean retValue = http://blog.csdn.net/u011960402/article/details/HANDLED; switch(message.what) { case USER_TURN_ON: // starts turning>2.1 bluetoothAdapter狀態轉換廣播的處理分析
我們先來看一下究竟是發出的那個broadcast,然後再來看有哪些地方注冊了這個broadcast的receiver。
private void broadcastState(int newState) { log("Bluetooth state " + mPublicState + " -> " + newState); // mPublicState這是用來保存目前狀態的變量,會先進行比較,看是否真的改變了 //若是沒有改變也就不需要做什麼了 if (mPublicState == newState) { return; } //否則就需要發送BluetoothAdapter.ACTION_STATE_CHANGED的broadcast Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); //兩個參數 //一個就是原有的狀態,一個是新的狀態 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mPublicState); intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mPublicState = newState; mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM); }
2.1.1注冊的BluetoothAdapter.ACTION_STATE_CHANGED的receiver
1)BluetoothEnabler中的receiver:
這個地方主要是對按鈕的處理,我們知道BluetoothEnabler主要是對bluetooth的打開和關閉進行處理的,所以很容易就聯想到,在打開的時候按鈕反灰之類的操作應該是這裡來實現的,我們來看具體的代碼:
public void> 2)BluetoothEventManager中的handler
我們前面分析過BluetoothEventManager是用來從BluetoothAPI中接收broadcast,並把他們分析到對應的UI線程中去。
這句代碼表明了對broadcast的處理:
// Bluetooth> A、BluetoothEventManager中注冊的callback的分析
我們從代碼中搜索一下會發現,真正注冊到這裡的callback只有一個,就是DeviceListPreferenceFragment,它的代碼如下:
public void>在前面,我們看到的就是BluetoothSettings是擴展這個類的,然而不幸的時候,BluetoothSettings中有自己的onResume函數,並不會走到這裡,所以,我們再來看看這裡的callback還有別的地方會用到麼?
我們發現除了BletoothSettings還有一個地方,就是DevicePickerFragment也是擴展這個類的,然而同樣的,它也重寫了onResume函數,哈哈,原來的這個callback沒有人注冊啊,所以也就不會調用了。呵呵~~
至此BluetoothEventManger中的handler就分析完成了,不幸的是好像什麼工作都沒有做。好吧,我們過去了。
3)其它。
其實還有一些別的地方也注冊了這個broadcast,但是至少目前在打開的時候他們還沒有注冊,所以,這裡就暫時不分析了,我們到了具體的情景時在去具體分析好了。比如以下地方:
1)BluetoothA2dpService,BluetoothController.Java,PhoneStatusBarPolicy.java等,這些地方基本都是一些狀態位的重置。
2)BluetoothOppBtEnablingActivity.java,BluetoothOppReceiver.java,BluetoothPbapReceiver.java,BluetoothHeadsetService.java這些地方都是對一些profile的處理,我們在具體分析到這裡的時候再來具體看。
3)BluetoothNameDialogFragment,這個只有在打開了修改名字才會使用到。
4)RequestPermissionActivity,這個是別的應用要求搜索或者打開藍牙的時候才會使用。
5)SettingsAppWidgetProvider,窗口小部件中進行一些功耗相關的控制的時候會用到。
以上幾個,我們暫時就不會進行分析了,避免分叉得太厲害。
所以,從這裡可以看到,基本在正在打開這個狀態,就只有對按鈕的一個反灰的操作了,其它的就沒有什麼別的特殊的了。
2.2 prepareBluetooth的分析
從注釋來看,這個函數就是打開藍牙模塊了,包括加載fw等一系列操作,各家的方案的區別可能就是體現在這裡了,所以,需要根據不同的廠商來進行不同的設置了。理所當然的是,這裡就有了各自的verdon operation了。需要注意的是他是不能discoverable和connectable,這也是因為在quick swtich打開的情況下,讓外界並不能發現我們是打開的。
private boolean prepareBluetooth() { //這是一個jni層的操作,涉及的內容及其廣泛,我們會在3中進行詳細介紹 if (mBluetoothService.enableNative() != 0) { return false; } // try to start event loop, give 2 attempts //啟動event loop,event loop是framework層用來接收來自jni層各種event的代碼,他會根據不同的event來所不同的操作 int retryCount = 2; boolean eventLoopStarted = false; //這裡會嘗試進行開始兩次,每次會有一個eventloop run的檢查,會檢查5次,每次間隔100ms,所以這裡最長大概會有1s左右的時間,事實上,我們並不會有這麼長的時間,呵呵~~ while ((retryCount-- > 0) && !eventLoopStarted) { //詳細見2.2.1 mEventLoop.start(); // it may take a moment for the other thread to do its // thing. Check periodically for a while. int pollCount = 5; while ((pollCount-- > 0) && !eventLoopStarted) { if (mEventLoop.isEventLoopRunning()) { eventLoopStarted = true; break; } try { Thread.sleep(100); } catch (InterruptedException e) { log("prepareBluetooth sleep interrupted: " + pollCount); break; } } } //沒有能夠啟動eventloop會直接把藍牙disable掉的 if (!eventLoopStarted) { mBluetoothService.disableNative(); return false; } // get BluetoothService ready //緊接著的BluetoothService中的一些處理,詳細見2.2.2 if (!mBluetoothService.prepareBluetooth()) { mEventLoop.stop(); mBluetoothService.disableNative(); return false; } //這裡會在10s之後發送一個timeout的msg,以用來恢復狀態機,詳細見我狀態機變化的那篇文章的分析。 sendmessageDelayed(PREPARE_BLUETOOTH_TIMEOUT, PREPARE_BLUETOOTH_TIMEOUT_TIME); return true; } }
2.2.1 Eventloop的啟動
BluetoothEventLoop在狀態機初始化的時候就已經建構了,這裡就不再分析了。我們直接從start函數來看,看eventloop究竟做了什麼:
/* package */ void start() { //看是否已經在run了 if (!isEventLoopRunningNative()) { if (DBG) log("Starting Event Loop thread"); //開始eventloop startEventLoopNative(); } }
很快大家就發現,這原來還是要到jni層啊,看來我們還是要好好看進去哦,不知道這個和enableNative會不會有關系,畢竟他是在前面啊,所以,這裡我們還是不著急分析,把它放到enableNative分析完成之後再去看。詳細分析見4。
2.2.2 BluetoothService中的PrepareBluetooth函數
/* package */ synchronized boolean prepareBluetooth() { //建立native的data,就是一些數據的關聯,沒有什麼實在的東西 if (!setupNativeDataNative()) { return false; } //那powered的property設為false,就是把discoverable和connectable設為false了,為什麼這麼做,上面已經講過了 switchConnectable(false); //更新sdp列表,這個我們需要重點看一下,詳細分析見下面的A updateSdpRecords(); return true; }
A、updateSdpRecords分析
private synchronized void updateSdpRecords() { ArrayListuuids = new ArrayList (); // Add the default records //支持HSP的AG端 uuids.add(BluetoothUuid.HSP_AG); //支持opp uuids.add(BluetoothUuid.ObexObjectPush); //看是否支持voice,這裡是支持的話,就加入HFP的AG和pbap if (mContext.getResources(). getBoolean(com.android.internal.R.bool.config_voice_capable)) { uuids.add(BluetoothUuid.Handsfree_AG); uuids.add(BluetoothUuid.PBAP_PSE); } // Add SDP records for profiles maintained by Android userspace //加入支持的uuid,這裡詳細分析見下面的2.2.2.1 addReservedSdpRecords(uuids); // Enable profiles maintained by Bluez userspace. //使能包含的profile,這個又通過jni層去實現了,所以,我們同樣放到後面2.2.2.2中分析 setBluetoothTetheringNative(true, BluetoothPanProfileHandler.NAP_ROLE, BluetoothPanProfileHandler.NAP_BRIDGE); // Add SDP records for profiles maintained by Bluez userspace //上面那些uuid是通過bluez去控制的,所以我們會有一些交互 //下面這幾個是上層直接控制就可以了 //audiosource,avrcpTarget和NAP的角色 uuids.add(BluetoothUuid.AudioSource); uuids.add(BluetoothUuid.AvrcpTarget); uuids.add(BluetoothUuid.NAP); // Cannot cast uuids.toArray directly since ParcelUuid is parcelable //因為uuids是parcel格式的,所以,我們一個一個讀出來好了,加入到mAdapterUuids中 mAdapterUuids = new ParcelUuid[uuids.size()]; for (int i = 0; i < uuids.size(); i++) { mAdapterUuids[i] = uuids.get(i); } }
2.2.2.1 uuid的記錄
SDP相關的注冊
private synchronized void addReservedSdpRecords(final ArrayListuuids) { //Register SDP records. int[] svcIdentifiers = new int[uuids.size()]; for (int i = 0; i < uuids.size(); i++) { //從128bit的uuid得到16bit/32bit的內容 svcIdentifiers[i] = BluetoothUuid.getServiceIdentifierFromParcelUuid(uuids.get(i)); } //這個就到了jni層的分析了,我們在後面會詳細分析 mAdapterSdpHandles = addReservedServiceRecordsNative(svcIdentifiers); }
至此,jni層之上的所有操作都已經全部分析完成了,後面我們在進行分析jni層之下的內容,那又將是一個非常龐大的體系。
關鍵點 canvas.drawBitmap(bitmap, srcRect, dstRect, null); 將bitmap的srcRect區域繪制到canva
最近仔細研究了下TabHost,主要是為了實現微信底部導航欄的功能,最後也給出一個文章鏈接,大家不要著急正文:TabHost的實現分為兩種,一個是不繼承TabActivi
JsonOnlineViewer可實現直接在android studio中調試接口數據,可以選擇請求類型,自定義請求頭及請求體,json數據格式化後展示 下載完
一、原理分析 package com.njupt.action; import java.io.IOException; import java.io.Pr