介绍蓝牙协议的初始化之前首先要简单讲一下蓝牙一些常用协议以及各个协议的作用,同时还要讲以下Application的知识以便对蓝牙协议的初始化有一个更清晰的认识。
一、蓝牙协议
简介:
HSP(手机规格)– 提供手机(移动电话)与耳机之间通信所需的基本功能。
HFP(免提规格)– 在 HSP 的基础上增加了某些扩展功能,原来只用于从固定车载免提装置来控制移动电话。
A2DP(高级音频传送规格)– 允许传输立体声音频信号。 (相比用于 HSP 和 HFP 的单声道加密,质量要好得
多)
AVRCP(音频/视频遥控规格)–用于从控制器(如立体声耳机)向目标设备(如装有 Media Player 的电脑)发
送命令(如前跳、暂停和播放)。
详细介绍:
HFP
HFP(Hands-freeProfile),让蓝牙设备可以控制电话,如接听、挂断、拒接、语音拨号等,拒接、语音拨号要视蓝牙耳机及电话是否
支持。
HSP
HSP 描述了Bluetooth 耳机如何与计算机或其它Bluetooth 设备(如手机)通信。连接和配置好后,耳机可以作为远程设备的音频输入和输出接口。
这是最常用的配置,为当前流行支持蓝牙耳机与移动电话使用。它依赖于在64千比特编码的音频/s的CVSD的或PCM以及AT命令从
GSM07.07的一个子集,包括环的能力最小的控制,接听来电,挂断以及音量调整。
典型的使用情景是使用无线耳机与手机进行连接。
可能会使用HSP的若干设备类型:耳机、手机、PDA、个人电脑、手提电脑。
A2DP
A2DP全名是AdvancedAudio Distribution Profile蓝牙音频传输模型协定!A2DP是能够采用耳机内的芯片来堆栈数据,达到声音的高
清晰度。有A2DP的耳机就是蓝牙立体声耳机。声音能达到44.1kHz,一般的耳机只能达到8kHz。如果手机支持蓝牙,只要装
载A2DP协议,就能使用A2DP耳机了。还有消费者看到技术参数提到蓝牙V1.0V1.1 V1.2 V2.0——这些是指蓝牙的技术版本,是指
通过蓝牙传输的速度,他们是否支持A2DP具体要看蓝牙产品制造商是否使用这个技术
AVRCP
AVRCP(Audio/VideoRemote Control Profile),也就是音频/视频远程控制规范。
AVRCP设计用于提供控制TV、Hi-Fi设备等的标准接口。此配置文件用于许可单个远程控制设备(或其它设备)控制所有用户可以接
入的A/V设备。它可以与A2DP或VDP配合使用。
AVRCP定义了如何控制流媒体的特征。包括暂停、停止、启动重放、音量控制及其它类型的远程控制操作。AVRCP定义了两个角
色,即控制器和目标设备。控制器通常为远程控制设备,而目标设备为特征可以更改的设备。在AVRCP中,控制器将检测到的用户
操作翻译为A/V控制信号,然后再将其传输至远程Bluetooth设备。对于“随身听”类型的媒体播放器,控制设备可以是允许跳过音轨的
耳机,而目标设备则是实际的播放器。常规红外遥控器的可用功能可以在此协议中实现。
AVRCP协议规定了AV/C数字接口命令集(AV/C命令集,由1394行业协会定义)的应用范围,实现了简化实施和易操作性。此协议
为控制消息采用了AV/C设备模式和命令格式,这些消息可以通过音频/视频控制传输协议(AVCTP)传输。
OPP
蓝牙通信程序部分需采用用于设备之间传输数据对象OPP Profile: Object Push Profile由于OPP profile又细分为OPPC (client)端和
OPPS(server)端profile,这两个profile区别在于只有client端可以发起数据传输的过程,但是附件设备与手机通信的情景中,既有手机
发起数据传输请求也有设备侧发起传输请求的需要,所以要在设备中实现OPPC和OPPS两个profile。
PBAP
电话号码簿访问协议(PhonebookAccess Profile)
前言
最近的开发中经常使用到Application类,它的用处很多,但是网上的资料有很多是旧的或者是介绍不全的,在这里全面总结一下,先介绍Application的所有方法,再介绍它的使用经验。
本文基于Android6.0源码,API LEVEL 23。
介绍
首先看看官网的介绍:
下面是我对Application类的理解:
- 每个APP都有一个Application实例:如果我们没有继承Application子类自定义它的话,APP会创建一个默认的实例。
- Application实例拥有着与APP一样长的生命周期:在APP开启的时候首先就会实例化它,然后才是入口的Activity或者Service等。
- Application与APP“同生共死”,在一个APP的生命周期只实例化一次,所以它“天生”就是一个单例,不需要使用单例模式去实现它。
- 而上面的官方Note里面说到,通常是没有必要实现Application的子类的,要用单例的话可以自己使用静态单例类实现,要用它的Context的话用
Context.getApplicationContext()
就行了。然而,Application类的作用可不单单是实现一个全局的单例,还有其他的很多功能,下面一一介绍。
Application类功能
Application类的方法
首先看看类结构:
Application是继承自ContextWarpper的,继承来的方法就不在这里说了,下面来看看Application的方法:
onCreate()方法
在Application创建的时候调用,一般用于初始化一些东西,如全局的对象,环境的配置等。
onConfigurationChanged(Configuration newConfig)方法
重写此方法可以监听APP一些配置信息的改变事件(如屏幕旋转等),当配置信息改变的时候会调用这个方法。在Manifest文件下的Activity标签(注意是Activity)里配置android:configChanges
属性相应的配置属性,会使Activity在配置改变时候不会重启,只会执行onConfigurationChanged()方法。如:android:configChanges="keyboardHidden|orientation|screenSize"
属性可以使Activity旋转时不重启。
onLowMemory()方法
重写此方法可以监听Android系统整体内存较低时候的事件。按我的理解就是,当APP处于前台时,但是所有后台程序都被kill光了,但是还是内存不足时,系统就会调用这个方法告诉APP,兄弟轮到你了。我们可以在这个方法里面释放一些不重要的资源,来保证到时候内存足够而让APP进程不被系统杀掉,或者提醒用户清一下垃圾,让内存清一点空位出来,我的手机老是这样提示我,不知道是不是这个方法惹的祸。
onTrimMemory(int level)方法
这个方法是一个比较难理解的方法,Trim意思是修剪,按我的理解,用这个方法打个比方:
从前有个伟大的妈妈叫Android系统,她有一群子女叫APP,她含辛茹苦地养着这群熊孩子。当系统老妈发现她的工资(内存)不够下个月的开销的时候,就会回调这个方法,告诉她的APP子女,我现在工资不够了,你们赶紧少吃少用点,不然我就要根据你们的重要性高低来一个一个地“清理门户”了。
这里有传入一个int类型的参数level,它告诉APP们内存不足的严重性(越高越严重)。假如这时候系统内存不足,运行着前台和后台一共几个APP,这些不同的APP会收到系统老妈不同的“劝告信息”:
- TRIM_MEMORY_RUNNING_MODERATE:数值为5,这个APP是系统老妈的“掌上明珠”(前台APP),老妈让APP注意一下:不要大手大脚(释放不用的内存),我的工资(内存)不够养你了,不过就算再不够,只是把你其他不争气兄弟姐妹(杀掉后台APP)清出家门,你注意一下吧。
- TRIM_MEMORY_RUNNING_LOW:数值10,这个APP是系统老妈的“掌上明珠”(前台APP),老妈语重心长地对APP说:孩子,我的工资(内存)实在不够了,你能不能拿点压岁钱出来帮补一下(释放不用的内存),不行的话就要把你的很多兄弟姐妹(杀掉后台APP)送走了。
- TRIM_MEMORY_RUNNING_CRITICAL:数值15,这个APP是系统老妈的“掌上明珠”(前台APP),老妈严重警告APP:臭小子,你的兄弟姐妹(杀掉后台APP)都快走光了,你还不给我多省点钱(要求释放内存),你还真的想把你的兄弟全赶走啊,当时候就剩你一个,说不定你都自身难保啦(执行onLowMemory()方法)。
- TRIM_MEMORY_UI_HIDDEN:数值20,老妈告诉这个APP:你个熊孩子,闯了祸(用户把APP从前台切换到后台),我要收回你的零用钱(UI资源)。
- TRIM_MEMORY_BACKGROUND :数值40,这些APP是老妈收养的(后台APP),老妈在吃完晚饭后留下了他,对他说:孩子啊,现在家里经济不好(内存不足),你就少花点吧,这个月的零用钱不发了吧(要求释放资源),不然的话我们家可能养不下你和你后面的那帮兄弟姐妹了(杀掉后台APP)。
- TRIM_MEMORY_MODERATE :数值60,这些APP是老妈收养的(后台APP),老妈偷偷地跟APP说:孩子啊,你们花费太多了,老妈的工资养不下你们了(内存不足),你们用少点吧(要求释放内存),不然等我把你后面那几个兄弟赶出去之后就轮到你了(已进入LRU缓存列表的中间位置,如果后面的APP进程资源都被回收的话,下一个就是轮到它了)。
TRIM_MEMORY_COMPLETE :数值80,这些APP是老妈充话费送的(后台APP),老妈狠狠地对他说:臭小子,没看到都快揭不开锅了(内存不足)吗?赶紧把你的私房钱拿出来(要求释放资源),不然你们就准备滚出这个家门吧(已处于LRU缓存列表的后面位置,APP随时都有被回收的风险)。
说了这么多其实实际上这个方法有什么用呢?我目前想到的系统用这个方法提醒APP释放一些缓存了,如图片缓存,数据缓存之类的。
这篇文章很详细的讲述了这个方法。
onTerminate()方法
这个方法在程序结束的时候会调用。但是这个方法只用于Android仿真机测试的时候,在Android产品机是不会调用的。所以这个方法并没什么用。
registerActivityLifecycleCallbacks()和unregisterActivityLifecycleCallbacks()
这两个方法用于注册或者注销对APP内所有Activity的生命周期监听,当APP内Activity的生命周期发生变化的时候就会调用ActivityLifecycleCallbacks里面的方法:
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
Log.d(TAG,"onActivityCreated: " + activity.getLocalClassName());
}
@Override
public void onActivityStarted(Activity activity) {
Log.d(TAG,"onActivityStarted: " + activity.getLocalClassName());
}
@Override
public void onActivityResumed(Activity activity) {
Log.d(TAG,"onActivityResumed: " + activity.getLocalClassName());
}
@Override
public void onActivityPaused(Activity activity) {
Log.d(TAG,"onActivityPaused: " + activity.getLocalClassName());
}
@Override
public void onActivityStopped(Activity activity) {
Log.d(TAG, "onActivityStopped: " + activity.getLocalClassName());
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
Log.d(TAG,"onActivityDestroyed: " + activity.getLocalClassName());
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
我们来测试一下,把APP切到后台再打开,log结果是:
onActivityPaused: MainActivity
onActivityStopped: MainActivity
onActivityStarted: MainActivity
onActivityResumed: MainActivity
- 1
- 2
- 3
- 4
registerComponentCallbacks()和unregisterComponentCallbacks()方法
用于注册和注销ComponentCallbacks2回调接口,里面的方法前面已经介绍过,看名字就知道:
registerComponentCallbacks(new ComponentCallbacks2() {
@Override
public void onTrimMemory(int level) {
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
}
@Override
public void onLowMemory() {
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
Context类也有这两个方法,但是Context类的方法只可以使用ComponentCallbacks,比Application少了一个onTrimMemory()回调。
registerOnProvideAssistDataListener()和unregisterOnProvideAssistDataListener()方法
API18以上的方法,网上关于这两个方法的介绍很少,几乎没有,在官网上的介绍是这样的:
This is called when the user is requesting an assist, to build a full ACTION_ASSIST Intent with all of the context of the current application.
好像是当用户请求帮助的时候会调用这个方法,然后会启动一个ACTION_ASSIST的Intent。什么时候才是用户请求帮助呢?StackOverflow里有的人说是长按Home键,外国的机子会跳出Google Now这个助手,至于国内的机子,我用我自己的华为荣耀6P长按Home键是弹出语音助手,但是没有回调这个方法。然后尝试了一下用下面的代码来发送一个ACTION_ASSIST来看看有什么效果:
Intent intent = new Intent(ACTION_ASSIST);
context.startActivity(intent);
- 1
- 2
结果打开了我手机上UC浏览器的语音搜索功能。。。
最后还是搞不懂这个方法什么时候会回调,如果有知道的请告知,谢谢!
Application类的使用
要使用自定义的Application,首先就是要自己新建一个Application的子类,然后把它的名字写在manifest文件里面的application标签里的android:name属性就行,如我的Application子类名字是BaseApplication,则:
android:name=".BaseApplication"
- 1
1.初始化资源
由于Application类是在APP启动的时候就启动,启动在所有Activity之前,所以可以使用它做资源的初始化操作,如图片资源初始化,WebView的预加载,推送服务的注册等等,注意不要执行耗时操作,会拖慢APP启动速度。
2.数据全局共享
- 可以设置一些全局的共享常量,如一些TAG,枚举值等。
- 可以设置一些全局使用的共享变量数据,如一个全局的Handler等等,但是要注意,这里缓存的变量数据的作用周期只在APP的生命周期,如果APP因为内存不足而结束的话,再开启这些数据就会消失,所以这里只能存储一些不重要的数据来使数据全APP共享,想要储存重要数据的话需要SharePreference、数据库或者文件存储等这些本地存储。
- 可以设置一些静态方法来让其他类调用,来使用Application里面的全局变量,如实现APP一键退出功能时候会用到。
public class Config {
private static final String TAG = "AdapterServiceConfig";
/**
* List of profile services.
*/
@SuppressWarnings("rawtypes")
//Do not inclue OPP and PBAP, because their services
//are not managed by AdapterService
private static final Class[] PROFILE_SERVICES = {
HeadsetService.class,
A2dpService.class,
A2dpSinkService.class,
HidService.class,
HealthService.class,
PanService.class,
GattService.class,
BluetoothMapService.class,
HeadsetClientService.class,
AvrcpControllerService.class,
SapService.class
};
/**
* Resource flag to indicate whether profile is supported or not.
*/
private static final int[] PROFILE_SERVICES_FLAG = {
R.bool.profile_supported_hs_hfp,
R.bool.profile_supported_a2dp,
R.bool.profile_supported_a2dp_sink,
R.bool.profile_supported_hid,
R.bool.profile_supported_hdp,
R.bool.profile_supported_pan,
R.bool.profile_supported_gatt,
R.bool.profile_supported_map,
R.bool.profile_supported_hfpclient,
R.bool.profile_supported_avrcp_controller,
R.bool.profile_supported_sap,
};
private static Class[] SUPPORTED_PROFILES = new Class[0];
static void init(Context ctx) {
if (ctx == null) {
return;
}
Resources resources = ctx.getResources();
if (resources == null) {
return;
}
ArrayList<Class> profiles = new ArrayList<Class>(PROFILE_SERVICES.length);
for (int i=0; i < PROFILE_SERVICES_FLAG.length; i++) {
boolean supported = resources.getBoolean(PROFILE_SERVICES_FLAG[i]);
if (supported) {
Log.d(TAG, "Adding " + PROFILE_SERVICES[i].getSimpleName());
profiles.add(PROFILE_SERVICES[i]);
}
}
int totalProfiles = profiles.size();
SUPPORTED_PROFILES = new Class[totalProfiles];
profiles.toArray(SUPPORTED_PROFILES);
}
static Class[] getSupportedProfiles() {
return SUPPORTED_PROFILES;
}
蓝牙协议的初始化很简单,如果不想启动某个蓝牙协议,直接把相应的flag设为false就可以了。
大家感觉还不错的,就点个赞