ساخت یک بلوتوث اسکنر با کمک Bluetooth API اندروید

ساخت یک بلوتوث اسکنر با کمک Bluetooth API اندروید


بلوتوث یکی از تکنولوژی های محبوب در گوشی های موبایل است و امکان انتقال اطلاعات بین گوشی های نزدیک به هم را میسر می سازد. هر گوشی مدرن دارای قابلیت بلوتوث است و چنانچه تمایل به ساخت یک رابط اپلیکیشن با کمک یک بلوتوث فعال داشته باشید لازم است از Bluetooth API اندروید استفاده کنید. در این مطلب آموزشی ما یک اپلیکیشن مشابه اپلیکیشن بلوتوث در تنظیمات اندروید می سازیم که دارای امکاناتی به شرح زیر می باشد.

- فعال سازی بلوتوث در یک گوشی.

- نمایش یک فهرست از گوشی های جفت شده.

- جستجو و فهرست کردن گوشی های بلوتوثی که در آن نزدیکی قرار دارند.

در این مطلب مقدماتی از چگونگی برقراری ارتباط و ارسال اطلاعات به گوشی بلوتوث دیگر شرح داده می شود. برای شروع کار باید یک پروژه بسازید، یک نمونه از آن را می توانید از GitHub دانلود کنید. اسکرین شات زیر یک نمای کلی از پروژه را نمایش می دهد. چنانچه با مشکلی روبه رو شدید می توانید به پروژه کامل شده در GitHub مراجعه کنید.

blog_16235_1

1. فعال سازی بلوتوث

پیش از فعال سازی بلوتوث در یک گوشی اندروید باید مجوزهای لازم برای انجام این کار را دریافت کنید، این کار در فایل منیفست اپلیکیشن صورت می پذیرد. مجوز BLUETOOTH به اپلیکیشن امکان برقراری اتصال، قطع آن و انتقال اطلاعات بین گوشی های دیگر را می دهد. مجوز BLUETOOTH_ADMIN اپلیکیشن را قادر به یافتن گوشی ها یا دستگاه های بلوتوث جدید می کند و تنظیمات بلوتوث گوشی را تغییر می دهد.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.tutsplus.matt.bluetoothscanner" >

<uses-permission android:name="android.permission.BLUETOOTH" />

<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

ما از آداپتور بلوتوث برای ارتباط با بلوتوث استفاده می کنیم. آداپتور در کلاس ListActivity معرفی شده است، چنانچه آداپتور null باشد به این معناست که گوشی از بلوتوث پشتیبانی نمی کند و اپلیکیشن در این گوشی قادر به کار نیست. این شرایط با نمایش یک پیغام به کاربر و خروج از اپلیکیشن منجر می شود.

@Override

protected void onCreate(Bundle savedInstanceState) {

...

BTAdapter = BluetoothAdapter.getDefaultAdapter();

// Phone does not support Bluetooth so let the user know and exit.

if (BTAdapter == null) {

new AlertDialog.Builder(this)

.setTitle("Not compatible")

.setMessage("Your phone does not support Bluetooth")

.setPositiveButton("Exit", new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int which) {

System.exit(0);

}

})

.setIcon(android.R.drawable.ic_dialog_alert)

.show();

}

}

چنانچه بلوتوث در گوشی در دسترس باشد باید آن را فعال سازی کنیم، برای این منظور از bluetoothAdabter.ACTION_REQUEST_ENABLE استفاده می کنیم. این کار یک دیالوگ را به کاربر نمایش می دهد و از او درخواست فعال سازی بلوتوث بر روی گوشی را می کند. REQUSET_BLUETOOTH یک مقدار صحیح ثابت است که برای شناسایی درخواست اکتیویتی نشانده می شود.

public class ListActivity extends ActionBarActivity implements DeviceListFragment.OnFragmentInteractionListener  {

public static int REQUEST_BLUETOOTH = 1;

...

protected void onCreate(Bundle savedInstanceState) {

...

if (!BTAdapter.isEnabled()) {

Intent enableBT = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);

startActivityForResult(enableBT, REQUEST_BLUETOOTH);

}

}

}

2. بدست آوردن یک فهرست از دستگاه های جفت شده

در این مرحله ما قادر به اسکن دستگاه های جفت شده و نمایش آنها در یک لیست می باشیم، وضعیت بلوتوث یک دستگاه به سه صورت زیر می باشد:

- ناشناس

- جفت شده

- متصل

درک تفاوت بین یک گوشی جفت شده و متصل اهمیت دارد، گوشی های جفت شده از حضور یکدیگر آگاهی دارند و یک link key را به اشتراک می گذارند که برای احراز هویت و برقراری اتصال استفاده می شود، در صورت برقراری یک اتصال رمزگذاری شده گوشی ها جفت می شوند.

گوشی های متصل دارای یک کانال RFCOMM هستند که آنها را قادر به ارسال و دریافت اطلاعات می کند. یک گوشی ممکن است دارای تعدادی گوشی جفت شده باشد اما در یک زمان خاص امکان اتصال به تنها یکی از آنها را دارد.

دستگاه های بلوتوث با آبجکت BluetoothDevice نمایش داده می شوند. یک فهرست از گوشی های جفت شده با متد ()getBondedDevices قابل دسترسی است که یک مجموعه از آبجکت های BluetoothDevice را برمی گرداند. فراخوانی متد getBondedDevices در متد ()onCreate متعلق به DeviceListFragment صورت می پذیرد.

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

Log.d("DEVICELIST", "Super called for DeviceListFragment onCreate\n");

deviceItemList = new ArrayList<DeviceItem>();

Set<BluetoothDevice> pairedDevices = bTAdapter.getBondedDevices();

}

ار متدهای ()getName و ()getAddress برای دریافت اطلاعات بیشتر درباره دستگاه بلوتوث استفاده می شود، متد ()getName یک شناسنده عمومی از گوشی را باز می گرداند، در حالی که مقدار بازگشتی متد ()getAddress مک آدرس گوشی است، یک شناسنده منحصر به فرد برای شناسایی گوشی.

حال که فهرستی از دستگاه های جفت شده در اختیار ما قرار گرفته است یک آبجکت DeviceItem برای هر آبجکت BluetoothDevice می سازیم و پس از آن هر آبجکت DeviceItem را به آرایه ای به نام deviceItemList می افزاییم. این آرایه به منظور نمایش یک فهرست از دستگاه های جفت شده در اپلیکیشن به کار می رود. کد مربوط به نمایش یک فهرست از آبجکت های DeviceItem در پروژه اولیه در دسترس می باشد.

if (pairedDevices.size() > 0) {

for (BluetoothDevice device : pairedDevices) {

DeviceItem newDevice= new DeviceItem(device.getName(),device.getAddress(),"false");

deviceItemList.add(newDevice);

}

}

3. کشف دستگاه های بلوتوثی که در اطراف قرار دارند

گام بعد کشف دستگاه هایی است که هنوز جفت نشده اند، یعنی همان دستگاه های ناشناس و سپس افزودن آنها به فهرست گوشی های جفت شده. این کار با لمس دکمه اسکن در صفحه صورت می پذیرد. کد موردنظر برای انجام این کار در DeviceListFreagment قرار گرفته است.

اول از همه لازم است یک BroadcastReceiver بسازیم و متد ()onRecieve آنرا اورراید نماییم. متد ()onRecieve همزمان با پیدا شدن یک دستگاه بلوتوث فراخوانی می شود.

متد ()onReceive یک اینتنت را به عنوان پارمتر دوم خود می گیرد. با فراخوانی ()getAction قادر به تشخیص اینتنی هستیم که در حال برادکستینگ است. چنانچه اکشن BluetoothDevice.ACTION_FOUND باشد نشان از این است که یک دستگاه بلوتوث را پیدا کرده ایم. پس از این کار با استفاده از نام دستگاه و آدرس مک آن یک آبجکت DeviceItem می سازیم و در نهایت آبجکت DeviceItem را به ArrayAdapter جهت نمایش در اپلیکیشن می افزاییم.

public class DeviceListFragment extends Fragment implements AbsListView.OnItemClickListener{

...

private final BroadcastReceiver bReciever = new BroadcastReceiver() {

public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

if (BluetoothDevice.ACTION_FOUND.equals(action)) {

BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

// Create a new device item

DeviceItem newDevice = new DeviceItem(device.getName(), device.getAddress(), "false");

// Add it to our adapter

mAdapter.add(newDevice);

}

}

};

}

زمانی که دکمه اسکن فعال می شود، لازم است گیرنده ای را که ساخته بودیم ثبت کنیم و متد ()startDiscovery را فراخوانی نماییم. چنانچه دکمه اسکن غیرفعال شود فرستنده نیز از حالت ثبت شده در می آید و متد ()cancelDiscovery فراخوانی می شود، توجه داشته باشید که این فرآیند منابع زیادی را مورد استفاده قرار می دهد. چنانچه اپلیکیشن شما به یک دستگاه بلوتوث دیگر متصل باشد باید پیش از اتصال این فرآیند جستجو را متوقف کنید.

علاوه بر این باید در زمان شروع فرآیند جستجو اقدام به پاک کردن آبجکت ArrayAdapter و mAdapter نیز نمایید.

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.fragment_deviceitem_list, container, false);

ToggleButton scan = (ToggleButton) view.findViewById(R.id.scan);

...

scan.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);

if (isChecked) {

mAdapter.clear();

getActivity().registerReceiver(bReciever, filter);

bTAdapter.startDiscovery();

} else {

getActivity().unregisterReceiver(bReciever);

bTAdapter.cancelDiscovery();

}

}

});

}

4. اتصال به یک دستگاه

اتصالات بلوتوث نیز مانند سایر انواع اتصالات می باشند و یک سرور و کلاینت وجود دارند که با سوکت های RFCOMM به برقراری ارتباط می پردازند. در اندروید سوکت های RFCOMM به عنوان یک آبجکت BluetoothSocket نمایش داده می شوند. بسیاری از کدهای فنی برای سرورها توسط اندروید SDK مدیریت می شوند و در API بلوتوث جای گرفته اند.

اتصال به عنوان کلاینت آسان است، اول از همه سوکت RFCOMM را از BluetoothDevice موردنظر با فراخوانی ()createRfcommSocketToServiceRecord می گیرید و یک UUID که مقداری 128 بیتی است را ارسال می کنید، UUID مشابه شماره پورت می باشد.

برای نمونه تصور کنید که در حال ساخت یک اپلیکیشن چت می باشید که از بلوتوث جهت چت با کاربران اطراف استفاده می کند. جهت یافتن سایر کاربران برای چت باید به دنبال گوشی های دیگری بگردید که اپلیکیشن چت را بر روی گوشی خود نصب کرده اند. استفاده از UUID برای گوش دادن و پذیرفتن ارتباطات بلوتوث به طور خودکار یک UUID را به لیست سرویس های گوشی یا پروتکل کشف سرویس می افزاید.

زمانی که BluetoothSocket ساخته شد شما قادر به فراخوانی connect() در BluetoothSocket هستید. این کار یک ارتباط با BluetoothDevice را با استفاده از سوکت RFCOMM ایجاد می کند. زمانی که گوشی شما متصل باشد می توانید از سوکت برای تبادل اطلاعات با گوشی یا دستگاه متصل شده استفاده کنید. این کار مشابه پیاده سازی استاندارد برای سرور است.

حفظ ارتباط بلوتوث هزینه بر است و باید هر زمان که کار تمام شد سوکت را ببندیم. برای بستن سوکت متد ()close از BluetoothDevice را صدا می زنیم.

این قطعه کد نحوه برقراری ارتباط با BluetoothDevice داده شده را به تصویر کشیده است:

public class ConnectThread extends Thread{

private BluetoothSocket bTSocket;

public boolean connect(BluetoothDevice bTDevice, UUID mUUID) {

BluetoothSocket temp = null;

try {

temp = bTDevice.createRfcommSocketToServiceRecord(mUUID);

} catch (IOException e) {

Log.d("CONNECTTHREAD","Could not create RFCOMM socket:" + e.toString());

return false;

}

try {

bTSocket.connect();

} catch(IOException e) {

Log.d("CONNECTTHREAD","Could not connect: " + e.toString());

try {

bTSocket.close();

} catch(IOException close) {

Log.d("CONNECTTHREAD", "Could not close connection:" + e.toString());

return false;

}

}

return true;

}

public boolean cancel() {

try {

bTSocket.close();

} catch(IOException e) {

Log.d("CONNECTTHREAD","Could not close connection:" + e.toString());

return false;

}

return true;

}

}

اتصال به عنوان سرور کمی پیچیده تر است، اول از همه باید از BluetoothAdapter خود یک BluetoothServerSocket دریافت کنید، این سوکت برای گوش دادن به یک اتصال مورد استفاده قرار می گیرد و برای گرفتن یک سوکت RFCOMM اشتراک گذاشته شده در اتصال استفاده می شود. زمانی که ارتباط برقرار شد سوکت سرور دیگر موردنیاز نخواهد بود و می توان با متد ()close آنرا بست.

ساخت سوکت سروربا فراخوانی (listUsingRfcommWithServiceRecord(String name, UUID mUUID صورت می پذیرد. این متد دو پارامتر می گیرد، یکی نام که از نوع رشته است و دیگری یک شناسنده منحصر به فرد از نوع UUID. پارامتر نام در حقیقت نامی است که هنگام افزوده شدن به SDP گوشی به سرویس می دهیم. شناسنده منحصر به فرد باید با UUID که کلاینت سعی در برقراری ارتباط با آن دارد مطابقت داشته باشد. پس از آن ()accept را در BluetoothServerSocket جدید صدا می زنیم و منتظر برقراری ارتباط می مانیم. زمانی که مقدار بازگشتی accept() چیزی به غیر از null باشد، آن را به BluetoothSocket اختصاص می دهیم که بعدا برای تبادل اطلاعات با گوشی متصل شده استفاده می شود.

کد زیر نحوه پذیرش یک ارتباط به عنوان سرور را نمایش می دهد:

public class ServerConnectThread extends Thread{

private BluetoothSocket bTSocket;

public ServerConnectThread() { }

public void acceptConnect(BluetoothAdapter bTAdapter, UUID mUUID) {

BluetoothServerSocket temp = null;

try {

temp = bTAdapter.listenUsingRfcommWithServiceRecord("Service_Name", mUUID);

} catch(IOException e) {

Log.d("SERVERCONNECT", "Could not get a BluetoothServerSocket:" + e.toString());

}

while(true) {

try {

bTSocket = temp.accept();

} catch (IOException e) {

Log.d("SERVERCONNECT", "Could not accept an incoming connection.");

break;

}

if (bTSocket != null) {

try {

temp.close();

} catch (IOException e) {

Log.d("SERVERCONNECT", "Could not close ServerSocket:" + e.toString());

}

break;

}

}

}

public void closeConnect() {

try {

bTSocket.close();

} catch(IOException e) {

Log.d("SERVERCONNECT", "Could not close connection:" + e.toString());

}

}

}

نوشتن و خواندن در ارتباط با استفاده از استریم ها انجام می شود یعنی InputStream و OutputStream. ما قادر به دریافت یک مرجع به این استریم ها با کمک فراخوانی ()getInputStream و getOutputStream در BluetoothSocket می باشیم. به منظور خواندن و نوشتن بر روی این استریم ها تابع ()read و ()write را مورد استفاده قرار می دهیم.

قطعه کد زیر نحوه انجام این کار برای یک عدد صحیح منفرد را نمایش داده است:

public class ManageConnectThread extends Thread {

public ManageConnectThread() { }

public void sendData(BluetoothSocket socket, int data) throws IOException{

ByteArrayOutputStream output = new ByteArrayOutputStream(4);

output.write(data);

OutputStream outputStream = socket.getOutputStream();

outputStream.write(output.toByteArray());

}

public int receiveData(BluetoothSocket socket) throws IOException{

byte[] buffer = new byte[4];

ByteArrayInputStream input = new ByteArrayInputStream(buffer);

InputStream inputStream = socket.getInputStream();

inputStream.read(buffer);

return input.read();

}

}

در finished project on GitHub قادر به پیدا کردن نمونه های تکمیل شده می باشید.

نتیجه گیری

ما با موفقیت قادر به ساخت بلوتوث اسکنر خود شدیم و موارد زیر را فرا گرفتیم:

- درخواست مجوز برای بلوتوث.

- فعال سازی بلوتوث بر روی گوشی.

- دریافت لیستی از گوشی های جفت شده.

- اسکن و نمایش یک لیست از گوشی های بلوتوث اطراف.

- برقراری یک ارتباط بلوتوث بین دو گوشی.

- ارسال و دریافت اطلاعات با کمک ارتباط بلوتوث.

 

http://code.tutsplus.com برگرفته از

اینها را هم بخوانید

نظرات کاربران

  1. avatar
    E
    BroadcastReceiver رو دقیقا" کجا تعریف میکنیم؟ توی MainActivity.java یه کلاس تعریف میکنیم که BroadcastReceiver رو تو اون کلاس تعریف کنیم؟

    به این نظر پاسخ دهید
    1. واننی منصوریان
      واننی منصوریان (ادمین)
      دوست عزیز BroadcastReceiver را میتونید هم در MainActivity تعریف کنید هم در یک کلاس جداگانه.

      به این نظر پاسخ دهید
  2. avatar
    E
    ممنون بابت پاسخ. یه سوال دیگه داشتم. برای کارهای زیر – فعال سازی بلوتوث در یک گوشی. – نمایش یک فهرست از گوشی های جفت شده. – جستجو و فهرست کردن گوشی های بلوتوثی که در آن نزدیکی قرار دارند. برای هر کدام یک کلاس جدا تعریف میکنیم؟؟؟؟ میشه در مورد کلییت کارهایی که اینجا انجام شده شرح بدین؟ مثلا چند تا کلاس داریم؟ و جایگاه هر یک از اینها. من همه این کار ها رو انجام دادم فقط قسمت scan کار نمیکنه! on-off-paired device اینا کار میکنه. فقط قسمت اسکن دستگاه مشکل دارم.

    به این نظر پاسخ دهید
    1. واننی منصوریان
      واننی منصوریان (ادمین)
      دوست عزیز تعریف چند کلاس یا یک کلاس بستگی به نحوه پیاده سازی ساختار اپلیکیشن شما دارد.

      به این نظر پاسخ دهید
  3. avatar
    E
    الان اینجا 4 تا اکتیویتی استفاده شده؟

    به این نظر پاسخ دهید
    1. واننی منصوریان
      واننی منصوریان (ادمین)
      دوست گرامی در اینجا از یک اکتیویتی استفاده شده و برای دریافت کدهای نهایی پروژه می توانید به لینک زیر مراجعه فرمایید: https://github.com/tutsplus/Android-BluetoothScannerFinishedProject موفق باشید

      به این نظر پاسخ دهید