GithubHelp home page GithubHelp logo

aicareles / android-ble Goto Github PK

View Code? Open in Web Editor NEW
2.6K 49.0 427.0 29.17 MB

Android-BLE蓝牙框架,提供了扫描、连接、使能/除能通知、发送/读取数据、接收数据,读取rssi,设置mtu等蓝牙相关的所有操作接口,内部优化了连接队列,以及快速写入队列, 并支持多服务通讯,可扩展配置蓝牙相关操作。

License: Apache License 2.0

Java 92.86% Kotlin 7.14%

android-ble's Introduction

个人项目或者定制化需求加v:liulei633001

QQ技术交流群:一群:494309361(已满) 二群:419784672

Android-BLE

License

Android-BLE蓝牙框架,提供了扫描、连接、使能/除能通知、发送/读取数据、接收数据,读取rssi,设置mtu等蓝牙相关的所有操作接口,内部优化了连接队列,以及快速写入队列, 并支持多服务通讯,可扩展配置蓝牙相关操作。

Android-BLE API

  • Ble - 最重要的类,对外提供所有的蓝牙操作接口.
  • BleDevice - 封装了蓝牙对象类,包含蓝牙连接状态以及基本蓝牙信息.
  • BleLog - 内部日志类,开发环境下打开可查看蓝牙相关操作信息.
  • BleStates - 蓝牙操作异常状态码信息类.(扫描、连接、读写等异常状态码).
  • ByteUtils - 各种字节数据转换的工具类.
  • CrcUtils - 字节校验的crc各种算法的工具类.
  • UuidUtils - 蓝牙服务/特征uuid转换工具类.

异常状态码

BleStates

接入前提示

1. 如果项目中的设备是统一类型(服务,特征uuid相同),则推荐在初始化时把服务,特征的uuid配置完整。
2. 如果项目中需要兼容多种设备类型(服务,特征uuid不相同),则在通信时需要使用byUuid的方式进行。

接入文档

1. 在 build.gradle 中添加下面依赖.

implementation 'com.github.aicareles:Android-BLE:3.3.1'

注意: 项目根build.gradle记得添加:
maven { url 'https://www.jitpack.io' }

android12 权限适配

List<String> permissions = new ArrayList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    permissions.add(Manifest.permission.BLUETOOTH_SCAN);
    permissions.add(Manifest.permission.BLUETOOTH_ADVERTISE);
    permissions.add(Manifest.permission.BLUETOOTH_CONNECT);
} else {
    permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
    permissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
requestPermission(permissions);

2. 在Application中初始化.

private void initBle() {
    Ble.options()//开启配置
        .setLogBleEnable(true)//设置是否输出打印蓝牙日志(非正式打包请设置为true,以便于调试)
        .setThrowBleException(true)//设置是否抛出蓝牙异常 (默认true)
        .setAutoConnect(false)//设置是否自动连接 (默认false)
        .setIgnoreRepeat(false)//设置是否过滤扫描到的设备(已扫描到的不会再次扫描)
        .setConnectTimeout(10 * 1000)//设置连接超时时长(默认10*1000 ms)
        .setMaxConnectNum(7)//最大连接数量
        .setScanPeriod(12 * 1000)//设置扫描时长(默认10*1000 ms)
        .setScanFilter(scanFilter)//设置扫描过滤
        .setUuidService(UUID.fromString(UuidUtils.uuid16To128("fd00")))//设置主服务的uuid(必填)
        .setUuidWriteCha(UUID.fromString(UuidUtils.uuid16To128("fd01")))//设置可写特征的uuid (必填,否则写入失败)
        .setUuidReadCha(UUID.fromString(UuidUtils.uuid16To128("fd02")))//设置可读特征的uuid (选填)
        .setUuidNotifyCha(UUID.fromString(UuidUtils.uuid16To128("fd03")))//设置可通知特征的uuid (选填,库中默认已匹配可通知特征的uuid)
        .setUuidServicesExtra(new UUID[]{BATTERY_SERVICE_UUID})//设置额外的其他服务组,如电量服务等
        .setFactory(new BleFactory() {//实现自定义BleDevice时必须设置
            @Override
            public MyDevice create(String address, String name) {
                return new MyDevice(address, name);//自定义BleDevice的子类
            }
        })
        .setBleWrapperCallback(new MyBleWrapperCallback())//设置全部蓝牙相关操作回调(例: OTA升级可以再这里实现,与项目其他功能逻辑完全解耦)
        .create(mApplication, new Ble.InitCallback() {
            @Override
            public void success() {
                BleLog.e("MainApplication", "初始化成功");
            }

            @Override
            public void failed(int failedCode) {
                BleLog.e("MainApplication", "初始化失败:" + failedCode);
            }
        });
     }

3. 开始使用.

1.扫描

ble.startScan(scanCallback);

扫描回调 (注: 记得打开蓝牙并检查是否授予蓝牙权限)

BleScanCallback<BleDevice> scanCallback = new BleScanCallback<BleDevice>() {
    @Override
    public void onLeScan(final BleDevice device, int rssi, byte[] scanRecord) {
        //Scanned devices
    }

   @Override
    public void onStart() {
        super.onStart();
    }

    @Override
    public void onStop() {
        super.onStop();
    }

    @Override
    public void onScanFailed(int errorCode) {
        super.onScanFailed(errorCode);
        Log.e(TAG, "onScanFailed: "+errorCode);
    }
};

2.连接/断开

//连接设备
ble.connect(device, connectCallback);

//连接多个设备
ble.connects(devices, connectCallback);

//取消正在连接的设备
ble.cancelConnecting(device);

//取消正在连接的多个设备
ble.cancelConnectings(devices);

//断开设备
ble.disconnect(device);

//断开所有设备
ble.disconnectAll();

连接/断开回调

private BleConnCallback<BleDevice> connectCallback = new BleConnCallback<BleDevice>() {
    @Override
    public void onConnectionChanged(BleDevice device) {

    }

    @Override
    public void onConnectTimeOut(BleDevice device) {
        super.onConnectTimeOut(device);
        Log.e(TAG, "onConnectTimeOut: " + device.getBleAddress());
    }

    @Override
    public void onConnectCancel(BleDevice device) {
        super.onConnectCancel(device);
        Log.e(TAG, "onConnectCancel: " + device.getBleName());
    }

    @Override
    public void onServicesDiscovered(BleDevice device, BluetoothGatt gatt) {
        super.onServicesDiscovered(device, gatt);
    }

    @Override
    public void onReady(BleDevice device) {
        super.onReady(device);
        //connect successful to enable notification
        ble.enableNotify(...);
    }

    @Override
    public void onConnectException(BleDevice device, int errorCode) {
        super.onConnectException(device, errorCode);

    }
};

3.使能/除能通知

ble.enableNotify(device, true, new BleNotifyCallback<BleDevice>() {
    @Override
    public void onChanged(BleDevice device, BluetoothGattCharacteristic characteristic) {
        UUID uuid = characteristic.getUuid();
        BleLog.e(TAG, "onChanged==uuid:" + uuid.toString());
        BleLog.e(TAG, "onChanged==data:" + ByteUtils.toHexString(characteristic.getValue()));
    }

    @Override
    public void onNotifySuccess(BleDevice device) {
        super.onNotifySuccess(device);
        BleLog.e(TAG, "onNotifySuccess: "+device.getBleName());
    }
});

4.读取数据

ble.read(device, new BleReadCallback<BleRssiDevice>() {
    @Override
    public void onReadSuccess(BleRssiDevice dedvice, BluetoothGattCharacteristic characteristic) {
        super.onReadSuccess(dedvice, characteristic);
    }

    @Override
    public void onReadFailed(BleRssiDevice device, int failedCode) {
        super.onReadFailed(device, failedCode);
    }
})

5.写入数据

//写入一包数据
ble.write(device, data, new BleWriteCallback<BleRssiDevice>() {
    @Override
    public void onWriteSuccess(BleRssiDevice device, BluetoothGattCharacteristic characteristic) {

    }

    @Override
    public void onWriteFailed(BleRssiDevice device, int failedCode) {
        super.onWriteFailed(device, failedCode);
    }
});

//写入大数据(文件、图片等)
byte[]data = toByteArray(getAssets().open("WhiteChristmas.bin"));
ble.writeEntity(mBle.getConnectedDevices().get(0), data, 20, 50, new BleWriteEntityCallback<BleDevice>() {
    @Override
    public void onWriteSuccess() {

    }

    @Override
    public void onWriteFailed() {

    }

    override void onWriteProgress(double progress) {

    }

    override void onWriteCancel() {

    }
});

//写入数据到队列中 (默认发送间隔50ms)
ble.writeQueue(RequestTask.newWriteTask(address, data));
//写入数据到队列中 (自定义间隔时间)
ble.writeQueueDelay(delay, RequestTask.newWriteTask(address, data));

//通过特定服务和特征值uuid写入数据
ble.writeByUuid(device, data, serviceUuid, charUuid, new BleWriteCallback<BleRssiDevice>() {
    @Override
    public void onWriteSuccess(BleRssiDevice device, BluetoothGattCharacteristic characteristic) {

    }

    @Override
    public void onWiteFailed(BleRssiDevice device, int failedCode) {
        super.onWiteFailed(device, failedCode);
    }
});

6. 移除监听(scan、connect)

ble.cancelCallback(connectCallback);
或
ble.cancelCallback(scanCallback);

8. 释放资源

 ble.released();

历史版本介绍:

历史版本

BLE蓝牙常见问题及解决方案

请通过该 Wiki BLE Page 了解更多信息.

Java-Sample Screenshot:

2 3 4

License

Copyright 2016 jerry

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Contribute:

android-ble's People

Contributors

aicareles avatar ficat avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

android-ble's Issues

NullPointerException

在BluetoothLeService类中的disconnect()方法
public void disconnect(final String address) {
if (mBluetoothAdapter == null || mBluetoothGattMap.get(address) == null) {//这里的条件为null
Log.e(TAG, mBluetoothGattMap.get(address).getDevice().getAddress());//这里用null掉方法了
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}

ble 扩展定制问题

BleRequestImpl 这个可以自定义他不香吗?现在又很多高版本的api都不支持!!!!

华为手机10的系统扫描问题

华为mate10,emui10,启动扫描的时候,callback的onLeScan一次回调都没有,时间到了会回调onStop,而直接进入系统的蓝牙界面是可以扫描到设备的

关于stopScan的问题

1、BluetoothScannerImplLollipop 中stopScan 会优先回调(super.stopScan())给调用方,是否应该放到scanner.stopScan(scannerCallback);后面
有一个场景是这样的,搜索指定设备,没有搜索到(搜索超时)时,希望继续搜索,所以我在onStop的回调中执行了startScan进行重新搜索,但是此时scanner.stopScan(scannerCallback)还未执行到,又开始了下次搜索,会报错SCAN_FAILED_ALREADY_STARTED

2、在超时扫描onStop前关闭蓝牙,然后开启蓝牙不会继续扫描设备了 isScaning一直是true.
ScanRequest 中stopScan isEnableInternal 当蓝牙关闭时判断为false ,导致没有走后面的停止流程
此问题demo可复现

蓝牙获取已连接设备异常

现在依赖的是2.6.1版本,调用已连接设备列表返回的为空集合,但是设置中蓝牙列表是有两条连接过的设备,下载别人的App也能获取到的,所以不知道为什么没有返回,请告知

连接问题

1.有时候连接不上设备,必须重启蓝牙才可以连接,这是为什么呢
2.第一次连接成功后,notify没有数据,必须断掉,第二次连接就有了

文档

文档写的真是乱,能不能好好整理一下。。

扫描时机

扫描连接在Ble: Service connection successful 执行之前会导致连接不成功
能否有一个Ble: Service connection successful的回调

丢包

最近在做蓝牙相关的,无论之前使用插件还是自己手写还是最终发现您的工程,无一例外的在安卓上都有丢包的情况,看见您最近解决的问题是丢包问题,所以还是想跟您说一下,有可能的话可以共同探讨下,解决这个问题

java.lang.NoClassDefFoundError: cn.com.heaton.blelibrary.ble.request.AdvertiserRequest$4

java.lang.NoClassDefFoundError: cn.com.heaton.blelibrary.ble.request.AdvertiserRequest$4
at cn.com.heaton.blelibrary.ble.request.AdvertiserRequest.(AdvertiserRequest.java:95)
at java.lang.Class.newInstanceImpl(Native Method)
at java.lang.Class.newInstance(Class.java:1208)
at cn.com.heaton.blelibrary.ble.request.Rproxy.init(Rproxy.java:44)
at cn.com.heaton.blelibrary.ble.proxy.RequestProxy.bindProxy(RequestProxy.java:42)
at cn.com.heaton.blelibrary.ble.Ble.init(Ble.java:96)
at cn.com.heaton.blelibrary.ble.Ble.create(Ble.java:108)
at cn.com.heaton.blelibrary.ble.Ble.create(Ble.java:103)
at cn.com.heaton.blelibrary.ble.Ble$Options.create(Ble.java:806)
at com.uhf.demo.bluetooth.BleManager.(BleManager.java:78)
at com.uhf.demo.bluetooth.BleManager.from(BleManager.java:56)
at com.uhf.demo.connect.ConnectActivity.initData(ConnectActivity.java:124)
at com.uhf.demo.base.BaseActivity.onCreate(BaseActivity.java:89)
at android.app.Activity.performCreate(Activity.java:5274)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2151)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2236)
at android.app.ActivityThread.access$800(ActivityThread.java:138)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1199)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5095)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
at dalvik.system.NativeStart.main(Native Method)

notify

Notify:使用最新的AndroidBLE v3.0.0版本,在6.0系统上 蓝牙连接成功后,在onReady回调中设置通知,设备立即返回的通知可以接收,但是在8.0及以上系统无效,没办法立即接收到设备发送的通知,有遇到过的嘛?

Android 10的连接问题

我的app里,业务是这样的,app启动后,会启动一个定时器,这个定时器会不断的去判断保存在本地的蓝牙设备是否连接上,如果没有连接上,就直接根据保存在本地的mac地址连接(不经过扫描过程),Android 10之前的版本一直都比较顺利,从Android 10出来后,很多用户反馈蓝牙连接不上设备,我这边自己测试Android 10,不断循环一下操作:
连接上了设备之后,重启设备。
多连接十几次后,就连接不上了,然后手机的蓝牙开关可以关闭,但是打不开了,只能重启手机,大神知道这个是什么原因吗?

关于提高Android ble 传输速率反馈,

很感谢您写的这个demo,对我工作中使用蓝牙帮助很大,
我在Ble 开发中有些问题网上很少有答案,比如蓝牙传输速率,
传输速率提高分两个方向:
1 Android 穿到 外围设备的速率,可以通过requestMtu,在
onMtuChanged 回调中获取申请了多少Mtu, 默认23, 减去1个op, 2个句柄,=20个字节(网上都说最多传20个字节...)
2 外为设备传给Android, 如果每次传20个字节,最少间隔7.5ms, 1.25ms一个步距(5个),
每个间隔时间最多传6个包, 120个字节. 因为外围设备(大多是单片机和蓝牙模块透传的) 写数据也有延迟,10字节/1ms, 最后是 没调成120字节/16ms,可以稳定传输20k数据.

关于notify消息接收问题

通过蓝牙设备发送notify消息,但是手机端接收不到,但是通过nrf测试软件可以接收到消息,消息properties:NOTIFY,是我哪里的配置错了吗?

mBle = Ble.options()//开启配置
                .setLogBleExceptions(true)//设置是否输出打印蓝牙日志(非正式打包请设置为true,以便于调试)
                .setThrowBleException(true)//设置是否抛出蓝牙异常
                .setAutoConnect(false)//设置是否自动连接
                .setFilterScan(true)//设置是否过滤扫描到的设备
                .setConnectFailedRetryCount(3)
                .setConnectTimeout(10 * 1000)//设置连接超时时长(默认10*1000 ms)
                .setScanPeriod(12 * 1000)//设置扫描时长(默认10*1000 ms)
                .setUuidService(UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb"))//主服务的uuid
                .setUuidWriteCha(UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"))//可写特征的uuid
                .setUuidReadCha(UUID.fromString("0000ffe2-0000-1000-8000-00805f9b34fb"))//可读特征的uuid
                .setUuidNotify(UUID.fromString("0000ffe2-0000-1000-8000-00805f9b34fb"))
                .create(this);

  mBle.connect(address.toUpperCase(), connectCallback);

 private BleConnectCallback<BleDevice> connectCallback = new BleConnectCallback<BleDevice>() {
        @Override
        public void onConnectionChanged(BleDevice device) {
            if (device.isConnected()) {
                mBleDevice = device;
                setNotify(device);
            }
        }

        @Override
        public void onConnectException(BleDevice device, int errorCode) {
            super.onConnectException(device, errorCode);
            progressDialog.dismiss();
            Toast.makeText(LockActivity.this, "连接异常,异常状态码:" + errorCode, Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onConnectTimeOut(BleDevice device) {
            super.onConnectTimeOut(device);
            progressDialog.dismiss();
            Toast.makeText(LockActivity.this, "连接超时", Toast.LENGTH_SHORT).show();
        }
    };


private void setNotify(BleDevice device) {
        mBle.startNotify(device, new BleNotiftCallback<BleDevice>() {
            @Override
            public void onChanged(BleDevice device, BluetoothGattCharacteristic characteristic) {
                Toast.makeText(LockActivity.this, "Notify success", Toast.LENGTH_SHORT).show();
                if (characteristic != null && characteristic.getValue() != null && characteristic.getValue().length == 17) {
                    TypeBean typeBean = BleUtils.newInstance().read(characteristic.getValue());
                    if (typeBean != null) {
                        if (Constants.READ_4 == typeBean.getType()) {
                            if (typeBean.getLockType() == Constants.Lock0 || typeBean.getLockType() == Constants.Lock3) {
                                if (tipDialog != null) {
                                    tipDialog.dismiss();
                                }
                            }
                        } else if (Constants.READ_6 == typeBean.getType()) {
                            write06.add(typeBean.getData());
                            if (typeBean.isOk()) {
                                byte[] data = new byte[write06.size() * 10];
                                int size = 0;
                                for (byte[] bytes : write06) {
                                    for (int i = 0; i < bytes.length; i++) {
                                        data[size + i] = bytes[i];
                                    }
                                    size += bytes.length;
                                }
                                byte[] sendData = SocketUtils.write06(data);
                                write06 = new ArrayList<>();
                                mThreadPool.execute(() -> {
                                    try {
                                        outputStream = socket.getOutputStream();

                                        outputStream.write(sendData);
                                        outputStream.flush();

                                        inputStream = socket.getInputStream();
                                    } catch (IOException e) {
                                        e.printStackTrace();
                                    }
                                });
                            }
                        }
                    }
                }
            }
        });
    }

这是我的代码
UUID是正确的

未限制点击事件时,频繁断开重连时会导致重连时不走任何一个回调方法

频繁断开重连时会导致重连时不走任何一个回调方法(v3.0.5)
原因:单个设备在短时间内多次连接,止步于
if (connectedAddressList.contains(address)) {
BleLog.d(TAG, "This is device already connected.");
return true;
}
解决方法:重连前调用
for (vo in mBle?.connetedDevices!!){
if(vo.address.contains(device.address)){
mBle?.bleRequest?.close(device.address)
}
}
然后再进行重连操作

开启关闭蓝牙问题

当关闭的状态开启软件,软件里面手动开启蓝牙,回调进行扫描蓝牙设备就会
04-08 11:59:36.397 9594-9594/com.prohua.bluetooth D/BluetoothAdapter: STATE_ON
04-08 11:59:36.397 9594-9594/com.prohua.bluetooth D/BluetoothLeScanner: could not find callback wrapper

java.lang.NoClassDefFoundError: cn.com.heaton.blelibrary.ble.request.ScanRequest$2

java.lang.NoClassDefFoundError: cn.com.heaton.blelibrary.ble.request.ScanRequest$2
at cn.com.heaton.blelibrary.ble.request.ScanRequest.(ScanRequest.java:120)
at java.lang.Class.newInstanceImpl(Native Method)
at java.lang.Class.newInstance(Class.java:1215)
at cn.com.heaton.blelibrary.ble.request.Rproxy.init(Rproxy.java:36)
at cn.com.heaton.blelibrary.ble.proxy.RequestProxy.bindProxy(RequestProxy.java:39)
at cn.com.heaton.blelibrary.ble.Ble.init(Ble.java:88)
at cn.com.heaton.blelibrary.ble.Ble.create(Ble.java:98)
at cn.com.heaton.blelibrary.ble.Ble$Options.create(Ble.java:790)
at com.xxx.MainActivity.onCreate(MainActivity.java:38)
at android.app.Activity.performCreate(Activity.java:5356)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1088)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2338)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2426)
at android.app.ActivityThread.access$800(ActivityThread.java:158)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1349)
at android.os.Handler.dispatchMessage(Handler.java:110)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:5390)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.