【Android Developers Training】 107. 认知用户当前的行为

注:本文翻译自Google官方的Android
Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好。

原文链接:http://developer.android.com/training/location/activity-recognition.html


样例代码:

ActivityRecognition.zip


行为认知会尝试检测当前用户的物理行为,比如:行走,驾驶或者静止站立。从一个行为认知客户端发出更新信息的请求,同之前的定位或者地理围栏所使用的定位客户端相比有所不同,但大致思路是一致的。基于你所选择的的更新间隔,定位服务会发出一个或更多个用户当前可能的行为信息,同时每一个信息都会有一个可能性级别。这节课将向你展示如何从定位服务实现用户的行为识别。


一). 请求行为认知更新


从定位服务请求一个行为认知更新与请求定期的地点更新是比较类似的。你通过客户端发出请求,然后定位服务通过一个PendingIntent将更新信息发回给你的应用。然而,在你请求行为识别更新之前,你需要申请一些特别的权限,然后你使用不同类型的客户端发出申请。下面各个章节将会讲解如何请求权限,连接客户端并请求更新。

申请就收更新的权限

一个应用如果想要获取行为认知更新,必须有“com.google.android.gms.permission.ACTIVITY_RECOGNITION”的权限。要为你的权限请求这一权限,将下列的XML标签作为<manifest>标签的子标签添加到你的清单列表当中:


<uses-permission
android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION"/>

另外,行为认知不需要ACCESS_COARSE_LOCATION权限和ACCESS_FINE_LOCATION权限。

检查Google Play服务

位置服务是Google
Play服务APK的其中一部分。由于用户设备的状态时难以预料的,你应该一直在你尝试连接定位服务之前,检查APK是否已经安装。要检查APK是否安装,可以调用GooglePlayServicesUtil.isGooglePlayServicesAvailable(),它会返回一个整形的结果码,其含义可以参阅:ConnectionResult。如果你遇到了一个错误,可以调用GooglePlayServicesUtil.getErrorDialog(),来获取一个本地的对话框,引导用户执行正确地行为,之后将这一对话框显示在一个DialogFragment上。这一对话框可能允许用户解决当前的问题,此时Google
Play服务会发回一个结果到你的activity中。要处理这一结果,需要覆写onActivityResult()方法。

Note:

要使你的应用可以兼容1.6及以后版本的系统,显示DialogFragment的activity必须是FragmentActivity的子类,而非Activity。使用FragmentActivity还可以允许你调用getSupportFragmentManager()方法来显示DialogFragment

由于你一直需要在你的代码多个地方检查Google
Play服务,所以应该定义一个方法将检查行为进行封装,之后在每次连接尝试之前进行检查。下面的代码片段包含了检查Google
Play服务所需要的代码:


public class MainActivity extends FragmentActivity {
...
// Global constants
/*
* Define a request code to send to Google Play services
* This code is returned in Activity.onActivityResult
*/
private final static int
CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
...
// Define a DialogFragment that displays the error dialog
public static class ErrorDialogFragment extends DialogFragment {
// Global field to contain the error dialog
private Dialog mDialog;
// Default constructor. Sets the dialog field to null
public ErrorDialogFragment() {
super();
mDialog = null;
}
// Set the dialog to display
public void setDialog(Dialog dialog) {
mDialog = dialog;
}
// Return a Dialog to the DialogFragment.
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return mDialog;
}
}
...
/*
* Handle results returned to the FragmentActivity
* by Google Play services
*/
@Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
// Decide what to do based on the original request code
switch (requestCode) {
...
case CONNECTION_FAILURE_RESOLUTION_REQUEST :
/*
* If the result code is Activity.RESULT_OK, try
* to connect again
*/
switch (resultCode) {
case Activity.RESULT_OK :
/*
* Try the request again
*/
...
break;
}
...
}
...
}
...
private boolean servicesConnected() {
// Check that Google Play services is available
int resultCode =
GooglePlayServicesUtil.
isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode) {
// In debug mode, log the status
Log.d("Activity Recognition",
"Google Play services is available.");
// Continue
return true;
// Google Play services was not available for some reason
} else {
// Get the error dialog from Google Play services
Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
resultCode,
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);

// If Google Play services can provide an error dialog
if (errorDialog != null) {
// Create a new DialogFragment for the error dialog
ErrorDialogFragment errorFragment =
new ErrorDialogFragment();
// Set the dialog in the DialogFragment
errorFragment.setDialog(errorDialog);
// Show the error dialog in the DialogFragment
errorFragment.show(
getSupportFragmentManager(),
"Activity Recognition");
}
return false;
}
}
...
}

在后续章节的代码片段中,都会调用这一方法来验证是否可获取Google
Play服务。

发送行文更新请求

从一个实现了定位服务所需要额回调函数的Activity或者Fragment发送更新请求。当你请求连接到一个行为认知客户端时,最好将请求做成异步的进程。当客户端已经连接了,定位服务会调用你的onConnected()实现。在这个方法中,你可以将更新请求发送给定位服务;该请求是同步的。一旦你发出了请求,你可以关闭客户端的连接。

整个过程会在下面的各个代码片段中展开。

定义Activity或者Fragment

定义一个FragmentActivity或者Fragment实现下列的接口:

ConnectionCallbacks

当客户端连接或者断开连接时定位服务调用的方法。

OnConnectionFailedListener

当请求连接到客户端时发生错误,定位服务调用的方法。

例如:


public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
}

之后定义全局变量和常量。给更新间隔定义常量,为行为识别客户端添加变量,同时还需要为定位服务发送更新时使用的PendingIntent添加另一个变量:


public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
// Constants that define the activity detection interval
public static final int MILLISECONDS_PER_SECOND = 1000;
public static final int DETECTION_INTERVAL_SECONDS = 20;
public static final int DETECTION_INTERVAL_MILLISECONDS =
MILLISECONDS_PER_SECOND * DETECTION_INTERVAL_SECONDS;
...
/*
* Store the PendingIntent used to send activity recognition events
* back to the app
*/
private PendingIntent mActivityRecognitionPendingIntent;
// Store the current activity recognition client
private ActivityRecognitionClient mActivityRecognitionClient;
...
}

onCreate()中,实例化行为认知客户端以及PendingIntent


public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
@Override
onCreate(Bundle savedInstanceState) {
...
/*
* Instantiate a new activity recognition client. Since the
* parent Activity implements the connection listener and
* connection failure listener, the constructor uses "this"
* to specify the values of those parameters.
*/
mActivityRecognitionClient =
new ActivityRecognitionClient(mContext, this, this);
/*
* Create the PendingIntent that Location Services uses
* to send activity recognition updates back to this app.
*/
Intent intent = new Intent(
mContext, ActivityRecognitionIntentService.class);
/*
* Return a PendingIntent that starts the IntentService.
*/
mActivityRecognitionPendingIntent =
PendingIntent.getService(mContext, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
...
}
...
}

开始请求进程

定义一个方法来请求行为识别更新。在该方法中,请求一个到定位服务的连接。你可以在你的activity中任何地方调用该方法;其目标是启动请求更新的一系列操作。

要保证竞争情况的发生(当你的应用尝试在第一个请求没有执行完毕时就启动另一个请求),定义一个布尔标记变量,它跟踪当前请求的状态。当你开始请求时将它设置为True,当请求完毕后,将它设置为false

下面的代码片段展示了如何启动一个更新请求:


public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
// Global constants
...
// Flag that indicates if a request is underway.
private boolean mInProgress;
...
@Override
onCreate(Bundle savedInstanceState) {
...
// Start with the request flag set to false
mInProgress = false;
...
}
...
/**
* Request activity recognition updates based on the current
* detection interval.
*
*/
public void startUpdates() {
// Check for Google Play services

if (!servicesConnected()) {
return;
}
// If a request is not already underway
if (!mInProgress) {
// Indicate that a request is in progress
mInProgress = true;
// Request a connection to Location Services
mActivityRecognitionClient.connect();
//
} else {
/*
* A request is already underway. You can handle
* this situation by disconnecting the client,
* re-setting the flag, and then re-trying the
* request.
*/
}
}
...
}

实现onConnected()。在该方法中,从定位服务请求行为识别更新。当定位服务完成了连接,并调用了onConnected(),更新请求会被立即调用:


public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
/*
* Called by Location Services once the location client is connected.
*
* Continue by requesting activity updates.
*/
@Override
public void onConnected(Bundle dataBundle) {
/*
* Request activity recognition updates using the preset
* detection interval and PendingIntent. This call is
* synchronous.
*/
mActivityRecognitionClient.requestActivityUpdates(
DETECTION_INTERVAL_MILLISECONDS,
mActivityRecognitionPendingIntent);
/*
* Since the preceding call is synchronous, turn off the
* in progress flag and disconnect the client
*/
mInProgress = false;
mActivityRecognitionClient.disconnect();
}
...
}

处理连接断开的情况

在一些情况下,定位服务可能会在你调用disconnect()之前就从行为识别客户端就端开了连接。要处理这一情况,实现onDisconnected()方法。在该方法中,对请求标记变量进行设置,来指出目前请求并不在流程中,然后删除客户端:


public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
/*
* Called by Location Services once the activity recognition
* client is disconnected.
*/
@Override
public void onDisconnected() {
// Turn off the request flag
mInProgress = false;
// Delete the client
mActivityRecognitionClient = null;
}
...
}

处理连接错误

除了处理定位服务的常规回调函数外,你还需要提供一个回调函数,该函数会在连接错误发生的时候被定为服务调用。该回调函数可以重用DialogFragment类(你在检查Google
Play服务时所定义的类)。同时它也可以重用当用户与错误对话框交互时,接收任何由Google Play服务返回的结果的onActivityResult()函数。下面的代码片段展示了该回调函数的一个例子:


public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
// Implementation of OnConnectionFailedListener.onConnectionFailed
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
// Turn off the request flag
mInProgress = false;
/*
* If the error has a resolution, start a Google Play services
* activity to resolve it.
*/
if (connectionResult.hasResolution()) {
try {
connectionResult.startResolutionForResult(
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
} catch (SendIntentException e) {
// Log the error
e.printStackTrace();
}
// If no resolution is available, display an error dialog
} else {
// Get the error code
int errorCode = connectionResult.getErrorCode();
// Get the error dialog from Google Play services
Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
errorCode,
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
// If Google Play services can provide an error dialog
if (errorDialog != null) {
// Create a new DialogFragment for the error dialog
ErrorDialogFragment errorFragment =
new ErrorDialogFragment();
// Set the dialog in the DialogFragment
errorFragment.setDialog(errorDialog);
// Show the error dialog in the DialogFragment
errorFragment.show(
getSupportFragmentManager(),
"Activity Recognition");
}
}
...
}
...
}


二). 处理行为更新

要处理每个更新间隔中定位服务发送的Intent,定义一个IntentService以及它所需要的方法onHandleIntent()。定位服务会将行为认知更新以Intent对象的形式发出,当你调用了requestActivityUpdates()后使用你提供的PendingIntent。一旦你为PendingIntent提供了一个显式地intent,只有你定义的IntentService会接收你的intent。

下面的代码片段阐述了如何在一个行为认知更新中处理数据。

定义一个IntentService

首先定义类及其需要的方法onHandleIntent()


/**
* Service that receives ActivityRecognition updates. It receives
* updates in the background, even if the main Activity is not visible.
*/
public class ActivityRecognitionIntentService extends IntentService {
...
/**
* Called when a new activity detection update is available.
*/
@Override
protected void onHandleIntent(Intent intent) {
...
}
...
}

之后,处理intent中的数据。从更新中,你可以获取一个可能行为的列表以及每个行为的可能性级别。下面的代码片段展示了如何获取最可能的行为信息,行为的可能性级别和它的类型:


public class ActivityRecognitionIntentService extends IntentService {
...
@Override
protected void onHandleIntent(Intent intent) {
...
// If the incoming intent contains an update
if (ActivityRecognitionResult.hasResult(intent)) {
// Get the update
ActivityRecognitionResult result =
ActivityRecognitionResult.extractResult(intent);
// Get the most probable activity
DetectedActivity mostProbableActivity =
result.getMostProbableActivity();
/*
* Get the probability that this activity is the
* the user‘s actual activity
*/
int confidence = mostProbableActivity.getConfidence();
/*
* Get an integer describing the type of activity
*/
int activityType = mostProbableActivity.getType();
String activityName = getNameFromType(activityType);
/*
* At this point, you have retrieved all the information
* for the current update. You can display this
* information to the user in a notification, or
* send it to an Activity or Service in a broadcast
* Intent.
*/
...
} else {
/*
* This implementation ignores intents that don‘t contain
* an activity update. If you wish, you can report them as
* errors.
*/
}
...
}
...
}

方法getNameFromType()会将activity类型转换为带有描述性的字符串。在一个需要发布的应用中,你应该从资源文件中获取字符串而非使用固定的变量值:


public class ActivityRecognitionIntentService extends IntentService {
...
/**
* Map detected activity types to strings
*@param activityType The detected activity type
*@return A user-readable name for the type
*/
private String getNameFromType(int activityType) {
switch(activityType) {
case DetectedActivity.IN_VEHICLE:
return "in_vehicle";
case DetectedActivity.ON_BICYCLE:
return "on_bicycle";
case DetectedActivity.ON_FOOT:
return "on_foot";
case DetectedActivity.STILL:
return "still";
case DetectedActivity.UNKNOWN:
return "unknown";
case DetectedActivity.TILTING:
return "tilting";
}
return "unknown";
}
...
}

在清单文件中指明IntentService

要在系统中指明IntentService,需要再应用的清单文件中添加一个<service>标签,例如:


<service
android:name="com.example.android.location.ActivityRecognitionIntentService"
android:label="@string/app_name"
android:exported="false">
</service>

注意,你不需要为该服务指定intent过滤器,因为它仅会接收显式的intent。如何创建接收的行为更新intent在之前的章节中已经说过了。


三).
停止行为认知更新

要停止行为认知更新,其思路和请求更新是一致的,但是调用的函数是removeActivityUpdates()而不是requestActivityUpdates()

由于移除更新会使用一些你在添加更新时所用到的方法,我们首先为两个操作定义请求类型:


public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
public enum REQUEST_TYPE {START, STOP}
private REQUEST_TYPE mRequestType;
...
}

修改启动行为认知的代码,这样它就能使用“START”请求类型:


public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
public void startUpdates() {
// Set the request type to START
mRequestType = REQUEST_TYPE.START;
/*
* Test for Google Play services after setting the request type.
* If Google Play services isn‘t present, the proper request type
* can be restarted.
*/
if (!servicesConnected()) {
return;
}
...
}
...
public void onConnected(Bundle dataBundle) {
switch (mRequestType) {
case START :
/*
* Request activity recognition updates using the
* preset detection interval and PendingIntent.
* This call is synchronous.
*/
mActivityRecognitionClient.requestActivityUpdates(
DETECTION_INTERVAL_MILLISECONDS,
mActivityRecognitionPendingIntent);
break;
...
/*
* An enum was added to the definition of REQUEST_TYPE,
* but it doesn‘t match a known case. Throw an exception.
*/
default :
throw new Exception("Unknown request type in onConnected().");
break;
}
...
}
...
}

开始过程

定义一个方法,用来请求停止行为认知更新。在该方法中,设置请求类型,并请求一个到定位服务的连接。你可以在你的activity的任何地方调用该方法;其目的是要开始一系列方法的调用来停止更新:


public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
/**
* Turn off activity recognition updates
*
*/
public void stopUpdates() {
// Set the request type to STOP
mRequestType = REQUEST_TYPE.STOP;
/*
* Test for Google Play services after setting the request type.
* If Google Play services isn‘t present, the request can be
* restarted.
*/
if (!servicesConnected()) {
return;
}
// If a request is not already underway
if (!mInProgress) {
// Indicate that a request is in progress
mInProgress = true;
// Request a connection to Location Services
mActivityRecognitionClient.connect();
//
} else {
/*
* A request is already underway. You can handle
* this situation by disconnecting the client,
* re-setting the flag, and then re-trying the
* request.
*/
}
...
}
...
}

onConnected()方法中,如果请求类型是“STOP”,那么调用removeActivityUpdates()。将你用来启动更新的PendingIntent作为参数传递给removeActivityUpdates()


public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
public void onConnected(Bundle dataBundle) {
switch (mRequestType) {
...
case STOP :
mActivityRecognitionClient.removeActivityUpdates(
mActivityRecognitionPendingIntent);
break;
...
}
...
}
...
}

你不需要修改onDisconnected()onConnectionFailed()的实现,因为这些方法并不依赖于该请求类型。

现在你已经有了一个行为认知应用的基本框架了。你可以将行为认知的功能和其它定位相关的功能结合在一起,比如定期的地点更新,地理围栏等,这些内容都在这系列课程中的其它课中讲授过了。

【Android Developers Training】 107. 认知用户当前的行为,布布扣,bubuko.com

时间: 2024-10-14 11:00:10

【Android Developers Training】 107. 认知用户当前的行为的相关文章

【Android Developers Training】 106. 创建并检测地理围栏

注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer.android.com/training/location/geofencing.html 地理围栏可以将用户当前地点信息和周围的地点信息相结合,它其实是用户接近潜在的感兴趣的地点的程度.要标记一个感兴趣的地点,你需要指定它的经纬度.要调整接近的位置,你还需要添加一个半径.经纬度和半径加起来就成为

【Android Developers Training】 102. 序言:让你的应用获知地点

注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer.android.com/training/location/index.html 移动应用的其中一个独一无二的特性是可以获知地点.移动用户会携带它们的设备到任何地方,你的应用会有地点感知的功能,这样的功能提供给了用户更丰富的使用体验.Google Play服务中新的地点服务API(Location

【Android Developers Training】 103. 查询当前地点

注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer.android.com/training/location/retrieve-current.html 样例代码: LocationUpdates.zip 地点服务自动维护用户当前的地点,所以你的应用所要做的事情就是在需要时去获取它.地点的精确度是基于你所申请的地点查询权限,以及当前设备上激活的的

【Android Developers Training】 99. 获取联系人详细信息

注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer.android.com/training/contacts-provider/retrieve-details.html 这节课将会展示如何获取一个联系人的详细数据,比如电子邮件地址,电话号码,等等.当用户获得一个联系人后,他会想要查看他的详细信息.你可以展示给他们所有的信息,或者只展示某一特定类

【Android Developers Training】 108. 使用模拟定位进行测试

注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer.android.com/training/location/location-testing.html 样例代码: LocationProvider.zip 要测试一个使用定位服务的地点认知应用,你不需要将你的设备从一个地方移动到另一个地方来生成数据.你可以将定位服务放到测试模式中.在该模式中你可

Android Developers:ProGuard

ProGuard工具通过删除未使用的代码,使用语义模糊的名字重命名类.字段和方法的方式,减少.优化和混淆你的代码.结果生成一个更小的,更难被反向工程的.apk文件.因为ProGuard使你的应用程序更难反向工程,当你发布的应用程序使用对安全敏感功能的时候,使用它尤为重要. ProGuard已经被集成到Android的构建系统中,所以你不需要手动的调用它.ProGuard仅仅当你在release模式构建你的应用程序时运行,所以你不需要在Debug模式的时候处理被混淆的代码.运行ProGuard是完

Android授权登录新浪微博获取用户个人信息

转载请注明出处:http://blog.csdn.net/u010214991/article/details/49149793 记得第一次发博文的时候我写了一篇文章,题为" Android使用官方API分享内容到QQ和微信(非第三方集成)",里面介绍了如何使用各官方提供的SDK来实现分享功能.那么今天我们来讲讲如何通过新浪微博授权登录获取用户信息,大家都知道,现在很多软件除了本地注册登录之外,还增加了第三方登录功能,最常见的是直接授权QQ等软件来登录应用而无需自行注册,其原理更多的是

Android之使用SharedPreferences保存用户偏好参数

在Android应用中,我们常需要记录用户设置的一些偏好参数,,此时我们就需要用SharedPreferences和Editor将这些信息保存下来,在下次登录时读取. SharedPreferences保存的数据主要类似于配置信息格式的数据,因此它保存数据的形式为key-value对,下面我们来看下实例代码. 首先是界面布局,比较简单,就是一个普通的登陆界面. 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/r

Android黑科技,读取用户短信+修改系统短信数据库

安卓系统比起ios系统最大的缺点,相信大家都知道,就是系统安全问题.这篇博客就秀一波“黑科技”. 读取用户短信 Android应用能读取用户手机上的短信,相信已经不是什么新鲜事,比如我们收到的短信验证码,一些app马上就能自动获取并填上验证码,省去我们手动填写验证码.原理就是通过Android的ContentProvider组件间接访问系统的短信数据库,获取所有短信内容.下面来演示一下. 布局很简单,如下: 代码如下: public class MainActivity extends Acti