透過引用 Unity Mono classes.jar 繼承 UnityPlayerActivity 的 Activity 中加入一個 callback function interface,以便 BroadcastReceiver 收到藍芽斷線、連線、服務、資料等訊息時,主動回傳到 Unity 中處理。
關於 Android Library 專案的 classes.jar 檔取得方式參考另一篇:
Android Studio 建立與使用 AAR Library
Unity 5.x Personal 之 Mono 的 Classes.jar 檔案位於(依 Unity 版本不同,檔案的路徑會有所不同):
C:\Program Files\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes\classes.jar
__________________________________________________
檔案:BleServiceOnReceiveCallback.java
package com.company.unityandroidbleplugin;
public interface BleServiceOnReceiveCallback {
void BleServiceOnConnect(String action);
void BleServiceOnDisconnect(String action);
void BleServiceOnDiscovered(String action);
void BleServiceOnDataAvailable(String WriteData, String ReadData);
}
__________________________________________________
略,參考 Google android-BluetoothLeGatt-master 範例。
__________________________________________________
檔案:SampleGattAttributes.java
略,參考 Google android-BluetoothLeGatt-master 範例。
__________________________________________________
檔案:MainActivity.java
package com.company.unityandroidbleplugin;
import android.app.Activity;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Toast;
import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;
import java.util.ArrayList;
public class MainActivity extends UnityPlayerActivity {
private final static String TAG = MainActivity.class.getSimpleName();
public static String UnityObjectName = "Main Camera";
private static final int REQUEST_ENABLE_BT = 1;
private Activity _activity;
public boolean mScanning;
private boolean mConnected = false;
private String mDeviceAddress;
private BluetoothLeService mBluetoothLeService;
private LeDeviceListAdapter mLeDeviceListAdapter;
private BluetoothAdapter mBluetoothAdapter;
private BleServiceOnReceiveCallback BleServiceReceiveCB = null;
// 搜尋藍芽的 Callback function。
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
// 這目前還是引用內定的 BaseAdapter 存放裝置清單,也可捨去 BaseAdapter 然後自行使用一個 ArrayList 去存取。
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
};
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
if (!mBluetoothLeService.initialize()) {
UnityLog(TAG, "Unable to initialize Bluetooth");
finish();
}
// Automatically connects to the device upon successful start-up initialization.
mBluetoothLeService.connect(mDeviceAddress);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mBluetoothLeService = null;
}
};
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
mConnected = true; //connected
BleServiceReceiveCB.BleServiceOnConnect(action);
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
mConnected = false; //disconnected
BleServiceReceiveCB.BleServiceOnDisconnect(action);
} else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
BleServiceReceiveCB.BleServiceOnDiscovered(action);
// 顯示 GATT 詳細 Service 和特徵值請參考 Google 的範例程式。
// Show all the supported services and characteristics on the user interface.
//displayGattServices(mBluetoothLeService.getSupportedGattServices());
} else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
// 這幾個資料依照自己 BluetoothLeService 提供的資料做取捨。
String r = intent.getStringExtra(BluetoothLeService.READ_DATA);
String w = intent.getStringExtra(BluetoothLeService.WRITE_DATA);
BleServiceReceiveCB.BleServiceOnDataAvailable(r, w);
}
}
};
public void SetBleServiceOnReceiveCallback(BleServiceOnReceiveCallback cb) {
BleServiceReceiveCB = cb;
}
public void RemoveBleServiceOnReceiveCallback() {
BleServiceReceiveCB = null;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_activity = UnityPlayer.currentActivity;
}
private static IntentFilter makeGattUpdateIntentFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
return intentFilter;
}
// 供除錯用,可省略。
public static void UnityLog(String tag, String message){
UnityPlayer.UnitySendMessage(UnityObjectName, "AndroidPluginMessage", tag + " : " + message);
}
// 顯示 Toast。
public void ShowToast(final String message, final int length){
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(_activity, message, length).show();
}
});
}
// 顯示 Alert。
public void ShowAlert(final String title, final String message){
runOnUiThread(new Runnable() {
@Override
public void run() {
AlertDialog.Builder bld = new AlertDialog.Builder(_activity);
bld.setTitle(title).setMessage(message).setNeutralButton("OK", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 加入按下 OK 後想做的事。
}
});
bld.create().show();
}
});
}
// 藍芽功能檢查、初始化、提示開啟藍芽。
public boolean InitializeBluetooth() {
// Check API Level. For API level 18 and above.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
UnityLog(TAG, "Only support Android version is 4.3 or above.");
return false;
}
// Use this check to determine whether BLE is supported on the device. Then you can
// selectively disable BLE-related features.
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
UnityLog(TAG, "BLE not supported");
return false;
}
// Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
// BluetoothAdapter through BluetoothManager.
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
// Checks if Bluetooth is supported on the device.
if (mBluetoothAdapter == null) {
UnityLog(TAG, "error bluetooth not supported");
return false;
}
// Ensures Bluetooth is enabled on the device. If Bluetooth is not currently enabled,
// fire an intent to display a dialog asking the user to grant permission to enable it.
if (!mBluetoothAdapter.isEnabled()) {
if (!mBluetoothAdapter.isEnabled()) { //check twice
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
// Initializes list view adapter.
mLeDeviceListAdapter = new LeDeviceListAdapter();
registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
this.getApplicationContext().bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
return true;
}
// 連接 BLE 裝置。
public boolean ConnectBleDevice(String mDeviceAddress) {
this.mDeviceAddress = mDeviceAddress;
return mBluetoothLeService.connect(mDeviceAddress);
}
// 斷開與 BLE 裝置的連線。
public void DisconnectBleDevice(){
mBluetoothLeService.disconnect();
}
// 掃描或停止掃描附近的 BLE 裝置。
public void scanLeDevice(final boolean enable) {
if (enable) {
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
// 取得已被發現的 BLE 裝置總數。
public int GetBleDeviceCount() {
return mLeDeviceListAdapter.getCount();
}
// 取得 BLE 裝置。
public BluetoothDevice GetBleDevice(int position) {
return mLeDeviceListAdapter.getDevice(position);
}
// 取得 BLE 裝置名稱。
public String GetBleDeviceName(int position) {
return mLeDeviceListAdapter.getDevice(position).getName();
}
// 取得 BLE 裝置的 IP 位址。
public String GetBleDeviceAddress(int position) {
return mLeDeviceListAdapter.getDevice(position).getAddress();
}
// Adapter for holding devices found through scanning.
private class LeDeviceListAdapter extends BaseAdapter {
private ArrayList
public LeDeviceListAdapter() {
super();
mLeDevices = new ArrayList
}
public void addDevice(BluetoothDevice device) {
if (device.getName() != null) {
if (!mLeDevices.contains(device)) {
mLeDevices.add(device); //如果 BLE 裝置未在清單內就加入清單。
// 這邊也可以增加一個 callback function,將新增的 device name 和 address 傳到 Unity 使用。
}
}
}
public BluetoothDevice getDevice(int position) {
return mLeDevices.get(position);
}
public void clear() {
mLeDevices.clear();
}
@Override
public int getCount() {
return mLeDevices.size();
}
@Override
public Object getItem(int i) {
return mLeDevices.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
return view;
}
}
}
__________________________________________________
檔案:AndroidPlugin.cs
using UnityEngine;
using System.Collections;
public class AndroidPlugin : MonoBehaviour
{
private static AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
private static AndroidJavaObject jo = jc.GetStatic
public static void ShowToast(string message, int length)
{
jo.Call("ShowToast", message, length);
}
public static void ShowAlert(string title, string message)
{
jo.Call("ShowAlert", title, message);
}
public static bool InitializeBluetooth()
{
return jo.Call<bool>
}
public static void scanLeDevice(bool enable)
{
jo.Call("scanLeDevice", enable);
}
public static bool ConnectBleDevice(string mDeviceAddress)
{
return jo.Call<bool>("ConnectBleDevice", mDeviceAddress);
}
public static void DisconnectBleDevice()
{
jo.Call("DisconnectBleDevice");
}
public static void SetBleServiceOnReceiveCallback(AndroidJavaProxy bleServiceOnReceiveCallback)
{
jo.Call("SetBleServiceOnReceiveCallback", bleServiceOnReceiveCallback);
}
public static void RemoveBleServiceOnReceiveCallback()
{
jo.Call("RemoveBleServiceOnReceiveCallback");
}
public static int GetBleDeviceCount()
{
return jo.Call<int>("GetBleDeviceCount");
}
public static int GetBleDeviceCount()
{
return jo.Call<int>("GetBleDeviceCount");
}
public static string GetBleDeviceName(int position) {
return jo.Call<string>("GetBleDeviceName", position);
}
public static string GetBleDeviceAddress(int position) {
return jo.Call<string>("GetBleDeviceAddress", position);
}
}
__________________________________________________
檔案:TestScript.cs
public class TestScript : MonoBehaviour{
......
void Start()
{
......
// 藍芽初始化。
AndroidPlugin.InitializeBluetooth();
// 掃描 BLE 裝置。
AndroidPlugin.scanLeDevice(true);
// 停止掃描 BLE 裝置。
AndroidPlugin.scanLeDevice(false);
// 設定 BleService 的 Callback function。
BleServiceOnReceiveCallback bleServiceOnReceiveCallback = new bleServiceOnReceiveCallback();
AndroidPlugin.SetBleServiceOnReceiveCallback(bleServiceOnReceiveCallback);
......
}
......
public class BleServiceOnReceiveCallback : AndroidJavaProxy
{
public BleServiceOnReceiveCallback():base("com.company.unityandroidbleplugin.BleServiceOnReceiveCallback")
{
}
public void BleServiceOnConnect(string action)
{
//當 BLE 連上時想做的事。
}
public void BleServiceOnDisconnect(string action)
{
//當 BLE 斷線時想做的事。
}
public void BleServiceOnDiscovered(string action)
{
//當掃描完 BLE 時想做的事。
}
public void BleServiceOnDataAvailable(string WriteData, string ReadData)
{
//當 BLE 有資料傳入或送出時想做的事。
}
}
}
__________________________________________________
檔案:AndroidManifest.xml
第一次編譯 Unity Android 平台時,Unity 專案目錄內會產生 Temp 資料夾。
從 \Unity 專案名稱\Temp\StagingArea\ 複製 AndroidManifest.xml 檔案。
另存到
\Unity 專案名稱\Assets\Plugins\Android\
,如果沒有 Plugins\Android 路徑資料夾則自行建立。
修改 package 名稱為自訂的 Plugin 名稱即可。
8 則留言:
感謝大大的教學 這個真的太有用了!!
我最近都在搞Ble, 把mpu6050連arduino再加Ble,用Unity做即時3d互動。不過有點阻滯,想問下你可以sd你的unity project 給我作參考嗎?
My email:lamyinfung3332@gmail.com
您好,最近在學習Unity與藍芽至手機,
有依照上面指示去做練習,但是將Unity Run進手機,會直接閃退結束應用程式,
有發現好像是JavaClass宣告後就會出現這樣的問題,另外將用一個public class 將JavaClass寫成全域變數,
執行後,雖然不會閃退,但是會完全沒有反應,不知道 怎麼改善這情況,
另外還想請教,有特定的UUID,要如何Set呢?
我的 Email:qsqswdwd33@gmail.com
謝謝
您好,我是一名大三的學生
最近在利用Arduino101做一個互動式的遊戲
在藍芽這邊卡關很久
想請問您能否提供您的專案借我們參考學習
Email:p513817@gmail.com
哈囉你好~
想請問一下藍牙部分
找不到
檔案:BleServiceOnReceiveCallback.java
這個腳本,是不是要自己新增?
還有那個MainActivity.java也是嗎
或者有 專案檔可以提供參考 謝謝你
c0975795138@gmail.com
您好,我是一名大三的學生
最近專題在利用ARM lpc1114做一個互動式的遊戲
在藍芽部分困惑很久
請問您是否提供您的專案借我們參考學習
email:jimmy66617@gmail.com
您好,我也是大三在忙研究的學生
我是最近在使用android和arduino感測器
想利用藍牙讓他們溝通
是否能提供您的專案借我們參考學習
不好意思謝謝您
我的email:a0932954941@gmail.com
您好,目前參照您的文章進行實作,但並不順利
請問能否提供專案讓我研究
我的email:rockisshit@gmail.com
謝謝您
張貼留言