編輯:關於Android編程
最近通過Google學習了兩部設備之間通過藍牙連接實現聊天的功能,以下為代碼:
/**
* BlueToothChat:開啟藍牙,300s可見,連接設備,線程UI處理,聊天設置,整個界面功能的實現
* @author micro
*
*/
public class BlueToothChat extends Activity {
//Handler的處理碼
public static final int MESSAGE_STATE_CHANGE = 1;
public static final int MESSAGE_READ = 2;
public static final int MESSAGE_WRITE = 3;
public static final int MESSAGE_DEVICE_NAME = 4;
public static final int MESSAGE_TOAST = 5;
//設備名和TOAST
public static final String DEVICE_NAME = "device_name";
public static final String TOAST = "toast";
//請求碼
public static final int REQUEST_CONNECT_DEVICE = 2;
public static final int REQUEST_ENABLE_BT = 3;
//控件
private ListView mConversation;
private EditText mEdit;
private Button sendBtn;
private String mConnectedServiceName; //設備名
private ArrayAdapter mConversationAdapter; //聊天設配器
private StringBuffer mOutString ; //輸出的信息
private BluetoothAdapter mBluetoothAdapter = null; //藍牙適配器
private BlueToothChatService mBlueToothChatService = null; //服務端
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//獲取默認的藍牙適配器
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(mBluetoothAdapter==null){
Toast.makeText(getApplicationContext(), "此設備不支持藍牙", Toast.LENGTH_SHORT).show();
this.finish(); //結束
return ;
}
}
/**
* 在onStart方法中開啟藍牙
*/
@Override
public void onStart(){
super.onStart();
//如果藍牙沒開,則開啟藍牙,開啟後回調REQUEST_ENABLE_BT
if(!mBluetoothAdapter.isEnabled()){
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent,REQUEST_ENABLE_BT);
}else{
if(mBlueToothChatService == null){
//設置聊天
setupChat();
}
}
}
/**
* 在onResume方法中開啟服務端裡的線程
*/
@Override
public void onResume(){
super.onResume();
//只有滿足以下的兩個條件之後我們才會開啟
if(mBlueToothChatService!=null){
if(mBlueToothChatService.getState()==BlueToothChatService.STATE_NONE){
mBlueToothChatService.start();
}
}
}
/**
* 聊天設置,初始化及監聽
*/
private void setupChat() {
// TODO Auto-generated method stub
//初始化ListView
mConversationAdapter = new
ArrayAdapter(this,R.layout.message);
mConversation = (ListView)findViewById(R.id.chat_list);
mConversation.setAdapter(mConversationAdapter);
//發送按鈕
sendBtn = (Button)findViewById(R.id.send_btn);
sendBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
TextView mTextView = (TextView)findViewById(R.id.edit_text);
String message = mTextView.getText().toString();
sendMessage(message);
}
});
//Edit監聽
mEdit = (EditText)findViewById(R.id.edit_text);
mEdit.setOnEditorActionListener(mWriteLisentner);
//初始化
mBlueToothChatService = new BlueToothChatService(this,handler);
mOutString = new StringBuffer("");
}
//發送信息到Handler處理
private void sendMessage(String message) {
// TODO Auto-generated method stub
if(mBlueToothChatService.getState()!=BlueToothChatService.STATE_CONNECTED){
Toast.makeText(getApplicationContext(), R.string.not_connected, Toast.LENGTH_SHORT).show();
return ;
}
if(message.length()>0){
//在這裡發送信息
byte[] send = message.getBytes();
mBlueToothChatService.write(send);
//重置清空
mOutString.setLength(0);
mEdit.setText(mOutString);
}
}
/**
* 監聽編輯器的結果
*/
private TextView.OnEditorActionListener mWriteLisentner = new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
// TODO Auto-generated method stub
//如果結果為空,提示
if(actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP){
String message = view.getText().toString();
sendMessage(message);
}
return true;
}
};
//通過ActionBar顯示
private final void setStatus(int resId){
final ActionBar actionBar = getActionBar();
actionBar.setSubtitle(resId);
}
public final void setStatus(CharSequence subTitle){
final ActionBar actionBar = getActionBar();
actionBar.setSubtitle(subTitle);
}
//handler處理機制
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg){
switch(msg.what){
case MESSAGE_STATE_CHANGE:
switch(msg.arg1){
case BlueToothChatService.STATE_CONNECTED:
setStatus( "connected to "+mConnectedServiceName);
mConversationAdapter.clear();
break;
case BlueToothChatService.STATE_CONNECTING:
setStatus(R.string.title_connecting);
break;
case BlueToothChatService.STATE_LISTEN:
case BlueToothChatService.STATE_NONE:
setStatus(R.string.title_not_connected);
break;
}
break;
case MESSAGE_WRITE:
byte[] writebuf = (byte[]) msg.obj;
String writemessage = new String(writebuf);
mConversationAdapter.add("Me: "+writemessage);
break;
case MESSAGE_READ:
byte[] readbuf = (byte[])msg.obj;
String readmessage = new String(readbuf, 0, msg.arg1);
mConversationAdapter.add(mConnectedServiceName+": "+readmessage);
break;
case MESSAGE_TOAST:
//這個好像沒用到
Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST), Toast.LENGTH_SHORT).show();
break;
case MESSAGE_DEVICE_NAME:
//保存鏈接設備的名稱
mConnectedServiceName = msg.getData().getString(DEVICE_NAME);
Toast.makeText(getApplicationContext(), "connect to "+mConnectedServiceName, Toast.LENGTH_SHORT).show();
break;
}
}
};
//請求碼的處理
@Override
public void onActivityResult(int requestCode,int resultCode,Intent data){
//通過請求碼來判斷是否與結果碼一樣,然後做處理
switch(requestCode){
//請求成功則鏈接設備
case REQUEST_CONNECT_DEVICE:
if(resultCode == Activity.RESULT_OK){
connectDevice(data);
}
break;
case REQUEST_ENABLE_BT:
if(resultCode == Activity.RESULT_OK){
//設備可用,進入setupChat()
setupChat();
}else{
Toast.makeText(getApplicationContext(), R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();
finish();
}
}
}
/**
* 連接設備
* @param data
*/
public void connectDevice(Intent data) {
// TODO Auto-generated method stub
//獲取Mac地址
String address = data.getExtras().
getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
//通過地址獲取設備
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
//然後連接
mBlueToothChatService.connect(device);
}
@Override
public boolean onCreateOptionsMenu(Menu menu){
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.option_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item){
Intent serverIntent =null;
switch (item.getItemId()) {
//掃描連接
case R.id.connect_scan:
serverIntent = new Intent(this,DeviceListActivity.class);
startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
return true;
//可發現300s內
case R.id.discoverable:
ensureDiscovery();
return true;
}
return false;
}
//設置300s可被發現
private void ensureDiscovery() {
// TODO Auto-generated method stub
if(mBluetoothAdapter.getScanMode()!=BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE){
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
}
}
@Override
public synchronized void onPause(){
super.onPause();
}
@Override
public void onStop(){
super.onStop();
}
@Override
public void onDestroy(){
super.onDestroy();
if(mBlueToothChatService!=null){
mBlueToothChatService.stop();
}
}
}
接下來是
/**
* BlueToothChatService實現的線程開啟,關閉,管理以及連接
* @author micro
*
*/
public class BlueToothChatService {
private static int mState;
public static final String NAME = "BlueToothChat";
public static final UUID MY_UUID = UUID.fromString("0001101-0000-1000-8000-00805F9B34FB");
//
private final Handler mHandler ;
//三個線程
private ConnectThread mmConnectThread ;
private ConnectedThread mmConnectedThread;
private AcceptThread mmAcceptThread;
//表示當前的連接狀態
public static final int STATE_NONE = 0;
public static final int STATE_LISTEN = 1;
public static final int STATE_CONNECTING = 2;
public static final int STATE_CONNECTED = 3;
//藍牙適配器
private BluetoothAdapter mAdapter;
//構造器初始化
public BlueToothChatService(Context context, Handler handler) {
// TODO Auto-generated constructor stub
mAdapter = BluetoothAdapter.getDefaultAdapter();
mState = STATE_NONE;
mHandler = handler;
}
//設置當前狀態
public synchronized void setState(int state){
mState = state;
//UI線程處理
mHandler.obtainMessage(BlueToothChat.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
}
/**
* Return the current connection state.
*/
public synchronized int getState() {
return mState;
}
/**
* 開啟AcceptThread線程
*/
public synchronized void start() {
// TODO Auto-generated method stub
if(mmConnectedThread!=null){
mmConnectedThread.cancel();
mmConnectedThread = null;
}
if(mmConnectThread!=null){
mmConnectThread.cancel();
mmConnectThread=null;
}
//服務端線程開啟
if(mmAcceptThread == null){
mmAcceptThread = new AcceptThread();
mmAcceptThread.start();
}
//設置為監聽狀態
setState(STATE_LISTEN);
}
/**
* 開啟一個設備連接線程
* @param device
*/
public synchronized void connect(BluetoothDevice device) {
// TODO Auto-generated method stub
if(mState == STATE_CONNECTING){
if(mmConnectThread!=null){
mmConnectThread.cancel();
mmConnectThread = null;
}
}
if(mmConnectedThread!=null){
mmConnectedThread.cancel();
mmConnectedThread = null;
}
//開啟
mmConnectThread = new ConnectThread(device);
mmConnectThread.start();
setState(STATE_CONNECTING);
}
/**
* 開啟一個已經連接的線程來管理藍牙連接
* Start the ConnectedThread to begin managing a Bluetooth connection
* @param socket
* @param device
* @param socketType
*/
public synchronized void connected(BluetoothSocket socket,
BluetoothDevice device, final String socketType){
if(mmConnectedThread!=null){
mmConnectedThread.cancel();
mmConnectedThread = null;
}
if(mmConnectThread!=null){
mmConnectThread.cancel();
mmConnectThread = null;
}
if(mmAcceptThread!=null){
mmAcceptThread.cancel();
mmAcceptThread = null;
}
mmConnectedThread = new ConnectedThread(socket, socketType);
mmConnectedThread.start();
// Send the name of the connected device back to the UI Activity
Message message = mHandler.obtainMessage(BlueToothChat.MESSAGE_DEVICE_NAME);
Bundle bundle = new Bundle();
bundle.putString(BlueToothChat.DEVICE_NAME, device.getName());
message.setData(bundle);
mHandler.sendMessage(message);
setState(STATE_CONNECTED);
}
/**
* 停止所有的線程
*/
public synchronized void stop() {
// TODO Auto-generated method stub
if(mmConnectedThread!=null){
mmConnectedThread.cancel();
mmConnectedThread = null;
}
if(mmConnectThread!=null){
mmConnectThread.cancel();
mmConnectThread = null;
}
if(mmAcceptThread!=null){
mmAcceptThread.cancel();
mmAcceptThread = null;
}
setState(STATE_NONE);
}
/**
* Write to the ConnectedThread in an unsynchronized manner
*
* @param out
* The bytes to write
* @see ConnectedThread#write(byte[])
*/
public void write(byte[] out) {
// Create temporary object
ConnectedThread r;
// Synchronize a copy of the ConnectedThread
synchronized (this) {
if (mState != STATE_CONNECTED)
return;
r = mmConnectedThread;
}
// Perform the write unsynchronized
//線程調用寫入方法
r.write(out);
}
/**
* failed處理
* @author micro
*
*/
public void connectionFailed(){
Message msg = mHandler.obtainMessage(BlueToothChat.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(BlueToothChat.TOAST, "Device connection failed to connect");
msg.setData(bundle);
mHandler.sendMessage(msg);
//重啟對Socket的監聽線程,即服務端的線程
BlueToothChatService.this.start();
}
/**
*
* @author micro
*
*/
public void connectionLost(){
Message msg = mHandler.obtainMessage(BlueToothChat.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(BlueToothChat.TOAST, "Device connection was lost");
msg.setData(bundle);
mHandler.sendMessage(msg);
//重啟對Socket的監聽線程,即服務端的線程
BlueToothChatService.this.start();
}
//ServerSocket
private class AcceptThread extends Thread{
private final BluetoothServerSocket serverSocket;
private String socketType;
public AcceptThread(){
BluetoothServerSocket temp =null;
try {
temp = mAdapter.
listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
} catch (Exception e) {
// TODO: handle exception
Log.e("listen Thread", "failed to get the temp thread");
}
serverSocket = temp;
}
@Override
public void run(){
setName("AcceptThread" + socketType);
BluetoothSocket socket = null;
//如果設備沒有連接
while(mState!=STATE_CONNECTED){
try {
socket = serverSocket.accept();
Log.e("success", "success");
} catch (IOException e) {
// TODO Auto-generated catch block
Log.e("socket", "couldn't accept the socket sending");
break;
}
//如果Socket.accpt()成功
if(socket!=null){
//異步線程加載
synchronized (BlueToothChatService.this) {
switch(mState){
case STATE_LISTEN:
case STATE_CONNECTING:
Log.e("connectting or Listen", "start");
connected(socket,socket.getRemoteDevice(),socketType);
Log.e("connectting or Listen", "success");
break;
//None或者連接上的時候關閉socket
case STATE_NONE:
case STATE_CONNECTED:
try {
socket.close();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
break;
}
}
}
}
}
public void cancel(){
try {
serverSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//連接線程connectThread,用於連接
private class ConnectThread extends Thread{
private BluetoothSocket mmSocket;
private BluetoothDevice mmDevice;
private String mSocketTye;
public ConnectThread(BluetoothDevice device){
mmDevice = device;
BluetoothSocket temp = null;
try {
temp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
mmSocket = temp;
}
@Override
public void run(){
setName("ConnectThread"+mSocketTye);
//關閉掃描發現,否則會影響socket之間的連接
mAdapter.cancelDiscovery();
try {
Log.e("mmSocket", "mmSocket.connect()");
mmSocket.connect();
Log.e("mmSocket", "mmSocket.connect success");
} catch (Exception e) {
// TODO: handle exception
try {
mmSocket.close();
} catch (Exception e2) {
// TODO: handle exception
}
//連接失敗
connectionFailed();
return;
}
//重置
synchronized (BlueToothChatService.this) {
mmConnectThread = null;
}
//已連接的線程Start the ConnectedThread to begin managing a Bluetooth connection
connected(mmSocket,mmDevice,mSocketTye);
}
public void cancel(){
try {
mmSocket.close();
} catch (Exception e) {
// TODO: handle exception
}
}
}
//這是一個在兩個設備之間進行數據交換的線程,前提是已經連接上
private class ConnectedThread extends Thread{
private BluetoothSocket mmSocket ;
private InputStream mmInput ;
private OutputStream mmOuput ;
public ConnectedThread(BluetoothSocket socket,String socketType){
mmSocket = socket;
InputStream tmpInput = null;
OutputStream tmpOutput = null;
try {
tmpInput = socket.getInputStream();
tmpOutput = socket.getOutputStream();
} catch (Exception e) {
// TODO: handle exception
}
mmInput = tmpInput;
mmOuput = tmpOutput;
}
@Override
public void run(){
byte[] buff = new byte[1024];
int bytes;
while (true) {
try {
//讀取
bytes = mmInput.read(buff);
//發送到UI線程處理
mHandler.obtainMessage(BlueToothChat.MESSAGE_READ,
bytes, -1, buff).sendToTarget();
} catch (IOException e) {
// TODO Auto-generated catch block
connectionLost();
BlueToothChatService.this.start();
break;
}
}
}
//寫入
public void write(byte[] buff){
try {
mmOuput.write(buff);
mHandler.obtainMessage(BlueToothChat.MESSAGE_WRITE,
-1, -1, buff).sendToTarget();
} catch (Exception e) {
// TODO: handle exception
}
}
public void cancel(){
try {
mmSocket.close();
} catch (Exception e) {
// TODO: handle exception
}
}
}
}
/*關於客戶端和服務端:
首先每一部設備都有服務線程和連接線程,當一部設備(A)對另一部設備(B)發起連接的時候,那麼A將作為客戶端,而B作為服務端,B的服務端線程的serverSocket將不斷監聽來自客戶端的socket
即就是:socket = serverSocket.accept();
這個實現之後,才能實現彼此的聊天數據線程的連接和交換*/
接下來實現的是:
已配對列表和發現新設備的列表和開啟300s藍牙可被發現的功能
/**
* This Activity appears as a dialog. It lists any paired devices and devices
* detected in the area after discovery. When a device is chosen by the user,
* the MAC address of the device is sent back to the parent Activity in the
* result Intent.
* 這個活動將以dialog的形式顯示,主要通過主題設置實現
* 包含的功能有:已配對的設備列表和發現的設備列表
* 當選擇任何一個選項的時候,將返回一些信息給BlueToothChat
*
*/
public class DeviceListActivity extends Activity {
public static final String EXTRA_DEVICE_ADDRESS = "device_name";
//藍牙必備
private BluetoothAdapter mBtAdapter;
private ArrayAdapter mPairDeviceArrayAdapter; //已配對的適配器
private ArrayAdapter mNewDevicesArrayAdapter; //發現的新設備適配器
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.device_list);
Button scanBtn = (Button)findViewById(R.id.button_scan);
scanBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//點擊之後開始掃描發現,按鈕隱藏不可見
// TODO Auto-generated method stub
doDiscovery();
v.setVisibility(View.GONE);
}
});
//初始化適配器
mPairDeviceArrayAdapter = new ArrayAdapter(this, R.layout.device_name);
mNewDevicesArrayAdapter = new ArrayAdapter(this, R.layout.device_name);
ListView pairedList = (ListView)findViewById(R.id.paired_devices);
ListView newList = (ListView)findViewById(R.id.new_devices);
pairedList.setAdapter(mPairDeviceArrayAdapter);
newList.setAdapter(mNewDevicesArrayAdapter);
pairedList.setOnItemClickListener(mDeviceClickListener);
newList.setOnItemClickListener(mDeviceClickListener);
//注冊廣播及狀態
IntentFilter intentFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
this.registerReceiver(reciver, intentFilter);
intentFilter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
this.registerReceiver(reciver, intentFilter);
//
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
Set pairedDevices = mBtAdapter.getBondedDevices();
if(pairedDevices.size()>0){
findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
for(BluetoothDevice device:pairedDevices){
mPairDeviceArrayAdapter.add(device.getName()+"\n"+device.getAddress());
}
}else{
String noDevice = getResources().getText(R.string.none_paired).toString();
mPairDeviceArrayAdapter.add(noDevice);
}
}
@Override
public void onDestroy(){
super.onDestroy();
if(mBtAdapter!=null){
mBtAdapter.cancelDiscovery();
}
this.unregisterReceiver(reciver);
}
private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
@Override
public void onItemClick(AdapterView av, View v, int arg2,
long arg3) {
// TODO Auto-generated method stub
//停止掃描發現
mBtAdapter.cancelDiscovery();
//截取後面17位字符也就是Mac地址
String info = ((TextView)v).getText().toString();
String address = info.substring(info.length()-17);
Intent intent = new Intent();
intent.putExtra(EXTRA_DEVICE_ADDRESS, address);//返回了一個地址
setResult(RESULT_OK, intent);
finish();//關閉界面
}
};
public void doDiscovery() {
// TODO Auto-generated method stub
setProgressBarVisibility(true);
setTitle(R.string.scanning);
findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);
if(mBtAdapter.isDiscovering()){
mBtAdapter.cancelDiscovery();
}
mBtAdapter.startDiscovery(); //開始掃描發現
}
// The BroadcastReceiver that listens for discovered devices and
// changes the title when discovery is finished
//通過廣播對掃描的設備做處理
private final BroadcastReceiver reciver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
//獲取當前的Action即就是其狀態
String action = intent.getAction();
//same and then
if(BluetoothDevice.ACTION_FOUND.equals(action)){
//獲取設備對象
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//判斷該設備是否已配對
if(device.getBondState()!=BluetoothDevice.BOND_BONDED){
mNewDevicesArrayAdapter.add(device.getName()+"\n"+device.getAddress());
}
}else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
//掃描結束
setProgressBarVisibility(false);
setTitle(R.string.select_device);
if(mNewDevicesArrayAdapter.getCount()==0){
String noDevice = getResources().getText(R.string.none_found).toString();
mNewDevicesArrayAdapter.add(noDevice);
}
}
}
};
}
權限:
以下是一些布局文件
device_list.xml:
實現一個如下圖所示的折線圖首先是控件繪圖區域的劃分,控件左邊取一小部分(控件總寬度的八分之一)繪制表頭,右邊剩余的部分繪制表格確定表格的行列數,首先繪制一個三行八列的網格
據說Android最推薦的是在ViewPager中使用FragMent,即ViewPager中的頁面不像前面那樣用LayoutInflater直接從布局文件加載,而是一個
這個月接到一個項目,要寫一個像360助手一樣的對於軟件管理的APP;其中,遇到了一個問題:多線程斷點下載 這個 ,由於之前沒有寫過這方面的應用功能。所以,不免要自學了。然
Android項目中自定義頂部標題欄下面給大家詳細介紹android中自定義頂部標題欄的思路及實現方式 先來圖: 思路及實現步驟 1.定義