編輯:關於android開發
移動互聯網時代,很多用戶趨向於將大量的資料保存在移動設備上。但在給用戶帶來便利的同時引發了一個新的問題——保存在移動設備上的資料該怎樣共享出去?到了思考時間,普通青年這樣想:折騰什麼勁啊,直接用數據線不就行了;而文藝青年可能這樣想:咱們建個群吧,大家有好的東西都可以共享;二逼青年不高興了,都特麼互聯網時代了,來點新意,好麼?直接上網盤啊,大家互相研究研究,你懂的,嘿嘿。然而我是這樣想的:都特麼別BB,技術要以時俱進,來個新潮點的不行麼?直接上Wi-Fi Direct。好用簡單不解釋。那麼我們就迫不及待地開始吧。
Wi-Fi Direct,也叫Wi-Fi P2p,還叫Wi-Fi直連,這是它的名字。但是它和WI-Fi是什麼關系呢,有一腿?我不知道。但是我們可以用我們熟悉的Wi-Fi來理解不熟悉的Wi-Fi Direct。想象這樣的情景,當用Wi-F前我們必須要干什麼啊,那當然是接熱點啊。但是要是沒有熱點怎麼辦?沒熱點玩個毛啊。但是Wi-Fi Direct就可以玩,而且可以玩得很愉快。所以Wi-Fi Direct不用連接熱點,也不需要。沒網也能玩得high,就是這麼任性。那它怎麼玩啊?兄弟,知道藍牙是怎麼玩的麼?不知道,唉,算了。我們還是看名字吧。p2p(peer to peer),也就是點對點,就是我的手機是一個點,你的手機是一個點,duang~~~,一條網線(數據線)怼上了,唉,網絡好了,可以玩耍了,就是怎麼簡單。但是為什麼要用它呢?我跟它又不是太熟,跟藍牙玩耍不好麼?大聲告訴你,不好。普通手機自帶藍牙傳輸距離是多遠,不知道吧,10~13m左右,Wi-Fi Direct呢,長一倍,自己算。傳輸速率呢,你忍受得了藍牙的速度麼?反正我是受不了0.2M/s的速度,但是Wi-Fi Direct 7,8M/s的速度我就很喜歡了。所以,騷年,用Wi-Fi Direct吧,靠譜!
涉及到Wi-Fi,當然理所應當的要用到INTERNET了。哦,不,等等。你之前不是說過,它不用網絡麼,怎麼現在反悔了。是,我是說過它不用網絡也可以玩,但是,你兩台怼在一起的設備間訪問,你總要有種訪問方式吧,Wi-Fi Direct使用Socket,而Socket就需要INTERNET了。另外,它還需要用到CHANGE_WIFI_STATE。因為兩台設備間怼上了只是一種理解,實際上,它是無線訪問,而上面我沒告訴你的是,Wi-Fi Direct兼容Wi-Fi。而且大部分的手機廠商都直接使用Wi-Fi模塊實現的Wi-Fi Direct標准,所以它們通常是綁定在一起的。怼上了就說明連上了,連上了是不是就改變了你的Wi-Fi狀態。最後,還需要ACCESS_WIFI_STATE,這個怎麼理解呢,額,我不知道。自己想去,總之需要就對了。
Android系統將Wi-Fi Direct技術封裝了,當需要使用到該技術時,只需攔截相應的系統廣播即可,系統會把Wi-Fi Direct的硬件狀態通過廣播告訴我們的,在廣播中就可以獲取到所需要的信息,如周圍的設備列表,管理員的IP地址等信息,有了這些信息後就可以建立Socket連接,進行數據操作了。所以廣播接收器可以說是這一大步驟的關鍵。創建廣播接收器很簡單,只需創建一個類繼承自BroadcastReceiver,重寫OnReceive()方法即可。可關鍵在於,在接收到廣播消息後,應該怎樣處理?這就得先看看會接收到些什麼消息了。為此,我整理了個說明表格,如表一:
WIFI_P2P_STATE_CHANGED_ACTION
指示Wi-Fi P2P是否開啟或設備是否支持Wi-Fi Direct
WIFI_P2P_PEERS_CHANGED_ACTION
代表對等節點(peer)列表發生了變化
WIFI_P2P_CONNECTION_CHANGED_ACTION
表明Wi-Fi P2P的連接狀態發生了改變
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
指示設備的詳細配置發生了變化
現在已經明確了,廣播中會接收到的消息,所以,接下來的任務就是怎樣處理這些消息。不過在開始之前,需要對一個關鍵類進行說明——WifiP2pManager。
這個類負責管理Wi-Fi對等設備。它讓應用可以發現可用的對等設備,設置對等設備的連接以及查詢對等設備的列表。需要注意的是,WifiP2pManager對應用請求都是采用的異步處理,所以需要使用到比較多的回調.它的大致工作過程是,首先通過Context的getSystemService方法獲取WifiP2pManager對象,緊接著需要用WifiP2pManager對象的initialize方法對它進行初始化,初始化完成會返回另一個關鍵參數— WifiP2pManager.Channel,之後的發現,連接操作都需要它的參與才能完成。初始化完成後,就可以調用discoverPeers方法進行設備搜尋了。當搜尋到設備後會以廣播的形式通知應用,應用獲取到通知後,需要調用WifiP2pManager對象的requestPeers方法獲取可用的設備列表。當獲取到可用列表後,會以回調的方式通知監聽在WifiP2pManager對象上的onPeersAvailable方法,這裡就可以獲取到相應的設備列表信息,用戶就可以選擇其中一台設備,繼續調用WifiP2pManager對象的connect方法連接設備了,連接成功後,依舊會通知監聽在WifiP2pManager對象上的回調,調用onConnectionInfoAvailable方法,這裡就可以獲取到管理員的IP信息,管理員是誰等信息,之後就是Socket的事了。
明確了WifiP2pManager的角色,接下來就可以處理廣播消息了。首先,WIFI_P2P_STATE_CHANGED_ACTION這個動作很好處理,因為它攜調用帶的消息比較單一,就是給出當前設備是否支持Wi-Fi Direct標准,或者Wi-Fi Direct功能是否開啟。所以當接收到這個消息後,可以針對相應的消息,通知Activity顯示相應的對話框,通知用戶即可,因為這些信息不是軟件能夠處理的。其次,當收到WIFI_P2P_PEERS_CHANGED_ACTION消息時,說明系統已經掃描到相應的設備了,需要把獲取這些信息,通知Activity進行相應處理即可。獲取的操作就是上面提到過的WifiP2pManager,調用WifiP2pManager.requestPeers方法來請求列表。接著處理WIFI_P2P_CONNECTION_CHANGED_ACTION,這個消息說明了Activity已經調用了connect方法了,所以這個消息封裝的都是連接狀態的一些信息,所以這裡需要做的就是判斷連接是否是可用的,如果可用,就將可用信息獲取出來,傳遞給Activity處理。最後來處理WIFI_P2P_THIS_DEVICE_CHANGED_ACTION消息,這個消息其實通常情況下是可以不用處理的,如果想處理也只需在Activity中更新本機設備信息就行了。至此,廣播接收器中的處理都已經結束了,有了它,Activity中的處理就很容易了。
Activity的操作流程也很清晰,其實就是將前面提到的WifiP2pManager的流程拆分出來了,Activity的作用就是注冊廣播,接收廣播傳過來的消息並處理,在必要時解注冊廣播即可。注冊廣播需要創建IntentFilter對象,並添加表一中的所有動作,調用Context.registerReceiver。然後就是WifiP2pManager的上場時間了,具體的流程前面已經描述了,所以這裡只是著重描述一下三個關鍵點。首先是discoverPeers,啟動設備發現,系統會自動處理相關的操作,掃描結束後會以廣播的形式通知應用,這一步很關鍵,沒有這一步,後面的步驟都不會進行。之後需要Activity實現相應的監聽器回調——WifiP2pManager.PeerListListener,當周圍設備信息可用時,並且執行了requestPeers後,會回調onPeersAvailable方法,該方法會傳進來WifiP2pDeviceList對象,調用WifiP2pDeviceList.getDeviceList()就可以獲取到掃描到的設備列表,接下來就可以選取其中一個設備進行connect()操作了。這一步還是需要設置監聽器 WifiP2pManager.ConnectionInfoListener,當連接狀態可用時會觸發 onConnectionInfoAvailable()方法,在這裡通過參數WifiP2pInfo就可以拿到IP地址,誰是組管理員等信息,之後就可以用這些信息建立Socket連接,從而進行數據操作了。假如是管理員就應該創建一個服務線程等待組成員連接進來,假如是普通的用戶則創建Socket連接到管理員的設備,連接建立成功後,可以用IO操作數據了。
其實上面已經說過了,當 onConnectionInfoAvailable()方法調用時,這時候兩台設備已經怼上了。但是怼上了,總有台設備是服務器吧,不然怎麼玩。服務器的地址可以用傳進來的參數WifiP2pInfo對象的groupOwnerAddress查到,自己是不是服務器也可以通過WifiP2pInfo.isGroupOwner確定。至此,就是玩Socket的事了。別告訴我你不會,假如真是不會的話,建議你先看看怎麼玩Socket。因為這個高級玩法目前你還駕馭不了。ok,國際慣例,到了上源碼的時間了。
1 import android.content.BroadcastReceiver;
2 import android.content.Context;
3 import android.content.Intent;
4 import android.net.NetworkInfo;
5 import android.net.wifi.p2p.WifiP2pManager;
6
7 /**
8 * Created by Andy on 2016/5/10.
9 */
10
11 public class WifiDirectReceiver extends BroadcastReceiver {
12 private HandlerActivity handlerActivity;
13 private WifiP2pManager manager;
14
15 public WifiDirectReceiver(){}
16
17 public WifiDirectReceiver(HandlerActivity handlerActivity,WifiP2pManager manager) {
18 this.handlerActivity=handlerActivity;
19 this.manager=manager;
20 }
21
22 @Override
23 public void onReceive(Context context, Intent intent) {
24 String action=intent.getAction();
25 if(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)){
26 /*判斷wifi p2p是否可用*/
27 int state=intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE,-1);
28 if(state==WifiP2pManager.WIFI_P2P_STATE_ENABLED){
29 handlerActivity.setIsWifiP2pEnabled(true);
30 }else {
31 handlerActivity.setIsWifiP2pEnabled(false);
32 }
33 }else if(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)){
34 /*可用設備列表發生變化,*/
35 manager.requestPeers(handlerActivity.getChannel(),handlerActivity);
36 }else if(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)){
37 /*連接狀態發生變化*/
38 NetworkInfo info=intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
39 if(info.isConnected()){
40 manager.requestConnectionInfo(handlerActivity.getChannel(),handlerActivity);
41 }else{
42 handlerActivity.onConnectDisabled();
43 }
44 }else if(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)){
45 /*當前設備發生變化*/
46 }
47 }
48 }
1 import android.app.AlertDialog;
2
3 import android.app.Fragment;
4 import android.app.FragmentManager;
5 import android.app.FragmentTransaction;
6 import android.content.Context;
7 import android.content.DialogInterface;
8 import android.content.Intent;
9 import android.content.IntentFilter;
10 import android.net.wifi.p2p.WifiP2pConfig;
11 import android.net.wifi.p2p.WifiP2pDeviceList;
12 import android.net.wifi.p2p.WifiP2pInfo;
13 import android.net.wifi.p2p.WifiP2pManager;
14 import android.os.Bundle;
15 import android.widget.Toast;
16
17 import com.mob.lee.fastair.BaseActivity;
18 import com.mob.lee.fastair.R;
19 import com.mob.lee.fastair.home.HomeFragment;
20 import com.mob.lee.fastair.utils.ActivityControllerUtil;
21 import com.mob.lee.fastair.utils.WifiP2pUtils;
22
23 import java.net.InetAddress;
24
25 import butterknife.ButterKnife;
26
27 /**
28 * Created by Andy on 2016/5/10.
29 */
30 public class HandlerActivity extends BaseActivity
31 implements WifiP2pManager.PeerListListener,
32 WifiP2pManager.ConnectionInfoListener{
33
34 private WifiP2pManager mManager;
35 private WifiP2pManager.Channel mChannel;
36 private IntentFilter mFilter;
37 private WifiDirectReceiver mWifiDirectReceiver;
38
39 private boolean connected;
40
41 private Fragment currentFragment;
42
43 @Override
44 protected void initView() {
45 setContentView(R.layout.activity);
46 ButterKnife.inject(this);
47 }
48
49 @Override
50 protected void setting(Bundle savedInstanceState) {
51 setCurrentFragment(new DiscoverFragment());
52
53 mFilter = new IntentFilter();
54 /*指示wifi p2p的狀態變化*/
55 mFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
56 /*指示可用節點列表的變化*/
57 mFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
58 /*指示連接狀態的變化*/
59 mFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
60 /*指示當前設備發生變化*/
61 mFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
62
63 /*初始化wifi p2p的控制器*/
64 mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
65 mChannel = mManager.initialize(this, getMainLooper(), null);
66 /*開啟設備發現*/
67 mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
68 @Override
69 public void onSuccess() {
70 }
71
72 @Override
73 public void onFailure(int reason) {
74 new AlertDialog.Builder(HandlerActivity.this)
75 .setTitle(R.string.tips_error)
76 .setMessage(WifiP2pUtils.getWifiP2pFailureReson(reason))
77 .setPositiveButton(R.string.tips_ok, new DialogInterface.OnClickListener() {
78 @Override
79 public void onClick(DialogInterface dialog, int which) {
80 finish();
81 }
82 }).show();
83 }
84 });
85
86 /*注冊廣播*/
87 mWifiDirectReceiver = new WifiDirectReceiver(this, mManager);
88 registerReceiver(mWifiDirectReceiver, mFilter);
89 }
90
91 @Override
92 protected void onDestroy() {
93 super.onDestroy();
94 /*動態注冊的廣播必須解注冊*/
95 unregisterReceiver(mWifiDirectReceiver);
96 if(connected) {
97 /*現在我不用了,但是還連著,我不高興,移除吧,下次再連*/
98 mManager.removeGroup(mChannel, new WifiP2pManager.ActionListener() {
99 @Override
100 public void onSuccess() {
101
102 }
103
104 @Override
105 public void onFailure(int reason) {
106 Toast.makeText(HandlerActivity.this, getResources().getString(R.string.toast_removeFalid) + WifiP2pUtils.getWifiP2pFailureReson(reason), Toast.LENGTH_SHORT).show();
107 }
108 });
109 }
110 }
111
112
113 public void setIsWifiP2pEnabled(boolean enabled) {
114 /*設備是否支持Wi-Fi Direct或者打開開關,通知一下*/
115 if (!enabled) {
116 new AlertDialog.Builder(this)
117 .setTitle(R.string.tips_error)
118 .setMessage(R.string.tips_disabled_wifi_p2p)
119 .setPositiveButton(R.string.tips_ok, new DialogInterface.OnClickListener() {
120 @Override
121 public void onClick(DialogInterface dialog, int which) {
122 finish();
123 }
124 }).show();
125 }
126 }
127
128 public WifiP2pManager.Channel getChannel() {
129 return mChannel;
130 }
131
132 /*發現周圍設備了*/
133 @Override
134 public void onPeersAvailable(WifiP2pDeviceList peers) {
135 if(currentFragment instanceof DiscoverFragment) {
136 ((DiscoverFragment)currentFragment).findDeviceList(peers.getDeviceList());
137 }
138 }
139
140 /*連接設備*/
141 public void connectDevice(WifiP2pConfig config){
142 mManager.connect(mChannel, config, new WifiP2pManager.ActionListener() {
143 @Override
144 public void onSuccess() {
145 Toast.makeText(HandlerActivity.this, R.string.toast_connectSuccess, Toast.LENGTH_SHORT).show();
146 }
147
148 @Override
149 public void onFailure(int reason) {
150 new AlertDialog.Builder(HandlerActivity.this)
151 .setTitle(R.string.tips_error)
152 .setMessage(WifiP2pUtils.getWifiP2pFailureReson(reason))
153 .setPositiveButton(R.string.tips_ok, new DialogInterface.OnClickListener() {
154 @Override
155 public void onClick(DialogInterface dialog, int which) {
156 dialog.dismiss();
157 }
158 }).show();
159 }
160 });
161 }
162
163 /*連接完成,獲取管理員的IP,跳轉界面*/
164 @Override
165 public void onConnectionInfoAvailable(WifiP2pInfo info) {
166 InetAddress address = null;
167 boolean isGroupOwner = false;
168 if (info.groupFormed && info.isGroupOwner) {
169 address = info.groupOwnerAddress;
170 isGroupOwner = true;
171 } else if (info.groupFormed) {
172 address = info.groupOwnerAddress;
173 isGroupOwner = false;
174 }
175 if (null != address) {
176 Intent preIntent = getIntent();
177 preIntent.putExtra("address", address.getHostAddress());
178 preIntent.putExtra("isGroupOwner", isGroupOwner);
179 Fragment fragment=null;
180 setCurrentFragment(fragment);
181 connected=true;
182 }
183 }
184
185 public void onConnectDisabled(){
186 connected=false;
187 }
188
189 public void setCurrentFragment(Fragment fragment){
190 currentFragment=fragment;
191 FragmentManager manager=getFragmentManager();
192 FragmentTransaction transaction = manager.beginTransaction();
193 transaction.replace(R.id.activity_content, fragment);
194 transaction.commit();
195 }
196
197 }
1 import android.app.AlertDialog;
2 import android.content.DialogInterface;
3 import android.net.wifi.WpsInfo;
4 import android.net.wifi.p2p.WifiP2pConfig;
5 import android.net.wifi.p2p.WifiP2pDevice;
6 import android.os.Bundle;
7 import android.view.LayoutInflater;
8 import android.view.View;
9 import android.view.ViewGroup;
10 import android.widget.ImageView;
11 import android.widget.RadioGroup;
12 import android.widget.TextView;
13
14 import com.mob.lee.fastair.BaseActivity;
15 import com.mob.lee.fastair.BaseFragment;
16 import com.mob.lee.fastair.R;
17 import com.mob.lee.scanview.ScanLayout;
18
19 import java.util.ArrayList;
20 import java.util.Collection;
21
22 import butterknife.ButterKnife;
23 import butterknife.InjectView;
24
25 /**
26 * Created by Andy on 2016/5/10.
27 */
28 public class DiscoverFragment extends BaseFragment{
29 @InjectView(R.id.fragment_discover_scanlayout)
30 ScanLayout mScanLayout;
31
32 private WifiP2pDevice device;
33 private ArrayList<WifiP2pDevice> mDevices;
34 private boolean isBeginConnection;
35
36 @Override
37 protected View initView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
38 View view=inflater.inflate(R.layout.fragment_discover,container,false);
39 ButterKnife.inject(this, view);
40 return view;
41 }
42
43 @Override
44 protected void setListener(View view, Bundle saveInstanceState) {
45 /*連接某台設備*/
46 mScanLayout.setOnItemClickListener(new ScanLayout.OnItemClickListener() {
47 @Override
48 public void onItemClick(View view, int position) {
49 device = mDevices.get(position);
50 WifiP2pConfig config = new WifiP2pConfig();
51 config.deviceAddress = device.deviceAddress;
52 config.wps.setup = WpsInfo.PBC;
53 ((HandlerActivity) getActivity()).connectDevice(config);
54 isBeginConnection=true;
55 }
56 });
57 }
58
59 @Override
60 protected void setting(Bundle saveInstanceState) {
61 ((BaseActivity)getActivity()).setToolbar(R.id.fragment_discover_toolbar, R.string.base_findDevice);
62 }
63
64 @Override
65 public void onResume() {
66 super.onResume();
67 mScanLayout.startScan();
68 }
69
70 @Override
71 public void onDestroyView() {
72 super.onDestroyView();
73 ButterKnife.reset(this);
74 }
75
76 /*發現了設備,顯示設備列表*/
77 public void findDeviceList(Collection<WifiP2pDevice> devices) {
78 if ((null == mDevices)) {
79 mDevices=new ArrayList<>();
80 }
81 mScanLayout.stopScan();
82 if (0 == devices.size()) {
83 new AlertDialog.Builder(getActivity())
84 .setTitle(R.string.tips_error)
85 .setMessage(R.string.tips_scan_failed)
86 .setPositiveButton(R.string.tips_ok, new DialogInterface.OnClickListener() {
87 @Override
88 public void onClick(DialogInterface dialog, int which) {
89 getActivity().finish();
90 }
91 }).show();
92 } else if(isBeginConnection){
93 return;
94 }else{
95 for(WifiP2pDevice device:devices){
96 if(mDevices.contains(device)){
97 continue;
98 }
99 mDevices.add(device);
100 View view = LayoutInflater.from(getActivity()).inflate(R.layout.item_scan, null);
101 ((TextView) view.findViewById(R.id.item_scan_name)).setText(device.deviceName);
102 mScanLayout.addView(view);
103 }
104 }
105 }
106 }
Android中BroadcastReceiver的兩種注冊方式(靜態和動態)詳解,broadcastreceiver
Android中BroadcastReceiver的兩種注冊方式(靜態和動態)詳解,broadcastreceiver今天我們一起來探討下安卓中BroadcastRece
Android 代碼動態改變View的屬性
Android 代碼動態改變View的屬性 設置Android View的長寬和位置我們平時都會在Layout的XML中定義,那麼什麼時候需要動態在代碼中設置View
Android開發學習——SQLite數據庫與單元測試,androidsqlite
Android開發學習——SQLite數據庫與單元測試,androidsqliteSQLite數據庫 輕量級關系型數據庫 創建數據庫需要使用的api:
Android中訪問sdcard路徑的幾種方式,androidsdcard
Android中訪問sdcard路徑的幾種方式,androidsdcard以前的Android(4.1之前的版本)中,SDcard路徑通過“/sdcard”或者“/mnt