[转载]实践分析:我是这么利用数据——对需求说不 | 36大数据

mikel阅读(1095)

[转载]实践分析:我是这么利用数据——对需求说不 | 36大数据.

需求

我常常鼓励我的程序员对需求说不,我想和大家分享一些在这方面的经验

首先,人是很奇怪的动物,对于容易获得的东西,往往不够珍惜,特别是像程序这样的虚拟的东东,更是对“免费”获得的程序不用正眼瞧一瞧,通常没有评价,或者评价为:也就那样吧。

这是我们每一位开发者不愿看到的,所以,我对任何需求都说不!

不是我不尊重用户,其实我想做的更好,我认为冒然的提出需求的人,其实不知道自己要什么!但是他们总是以为自己知道自己不要什么,以至于原样按着需 要做出来的软件会被认为并不是需要的,而世界上没有一样的东西,他们总可以挑剔你(这里需要一些逻辑上的理解),所以,大胆的拒绝他,无论他说的多么的着 急!

1、如果这个用户确实很需要某项功能,他会再和你提起这方面的事情,你不会错过做出牛X软件的机会

2、如果这个用户确实很需要某项功能,他会仔细的再想一想,为什么那小子不给我做这个,是不是我提的要求很不靠谱,进而他会绞尽脑汁的想:怎么才能靠谱一点?

3、如果这个用户确实很需要某项功能,他会找到有类似需求的人,集体骚扰你,表明这个确实是民意所指,不是他个人的一厢情愿

4、如果这个用户确实很需要某项功能,他会深入的再研究研究这个问题,或许只是在原有的功能的基础上,稍微配合点工作即可完成,无需新的功能。

5、如果这个用户确实很需要某项功能,xxxxxxx

这里的xxxxx代表很多的内容(当然包括投诉你!不过你要顶住压力),这些内容帮你争取了时间,你其实可以在此期间好好的思考该怎么做,帮你带来所有你期望的:更明确的要求、带着创意的想法、一群可以集体讨论的人等等,也帮用户自己全面的调动了想象力和执行力,他会想尽一切办法达到目的,不是么?

其次,程序员也是很奇怪的动物,往往自己承担压力,喜欢抱怨却不敢和用户以及老板斗争,其实斗一斗大家都运动开来了,每个人都在此过程中说出了自己的想法,如果不被认可,还能激发他改进自己的想法,变的更为巧妙,这往往是创意的来源,真的是太棒了!

最后,说谎是个奇怪的事情,明明很容易做的功能,却要说的很难,这往往需要一些方法,所幸有些通用的方法可以参考

1、我正在忙于其他的项目

2、我觉得这个功能技术上有点困难,我需要合计合计

3、这不在目前的规划路线上,我们把它放到2期可以么?

4、xxxxxxx

故事还没有结束,如同编程有元数据一样,项目也有元数据,这就是——需求统计。

如果你很讲究,就全面的记录每次需求提出的人、时间、背景、是否是前面需求的延续等等,设定一个预警线(某个关于多个指标的函数),到达报警条件的才提上日程。

我们坚持这么做,我们发现

1、在此过程中,往往把一个团队训练的更科学,更有条不紊。

2、程序员和用户产生了更多的交流,他们成为了基友

3、软件开发不再孤立,用户吵闹惯了,各种事情都要叽歪几句,凑着凑着就完成一些很有意思的作品

4、由于随时要吵架,团队成员变得外向了。

好了,我是这么利用数据在和需求玩游戏,你呢?

作者:欢乐的财神

实践分享:我是这么利用数据の小身材,大味道(一)>>>

End.

[转载]AsyncTask | Android Developers

mikel阅读(835)

转载AsyncTask | Android Developers.

ublic abstract class

AsyncTask

extends Object

java.lang.Object
   ↳ Android.os.AsyncTask<Params, Progress, Result>

Class Overview


AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent pacakge such as Executor, ThreadPoolExecutor and FutureTask.

An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread. An asynchronous task is defined by 3 generic types, called Params, Progress and Result, and 4 steps, called onPreExecute, doInBackground, onProgressUpdate and onPostExecute.

Developer Guides

For more information about using tasks and threads, read the Processes and Threads developer guide.

Usage


AsyncTask must be subclassed to be used. The subclass will override at least one method (doInBackground(Params...)), and most often will override a second one (onPostExecute(Result).)

Here is an example of subclassing:

 privateclassDownloadFilesTaskextendsAsyncTask<URL,Integer,Long>{
     protectedLong doInBackground(URL... urls){
         int count = urls.length;
         long totalSize =0;
         for(int i =0; i < count; i++){
             totalSize +=Downloader.downloadFile(urls[i]);
             publishProgress((int)((i /(float) count)*100));
             // Escape early if cancel() is called
             if(isCancelled())break;
         }
         return totalSize;
     }

     protectedvoid onProgressUpdate(Integer... progress){
         setProgressPercent(progress[0]);
     }

     protectedvoid onPostExecute(Long result){
         showDialog("Downloaded "+ result +" bytes");
     }
 }
 

Once created, a task is executed very simply:

 newDownloadFilesTask().execute(url1, url2, url3);
 

AsyncTask’s generic types


The three types used by an asynchronous task are the following:

  1. Params, the type of the parameters sent to the task upon execution.
  2. Progress, the type of the progress units published during the background computation.
  3. Result, the type of the result of the background computation.

Not all types are always used by an asynchronous task. To mark a type as unused, simply use the type Void:

 privateclassMyTaskextendsAsyncTask<Void,Void,Void>{...}
 

The 4 steps


When an asynchronous task is executed, the task goes through 4 steps:

  1. onPreExecute(), invoked on the UI thread before the task is executed. This step is normally used to setup the task, for instance by showing a progress bar in the user interface.
  2. doInBackground(Params...), invoked on the background thread immediately after onPreExecute() finishes executing. This step is used to perform background computation that can take a long time. The parameters of the asynchronous task are passed to this step. The result of the computation must be returned by this step and will be passed back to the last step. This step can also use publishProgress(Progress...) to publish one or more units of progress. These values are published on the UI thread, in the onProgressUpdate(Progress...) step.
  3. onProgressUpdate(Progress...), invoked on the UI thread after a call to publishProgress(Progress...). The timing of the execution is undefined. This method is used to display any form of progress in the user interface while the background computation is still executing. For instance, it can be used to animate a progress bar or show logs in a text field.
  4. onPostExecute(Result), invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter.

Cancelling a task


A task can be cancelled at any time by invoking cancel(boolean). Invoking this method will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object), instead of onPostExecute(Object) will be invoked after doInBackground(Object[]) returns. To ensure that a task is cancelled as quickly as possible, you should always check the return value of isCancelled() periodically from doInBackground(Object[]), if possible (inside a loop for instance.)

Threading rules


There are a few threading rules that must be followed for this class to work properly:

Memory observability


AsyncTask guarantees that all callback calls are synchronized in such a way that the following operations are safe without explicit synchronizations.

Order of execution


When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.

If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.

Summary


Nested Classes
enum AsyncTask.Status Indicates the current status of the task.
Fields
public static final Executor SERIAL_EXECUTOR An Executor that executes tasks one at a time in serial order.
public static final Executor THREAD_POOL_EXECUTOR An Executor that can be used to execute tasks in parallel.
Public Constructors
AsyncTask()

Creates a new asynchronous task.
Public Methods
final boolean cancel(boolean mayInterruptIfRunning)

Attempts to cancel execution of this task.

static void execute(Runnable runnable)

Convenience version of execute(Object) for use with a simple Runnable object.
final AsyncTask<Params, Progress, Result> execute(Params… params)

Executes the task with the specified parameters.
final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params… params)

Executes the task with the specified parameters.
final Result get(long timeout, TimeUnit unit)

Waits if necessary for at most the given time for the computation to complete, and then retrieves its result.
final Result get()

Waits if necessary for the computation to complete, and then retrieves its result.
final AsyncTask.Status getStatus()

Returns the current status of this task.
final boolean isCancelled()

Returns true if this task was cancelled before it completed normally.
Protected Methods
abstract Result doInBackground(Params… params)

Override this method to perform a computation on a background thread.
void onCancelled(Result result)

Runs on the UI thread after cancel(boolean) is invoked and doInBackground(Object[]) has finished.

void onCancelled()

Applications should preferably override onCancelled(Object).

void onPostExecute(Result result)

Runs on the UI thread after doInBackground(Params...).

void onPreExecute()

Runs on the UI thread before doInBackground(Params...).
void onProgressUpdate(Progress… values)

Runs on the UI thread after publishProgress(Progress...) is invoked.
final void publishProgress(Progress… values)

This method can be invoked from doInBackground(Params...) to publish updates on the UI thread while the background computation is still running.
[Expand]

Inherited Methods
From class java.lang.Object

Fields


public static final Executor SERIAL_EXECUTOR

Added in API level 11

An Executor that executes tasks one at a time in serial order. This serialization is global to a particular process.

public static final Executor THREAD_POOL_EXECUTOR

Added in API level 11

An Executor that can be used to execute tasks in parallel.

Public Constructors


public AsyncTask ()

Added in API level 3

Creates a new asynchronous task. This constructor must be invoked on the UI thread.

Public Methods


public final boolean cancel (boolean mayInterruptIfRunning)

Added in API level 3

Attempts to cancel execution of this task. This attempt will fail if the task has already completed, already been cancelled, or could not be cancelled for some other reason. If successful, and this task has not started when cancel is called, this task should never run. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.

Calling this method will result in onCancelled(Object) being invoked on the UI thread after doInBackground(Object[]) returns. Calling this method guarantees that onPostExecute(Object) is never invoked. After invoking this method, you should check the value returned by isCancelled() periodically from doInBackground(Object[]) to finish the task as early as possible.

Parameters
mayInterruptIfRunning true if the thread executing this task should be interrupted; otherwise, in-progress tasks are allowed to complete.
Returns
  • false if the task could not be cancelled, typically because it has already completed normally; true otherwise

public static void execute (Runnable runnable)

Added in API level 11

Convenience version of execute(Object) for use with a simple Runnable object. See execute(Object[]) for more information on the order of execution.

public final AsyncTask<Params, Progress, Result> execute (Params… params)

Added in API level 3

Executes the task with the specified parameters. The task returns itself (this) so that the caller can keep a reference to it.

Note: this function schedules the task on a queue for a single background thread or pool of threads depending on the platform version. When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting HONEYCOMB, tasks are back to being executed on a single thread to avoid common application errors caused by parallel execution. If you truly want parallel execution, you can use the executeOnExecutor(Executor, Params...) version of this method with THREAD_POOL_EXECUTOR; however, see commentary there for warnings on its use.

This method must be invoked on the UI thread.

Parameters
params The parameters of the task.
Returns
  • This instance of AsyncTask.
Throws
IllegalStateException If getStatus() returns either RUNNING or FINISHED.

public final AsyncTask<Params, Progress, Result> executeOnExecutor (Executor exec, Params… params)

Added in API level 11

Executes the task with the specified parameters. The task returns itself (this) so that the caller can keep a reference to it.

This method is typically used with THREAD_POOL_EXECUTOR to allow multiple tasks to run in parallel on a pool of threads managed by AsyncTask, however you can also use your own Executor for custom behavior.

Warning: Allowing multiple tasks to run in parallel from a thread pool is generally not what one wants, because the order of their operation is not defined. For example, if these tasks are used to modify any state in common (such as writing a file due to a button click), there are no guarantees on the order of the modifications. Without careful work it is possible in rare cases for the newer version of the data to be over-written by an older one, leading to obscure data loss and stability issues. Such changes are best executed in serial; to guarantee such work is serialized regardless of platform version you can use this function with SERIAL_EXECUTOR.

This method must be invoked on the UI thread.

Parameters
exec The executor to use. THREAD_POOL_EXECUTOR is available as a convenient process-wide thread pool for tasks that are loosely coupled.
params The parameters of the task.
Returns
  • This instance of AsyncTask.
Throws
IllegalStateException If getStatus() returns either RUNNING or FINISHED.

public final Result get (long timeout, TimeUnit unit)

Added in API level 3

Waits if necessary for at most the given time for the computation to complete, and then retrieves its result.

Parameters
timeout Time to wait before cancelling the operation.
unit The time unit for the timeout.
Returns
  • The computed result.
Throws
CancellationException If the computation was cancelled.
ExecutionException If the computation threw an exception.
InterruptedException If the current thread was interrupted while waiting.
TimeoutException If the wait timed out.

public final Result get ()

Added in API level 3

Waits if necessary for the computation to complete, and then retrieves its result.

Returns
  • The computed result.
Throws
CancellationException If the computation was cancelled.
ExecutionException If the computation threw an exception.
InterruptedException If the current thread was interrupted while waiting.

public final AsyncTask.Status getStatus ()

Added in API level 3

Returns the current status of this task.

Returns
  • The current status.

public final boolean isCancelled ()

Added in API level 3

Returns true if this task was cancelled before it completed normally. If you are calling cancel(boolean) on the task, the value returned by this method should be checked periodically from doInBackground(Object[]) to end the task as soon as possible.

Returns
  • true if task was cancelled before it completed
See Also

Protected Methods


protected abstract Result doInBackground (Params… params)

Added in API level 3

Override this method to perform a computation on a background thread. The specified parameters are the parameters passed to execute(Params...) by the caller of this task. This method can call publishProgress(Progress...) to publish updates on the UI thread.

Parameters
params The parameters of the task.
Returns
  • A result, defined by the subclass of this task.

protected void onCancelled (Result result)

Added in API level 11

Runs on the UI thread after cancel(boolean) is invoked and doInBackground(Object[]) has finished.

The default implementation simply invokes onCancelled() and ignores the result. If you write your own implementation, do not call super.onCancelled(result).

Parameters
result The result, if any, computed in doInBackground(Object[]), can be null

protected void onCancelled ()

Added in API level 3

Applications should preferably override onCancelled(Object). This method is invoked by the default implementation of onCancelled(Object).

Runs on the UI thread after cancel(boolean) is invoked and doInBackground(Object[]) has finished.

protected void onPostExecute (Result result)

Added in API level 3

Runs on the UI thread after doInBackground(Params...). The specified result is the value returned by doInBackground(Params...).

This method won’t be invoked if the task was cancelled.

Parameters
result The result of the operation computed by doInBackground(Params...).

protected void onPreExecute ()

Added in API level 3

protected void onProgressUpdate (Progress… values)

Added in API level 3

Runs on the UI thread after publishProgress(Progress...) is invoked. The specified values are the values passed to publishProgress(Progress...).

Parameters
values The values indicating progress.

protected final void publishProgress (Progress… values)

Added in API level 3

This method can be invoked from doInBackground(Params...) to publish updates on the UI thread while the background computation is still running. Each call to this method will trigger the execution of onProgressUpdate(Progress...) on the UI thread. onProgressUpdate(Progress...) will note be called if the task has been canceled.

Parameters
values The progress values to update the UI with.

[转载]谈谈基于OAuth 2.0的第三方认证 [上篇] - Artech - 博客园

mikel阅读(984)

[转载]谈谈基于OAuth 2.0的第三方认证 [上篇] – Artech – 博客园.

对于目前大部分Web应用来说,用户认证基本上都由应用自身来完成。具体来说,Web应用利用自身存储的用户凭证(基本上是用户名/密码)与用户提供的凭证进行比较进而确认其真实身份。但是这种由Web应用全权负责的认证方式会带来如下两个问题:

  • 对 于用户来说,他们不得不针对不同的访问Web应用提供不同的用户凭证。如果这些凭证具有完全不同的密码,我们没有多少人能够记得住,所以对于大部分整天畅 游Internet的网友来说,我想他们在不同的网站注册的帐号都会采用相同的密码。密码的共享必然带来安全隐患,因为我们不能确定Web应用本身是否值 得信任。“信任危机”来源于两个方面:首先是对“人品”缺乏信任,我们不知道他们是否具有保护用户敏感信息的意愿;其次是对“能力”缺乏信任,即我们不清 楚他们是否有能力保护用户的敏感信息,对于知名网站泄露用户帐号信息的情况我们实在已经看的太多了。
  • 对于Web应用的提供者 来说,他们不得不对花费大量的时间、精力和资源来设计和开发自己的认证系统。在很多情况下,他们提供的是包含多个子系统的一整套解决方案,如果每个子系统 均需要独立的认证,那么投入的成本可想而知。所以他们希望能够提供一个单一的“认证中心”来对整个“生态系统”提供认证,如果这个认证中心完全由第三方来 免费提供,这无疑是最好的选择。

上面提出的这两点旨在说明一个问题:在Internet环境下,我们针对具体的Web 应用设计独立的认证系统往往是一件“吃力不讨好”的事情。如果我们开发一个很小的Web应用,可能在实现用户认证功能上面花费的成本比实现应用自身业务功 能的成本更大,而且还会因为“信任危机”导致潜在的使用者不敢注册。

在这种情况下,如果一个值得信任的第三方能够提供一种免费的认证服 务,那么这两个问题均会迎刃而解。实际上目前这样的第三方认证服务很多,而且他们的提供者均为值得信赖的IT服务提供商,比如微软、Google、 Facebook、Twitter,以及国内的新浪、腾讯、网易、人人和豆瓣等。就目前来说,这些第三方认证服务绝大部分均是基于OAuth 2.0设计的。

假设我有一件非常重要的文件存储与于瑞士银行的私有保险柜中,如果我需要委托某个人将他提取出来,除了将密码告诉他之外 别无他法,但是OAuth的目的却是定义一种协议帮助资源的拥有者在不提供自身凭证的前提下授权第三方应用以他的名义存取受保护的资源。OAuth的全称 为“Open Authorization”,所以它是一个开发的协议,目前最新的版本为2.0。

OAuth 2.0的角色

获 得资源拥有者授权的第三方应用请求受保护的资源采用的不是授权者的凭证,所有一个被称为Access Token的安全令牌。Access Token颁发过程会涉及到若干不同的“实体”,它们在这个过程中扮演者不同的角色,我们通过一个具体的场景来认识一下该过程中涉及到的几种角色。

假 设我们开发了一个集成了新浪微博认证的用于发布打折商品消息的App,经过用户授权之后它可以调用新浪微博的Web API或者用户的电子邮箱地址并发布相应的打折消息。那么OAuth 2.0在这个场景中的作用就在于:用户授权该应用以自己的名义调用新浪微博的Web API获取自己的电子邮箱地址,整个过程涉及到如下4种角色。

  • 资源拥有者(RO:Resource Owner):资源的拥有者也是授权者,如果它是一个“人”,一般就是指最终用户。由于“资源”在这个场景中表示为用户的电子邮箱地址,所以资源拥有者自然就是指最终用户。
  • 客户端应用(Client):需要取得资源拥有者授权并最终访问受保护资源的应用,对于我们的场景来说,就是我们创建的App。
  • 资源服务器(Resource Server):最终承载资源的服务器,它一般体现为一个可被调用的Web API。对于我们提供的场景来说,客户端通过调用新浪微博得Web API获得用户的电子邮箱地址,所以新浪微博就是资源服务器。
  • 授权服务器(Authorization Server):它对用户(一般情况下为资源拥有者)和客户端应用实施认证,并在用户授权的情况下向客户端应用颁发Access Token。在我们提供的场景中,资源服务器和认证服务器合二为一,均为新浪微博。

客户端凭证

一 般来说,如果我们需要针对某种类型的第三方认证服务来开发我们自己的应用,我们需要向采用的认证服务提供商对该应用进行注册,注册成功之后会得到一个唯一 标识该应用的ClientID和对应的ClientSecret(ClientID/ClientSecret是Windows Live Connect 的说法,Twitter和Facebook分别叫做ConsumerKey/ComsumerSecret和AppID/AppSecret。如果采用 Google提供的OAuth 2.0 API,ClientID和ClientSecret是不需要的。)。它们相当于客户端应用的凭证,认证服务利用它们来确定其真实身份。

接 下来我们简单演示一下如何为集成Windows Live Connect API的应用注册一个ClientID。我们利用浏览器直接访问https://account.live.com/developers /applications,如果当前用户尚未登录,浏览器会自动重定向到登录窗口。当我们采用某个Windows Live帐号完成登录之后,如下图所示的“Windows Live Developer Center”页面会呈现出来。

然 后我们直接点击“Create application”连接创建一个新的应用。我们只需要在显示页面的“Application name”文本框中为创建的应用设置一个名称,同时在“Language”下拉框中选择适合的语言。如下图所示,我们为创建的应用取名为 “AppDemo”。

当 我们点击“I accept”按钮之后,应用被成功创建,相应的ClientID和ClientSecret也被生成出来。如下图所示,ClientID和 ClientSecret的值分别为“000000004410A2A5”和“HeIrRmGyHHtMqhBDJipfGiauQnSHtYUX”。除 此之外,我们要需要设置重定向地址的域名,Windows Live向客户端应用发送Access Token,以及其他数据采用的URI必须采用此域名,我们在下图中指定的域名为“https://www.artech.com”。域名成功设置之后, 点击“Save”按钮之后整个注册工作结束。

处理流程

虽然OAuth 2.0具体采用的执行流程因采用不同类型的授权方式而有所不同,但是整个流程大体上由客户端应用分别与资源拥有者、授权服务器和资源服务器进行的3轮交互来完成。这个过程基本上体现在下图中,这被称为经典的“Three-Legged OAuth”。

客 户端应用试图获取某个受保护的资源,首先得取得资源拥有者的授权,所以第一轮消息交换旨在让客户端获得资源拥有者(即用户)的授权。客户端应用得到授权之 后会得到一个被称为Authorization Grant的对象,该对象实际上就是一个简单的字符串用以表示成功取得了用户的授权。接下来客户端应用利用此Authorization Grant向授权服务取获取用于访问受保护资源所需的Access Token。在成功获得Access Token之后,客户端应用将其附加到针对资源服务器的请求中以获取它所需要的目标资源。

Authorization Grant

OAuth 2.0的执行流程有点类似于的Kerberos认证: 客户端先获得“认购权证”TGT(Ticket Granting Ticket),再利用TGT购买“入场券”ST(Service Ticket),最后凭借ST进行服务调用。对于OAuth 2.0来说,Access Token相当于Kerberos的ST,而Authorization Grant则与TGT具有相同的作用。

OAuth 2.0中的Authorization Grant代表一种中间凭证(Intermediate Credential),它代表了资源拥有者针对客户端应用获取目标资源的授权。OAuth 2.0定义了如下4种Authorization Grant类型,我们也可以利用定义其中的扩展机制定制其他类型的Authorization Grant。Authorization Grant的类型体现了授权采用的方式以及Access Token的获取机制。

  • Implicit:它代表一种“隐 式”授权方式,即客户端在取得资源拥有者(最终用户)授权的情况下直接获取Access Token,而不是间接地利用获取的Authorization Grant来取得Access Token。换句话说,此种类型的Authorization Grant代表根本不需要Authorization Grant,那么上面介绍的“Three-Legged OAuth”变成了“Two-Legged OAuth”。
  • Authorization Code:这是最为典型的Authorization Grant,客户端应用在取得资源拥有者授权之后会从授权服务器得到一个Authorization Code作为Authorization Grant。在它获取寄宿于资源服务器中的目标资源之前,需要利用此Authorization Code从授权服务器获取Access Token。
  • Resource Owner Password Credentials:资源拥有者的凭证直接作为获取Access Token的Authorization Grant。这种Authorization Grant类型貌似与OAuth设计的初衷向违背(OAuth的主要目的在于让客户端应用在不需要提供资源拥有者凭证的情况下能够以他的名义获取受保护的 资源),但是如果客户端程序是值得被信任的,用户(资源拥有者)向其提供自己的凭证也是可以接受的。
  • Client Credentials:客户端应用自身的凭证直接作为它用于获取Access Token的Authorization Grant。这种类型的Authorization Grant适用于客户端应用获取属于自己的资源,换句话说客户端应用本身相当于资源的拥有者。

在本系列后续两篇文章中我们将对Implicit和Authorization Code这两种典型的Authorization Grant进行详细介绍,敬请期待。

谈谈基于OAuth 2.0的第三方认证 [上篇]
谈谈基于OAuth 2.0的第三方认证 [中篇]
谈谈基于OAuth 2.0的第三方认证 [下篇]

[转载]谈谈基于OAuth 2.0的第三方认证 [中篇] - Artech - 博客园

mikel阅读(1080)

[转载]谈谈基于OAuth 2.0的第三方认证 [中篇] – Artech – 博客园.

虽然我们在《上篇》 分别讨论了4种预定义的Authorization Grant类型以及它们各自的适用场景的获取Access Token的方式,我想很多之前没有接触过OAuth 2.0的读者朋友们依然会有“不值所云” 之感,所以在介绍的内容中,我们将采用实例演示的方式对Implicit和Authorization Code这两种常用的Authorization Grant作深入介绍。本章着重介绍Implicit Authorization Grant。

Implicit Authorization Grant授权流程

假 设我们的客户端应用集成了Windows Live Connect API认证服务,并且在成功取得用户授权并得到Access Token之后调用相应的Web API获取当前登录用户的个人信息。一般来说,Implicit类型的Authorization Grant大都被将浏览器作为执行上下文的客户端应用采用,换句话说,这样的客户端就是在浏览器中执行的JavaScript程序。下图体现了这样一个采用Implicit类型的Authorization Grant的客户端应用取得授权、得到Access Token并最终获取到受保护资源(登录用户个人信息)的完整流程。

如右图所示,用户会先被客户端应用重定向到授权服务器(login.live.com),具体的地址为“https://login.live.com/oauth20_authorize.srf”。相关的输入参数通过查询字符串的形式,必须提供的参数包含在如下的列表中。

  • response_type: 表示请求希望获取的对象类型,在此我们希望获取的是Access Token,所以这里指定的值为“token”。
  • redirect_uri: 表示授权服务器在获得用户授权并完成对用户的认证之后重定向的地址,Access Token就以Hash(#)的方式附加在该URL后面。客户端应用利用这个地址接收Access Token。
  • client_id: 唯一标识被授权客户端应用的ClientID。
  • scope: 表示授权的范围,如果采用“wl.signin”意味着允许用户从客户端应用直接登录到Live Services,如果Scope为“wl.basic”则表示运行客户端应用获取联系人信息。如果读者朋友希望了解Windows Live Connect具体支持那些Scope,可以查阅Windows Live Connect API的官方文档。

如果 当前用户尚未登录到Windows Live Services,登录窗口将会出现,当用户输入正确Windows Live帐号和密码并成功通过认证之后,浏览器其上会出现如下图所示的授权页面,具体需要授予的权限集取决于上面介绍的Scope参数。我们点击 “Yes”按钮完成授权,成功授权之后,这个的授权页面在后续的请求中将不会再出现。

授 权服务器在获取用户的授权之后,会生成一个Access Token。接下来,它会提取请求中指定的重定向地址(即redirect_uri参数),然后将生成的Access Token以Hash(#)的形式附加在该地址后面,最终针对这个携带有Access Token的新地址返回一个重定向的响应。如第一张图所示,我们采用的重定向地址为“http://www.myapp.com /capturetoken”,那么最终浏览器将会重定向到地址“http://www.myapp.com/capturetoken#acess_token={accesstoken}”上。

这个重定向地址对应着客户端应用需要获取授权资源的页面,该页面可以直接从代表当前地址的URL中获得Access Token,并利用它来获取目标资源。对于我们的例子来说,它需要获取当前Windows Live帐号的基本信息,请求的地址为“https://apis.live.net/v5.0/me”,Access Token以查询字符串的形式(“?access_token={accesstoken}”)提供给资源服务器,后者据此验证请求的合法性并在验证成功的情况下将当前用户的基本信息以JSON的形式返回给客户端应用。

实例演示:创建采用Implicit Authorization Grant的Web API应用

接下来我们创建一个ASP.NET Web API程序来实现上面这个应用场景。我们首先需要按照《上篇》介绍的流程为该应用注册一个ClientID,如果我们已经在Windows Live Connect上创建了一个应用,我们可以直接使用该应用的ClientID。

假设我们在Windows Live Connect创建了一个采用“https://www.artech.com” 作为域名的应用,我们需要利用hosts文件(“%windir%\System32\drivers\etc\hosts”)将此域名映射为本机的IP 地址(127.0.0.1),具体的映射脚本如下所示。除此之外,由于我们采用HTTPS并且采用本地IIS作为宿主,所以我们需要为Web API应用所在的站点添加一个HTTPS绑定。

   1: 127.0.0.1 www.artech.com

在具体介绍认证实现原理之前,我们不妨先来演示一下最终达到的效果。我们在ASP.NET Web API应用中定义了如下一个继承自ApiController的DemoController,它具有唯一一个用于获取当前登录用户个人基本信息的 Action方法GetProfile。在该方法中,它通过我们定义的扩展方法TryGetAccessToken从当前请求中提取Access Token,然后利用它调用Windows Live Connect提供的Web API(https://apis.live.net/v5.0/me)。

   1: [Authenticate("https://www.artech.com/webapi/account/capturetoken")]
   2: public class DemoController : ApiController
   3: {
   4:     public HttpResponseMessage GetProfile()
   5:     {
   6:         string accessToken;
   7:         if (this.Request.TryGetAccessToken(out accessToken))
   8:         {
   9:             using (HttpClient client = new HttpClient())
  10:             {
  11:                 string address = string.Format("https://apis.live.net/v5.0/me?access_token={0}", accessToken);
  12:                 return client.GetAsync(address).Result;
  13:             }
  14:         }
  15:         return new HttpResponseMessage(HttpStatusCode.BadRequest) { ReasonPhrase = "No access token" };
  16:     }
  17: }

集成Windows Live Connect认证的实现最终是通过应用在DemoController类型上的AuthenticateAttribute特性来完成的,这是一个AuthenticationFilter,作为参数的URL指向一个用于获取和转发Access Token的Web页面。 现在我们直接利用浏览器来调用定义在DemoController中的Action方法GetProfile,如果当前用户尚未登录到Windows Live,浏览器会自动重定向到Windows Live的登录界面。当我们输入正确Windows Live帐号和密码后,当前用户的基本信息以JSON格式显示在浏览器上(如果尚未对该应用进行授权,如上图所示的页面会呈现出来),具体的效果如下图所 示。

应用在DemoController上的AuthenticateAttribute特性完成了针对授权页面的重定向和Access Token的请求和接收。除此之外,为了让浏览器能够在第一次认证之后能够自动地发送Access Token,我们利用AuthenticateAttribute将Access Token写入了Cookie之中,这与Forms认证比较类似。不过就安全的角度来讲,利用Cookie携带安全令牌会引起一种被称为“跨站请求伪造 (CSRF:Cross-Site Request Forgery)”的安全问题,所以通过HTTP报头来作为安全令牌的载体是更安全的做法。

如下所示的代码片断体现了整个AuthenticateAttribute特性的定义,我们可以看到它同时实现了 IAuthenticationFilter和IActionFilter。字符串常量CookieName表示携带Access Token的Cookie名称,只读属性CaptureTokenUri表示授权服务器发送Access Token采用的重定向地址,它指向一个我们由我们设计的Web页面,该页面在接受到Access Token之后会自动向目标资源所在的地址发送一个请求,该请求地址以查询字符串的形式携带此Access Token。(之所以我们需要利用一个Web页面在客户端(浏览器)接收并重发Access Token,是因为授权服务器将返回的Access Token至于重定向URI的Hash(#)部分,所以在服务端是获取不到的,只能在客户端来收集。这个Web页面的目的在在于在客户端获取的Access Token并发送到服务端。)

   1: [AttributeUsage(AttributeTargets.Class| AttributeTargets.Method)]
   2: public class AuthenticateAttribute : FilterAttribute, IAuthenticationFilter, IActionFilter
   3: {
   4:     public const string CookieName = "AccessToken";
   5:     public string     CaptureTokenUri { get; private set; }
   6:
   7:     public AuthenticateAttribute(string captureTokenUri)
   8:     {
   9:         this.CaptureTokenUri = captureTokenUri;
  10:     }
  11:
  12:     public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
  13:     {
  14:         return Task.FromResult<object>(null);
  15:     }
  16:
  17:     public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
  18:     {
  19:         string accessToken;
  20:         if (!context.Request.TryGetAccessToken(out accessToken))
  21:         {
  22:             string clientId = "000000004810C359";
  23:             string redirectUri = string.Format("{0}?requestUri={1}", this.CaptureTokenUri, context.Request.RequestUri);
  24:             string scope = "wl.signin%20wl.basic";
  25:
  26:             string uri = "https://login.live.com/oauth20_authorize.srf";
  27:             uri += "?response_type=token";
  28:             uri += "&redirect_uri={0}&client_id={1}&scope={2}";
  29:             uri = String.Format(uri, redirectUri, clientId, scope);
  30:             context.Result = new RedirectResult(new Uri(uri), context.Request);
  31:         }
  32:         return Task.FromResult<object>(null);
  33:     }
  34:
  35:     public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
  36:     {
  37:         HttpResponseMessage response = continuation().Result;
  38:         string accessToken;
  39:         if (actionContext.Request.TryGetAccessToken(out accessToken))
  40:         {
  41:             response.SetAccessToken(actionContext.Request, accessToken);
  42:         }
  43:         return Task.FromResult<HttpResponseMessage>(response);
  44:     }
  45: }

在实现的ChallengeAsync方法(该方法在认证过程中向客户端发送“质询”响应)中,我们利用自定义的扩展方法 TryGetAccessToken试着从当前请求中获取携带的Access Token。如果这样的Access Token不存在,我们通过为HttpAuthenticationChallengeContext的Result属性设置一个 RedirectResult对象实现针对Windows Live Connect授权页面的重定向,相关的参数(respone-type、redirect_uri、client_id和scope)以查询字符串的形 式提供。

值得一提的作为重定向地址的参数redirect_uri,我们会将当前请求的地址作为查询字符串(名称为“requestUri”)附加到 CaptureTokenUri上得到的URI作为该参数的值,当前请求的地址正式Web页面发送Access Token的目标地址。

另一个实现的ExecuteActionFilterAsync方法复杂将Access Token写入响应Cookie之中,具体的操作实现在我们自定义的扩展方法SetAccessToken中。下面的代码片断给出了两个扩展方法 SetAccessToken和TryGetAccessToken的定义。

   1: public static class Extensions
   2: {
   3:     public  static bool TryGetAccessToken(this HttpRequestMessage request, out string accessToken)
   4:     {
   5:         //从Cookie中获取Access Token
   6:         accessToken = null;
   7:         CookieHeaderValue cookieValue = request.Headers.GetCookies(AuthenticateAttribute.CookieName).FirstOrDefault();
   8:         if (null != cookieValue)
   9:         {
  10:             accessToken = cookieValue.Cookies.FirstOrDefault().Value;
  11:             return true;
  12:         }
  13:
  14:         //从查询字符串中获取Access Token
  15:         accessToken = HttpUtility.ParseQueryString(request.RequestUri.Query)["access_token"];
  16:         return !string.IsNullOrEmpty(accessToken);
  17:     }
  18:
  19:     public static void SetAccessToken(this HttpResponseMessage response, HttpRequestMessage request, string accessToken)
  20:     {
  21:         if (request.Headers.GetCookies(AuthenticateAttribute.CookieName).Any())
  22:         {
  23:             return;
  24:         }
  25:         CookieHeaderValue cookie = new CookieHeaderValue(AuthenticateAttribute.CookieName, accessToken)
  26:         {
  27:             HttpOnly = true,
  28:             Path = "/"
  29:         };
  30:         response.Headers.AddCookies(new CookieHeaderValue[] { cookie });
  31:     }
  32: }

在我们演示的实例中,应用在DemoController类型上的AuthenticateAttribute特性的 CaptureTokenUri属性(“https://www.artech.com/webapi/account/capturetoken”)指 向定义在AccountController这么一个Controller(ASP.NET MVC的Controller,不是ASP.NET Web API的HttpController)的Action方法CaptureToken,具体定义如下所示。

   1: public class AccountController : Controller
   2: {
   3:     public ActionResult CaptureToken(string requestUri)
   4:     {
   5:         ViewBag.RequestUri = requestUri;
   6:         return View();
   7:     }
   8: }

由于AuthenticateAttribute在调用Windows Live Connect的API获取Access Token所指定的重定向地址具有一个名为“requestUri”的查询字符串,其值正好是调用Web API的地址,该地址会自动绑定到Action方法CaptureToken的requestUri参数上。如果上面的代码片断所示,该方法会将该地址以 ViewBag的形式传递到呈现的View之中。

   1: <html>
   2:     <head>
   3:         <script src="@Url.Content("~/scripts/jquery-1.10.2.js")"></script>
   4:         <script type="text/javascript">
   5:             $(document).ready(function () {
   6:                 var redirectUri = '@ViewBag.RequestUri';
   7:                 if (redirectUrl.indexOf('?') >= 0) {
   8:                     redirectUrl += "&" + location.hash.slice(1)
   9:                 }
  10:                 else {
  11:                     redirectUrl += "?" + location.hash.slice(1)
  12:                 }
  13:                 location.href = redirectUri;
  14:             });
  15:         </script>
  16:     </head>
  17: </html>

上面的代码片断代表Action方法CaptureToken对应View的定义。在该View中,我们从当前地址的Hash(#)部分得到 Access Token,并将其作为查询字符串附加到从ViewBag中得到的资源访问地址上,并通过设置location的href属性的方式携带Access Token对Web API再次发起调用。


谈谈基于OAuth 2.0的第三方认证 [上篇]
谈谈基于OAuth 2.0的第三方认证 [中篇]
谈谈基于OAuth 2.0的第三方认证 [下篇]

[转载]基于Eclipse* 使用安卓* x86 NDK和NDK应用移植实例 | Intel® Developer Zone

mikel阅读(1150)

[转载]基于Eclipse* 使用安卓* x86 NDK和NDK应用移植实例 | Intel® Developer Zone.

目标

面向 Eclipse (ADT) 的 Android 插件现在支持基于 NDK 的应用开发。 其可自动生成项目和构件文件以及代码存根,并可集成到整个 Android 应用开发中(构建原生库、将库复制到项目内的相应 JNI 文件夹、将应用打包以及生成带有 NDK 代码的最终 APK)。 本文将讨论如何配置 Eclipse 以利用该功能,并示范移植 NDK 应用的示例。

配置 Eclipse ADT 插件以配合 NDK 使用

必须先配置 Eclipse ADT 插件指向 NDK 安装路径的位置后,方可使用该新功能。 首先务必更新 Eclipse 中的 ADT 插件以获得最新更改,并从下面的网站下载最新的 NDK:

http://developer.android.com/tools/sdk/ndk/index.html

在 Eclipse 中,打开“窗口”->“首选项”->Android->NDK。 如下所示输入 NDK 安装路径(在此我们假定您已将 NDK 下载至了 Windows* 上的 c:\sw\android-ndk),单击“应用”,然后单击“确定”。


图 1: Eclipse Android ADT 插件中的 NDK 配置

我们现在应当在 Eclipse ADT 中启用和激活与 NDK 相关的功能。 让我们打开其中一个 NDK JNI 示例并进行构建。

打开 Eclipse 中的 hello-jni 示例 NDK 应用,并将 NDK build 支持添加至项目

要在 Eclipse 中构建 NDK JNI 原生示例,我们需要让 Eclipse ADT 了解该示例使用原生/NDK 代码。

让我们先将 HelloJni 示例导入 eclipse。 在 Eclipse 中打开“文件”->“新建”->“项目”-> Android ->“来自现有代码的 Android 项目”。 在下个屏幕中,输入 hello-jni 示例项目(在 NDK 安装文件夹中提供)的路径,并选择“复制到工作区”复选框,再单击“完成”。 使用来自您 NDK 安装文件夹的 HelloJni 路径替代根目录路径。


图 2: 将示例 HelloJni 项目导入 Eclipse

由于 x86 Android NDK 支持 Gingerbread* 及更高的 Android 平台,我们要确保更新应用描述文件中的最小和目标 SDK 配置。 在 Eclipse 中打开 HelloJni 示例下的 AndroidManifest.xml;单击 AndroidManifest 编辑器中的“使用 SDK”并检查最小 SDK 是否设置为 10 (API 10)。


图 3: Eclipse 中的描述文件编辑器

我们现在已准备好向 Eclipse 中的该项目添加 NDK build 支持。 在 Eclipse 中,右键单击 hello-jni 项目 (com.example.hellojni.HelloJni) -> Android 工具 -> Add Native 支持 -> 完成。


图 4: 将 NDK 支持添加至 Eclipse 中的项目

这样将使用和 NDK 构件相关的设置更新项目配置。 在我们重新构建项目时,它将先编译并构建 NDK 代码,将构建的库复制到相应的项目文件夹,然后继续常规的 APK 构建流程。

在模拟器中构建并运行应用

现在继续构建应用,并在 Android 模拟器中运行它。 我们假定已经在基于 HAXM 的 Android 模拟器上运行 Gingerbread x86 AVD。

在 Eclipse 中,右键单击 hello-jni 项目 (com.example.hellojni.HelloJni) -> 运行为 -> Android 应用。 Eclipse 应当自动重建项目并尝试在模拟器上部署/运行 APK。

但是在模拟器运行时我们看到了下面的错误消息,这是因为默认的 NDK 项目设置不支持 x86。


图 5: 在模拟器中运行示例应用

在 Eclipse 中,您可看到 logcat 显示错误“无法加载 hello-jni”


图 6: Logcat 显示无法加载原生库

默认情况下,NDK build 不会自动为 x86 ABI 进行构建。 我们将需要创建构件文件“Application.mk”来明确指定我们的构建目标,或将命令行参数传入 NDK build 命令。

移植应用以使用 x86 ABI 和 NDK build 文件

我们可修改项目以支持 x86 ABI。 在 Eclipse 中,右键单击 hello-jni 项目中的“jni”文件夹,然后选择“新建”->“文件”-> 将“Application.mk”作为文件名输入,然后单击“完成”。


图 7: 将新的 NDK build 文件“Application.mk”添加至示例项目

编辑文件“Application.mk”,如下所示添加行“APP_ABI := all”,然后保存文件。


图 8: 编辑 Application.mk 文件以将“all”架构作为目标

构建应用,验证它是否构建 x86 库,并在 x86 模拟器中测试应用

当前在我们重新构建应用时,其将为 NDK 中所有支持的 ABI 进行构建。 右键单击 Eclipse -> 运行为 -> Android 应用中的“hello-jni”。 Eclipse 将自动调用 NDK build 并生成所有原生库。 您可如下所示在库文件夹下验证它。


图 9: Eclipse 项目资源管理器显示生成的原生库

Eclipse 将在模拟器中部署应用。 此时我们应当会看到应用成功运行,如该截屏中所示:


图 10: 示例应用成功地在 x86 Android 模拟器内运行

我们希望该文章可帮助您了解如何配置 Eclipse ADT 以在 NDK 代码开发中使用它,以及如何使用 NDK build 文件“Application.mk”来为所需的目标 ABI 进行构建。

通知

本文件中包含关于英特尔产品的信息。 本文件不构成对任何知识产权的授权,包括明示的、暗示的,也无论是基于禁止反言的原则或其他。 英特尔不承担任何其他责任。英特尔在此作出免责声明:本文件不构成英特尔关于其产品的使用和/或销售的任何明示或暗示的保证,包括不就其产品的(i)对某 一特定用途的适用性、(ii)适销性以及(iii)对任何专利、版权或其他知识产权的侵害的承担任何责任或作出任何担保。

本文件中包含关于英特尔产品的信息。 本文件不构成对任何知识产权的授权,包括明示的、暗示的,也无论是基于禁止反言的原则或其他。 英特尔不承担任何其他责任。英特尔在此作出免责声明:本文件不构成英特尔关于其产品的使用和/或销售的任何明示或暗示的保证,包括不就其产品的(i)对某 一特定用途的适用性、(ii)适销性以及(iii)对任何专利、版权或其他知识产权的侵害的承担任何责任或作出任何担保。

英特尔有权随时更改产品的规格和描述而毋需发出通知。 设计者不应信赖任何英特产品所不具有的特性,设计者亦不应信赖任何标有“保留权利”或“未定义”说明或特性描述。 对此,英特尔保留将来对其进行定义的权利,同时,英特尔不应为因其日后更改该等说明或特性描述而产生的冲突和不相容承担任何责任。 此处提供的信息可随时改变而毋需通知。 请勿根据本文件提供的信息完成一项产品设计。

本文件所描述的产品可能包含使其与宣称的规格不符的设计缺陷或失误。 这些缺陷或失误已收录于勘误表中,可索取获得。

在发出订单之前,请联系当地的英特尔营业部或分销商以获取最新的产品规格。

索取本文件中或英特尔的其他材料中提的、包含订单号的文件的复印件,可拨打1-800-548-4725,或登陆 http://www.intel.com/design/literature.htm

在性能检测过程中涉及的软件及其性能只有在英特尔微处理器的架构下方能得到优化。 诸如SYSmark和MobileMark等测试均系基于特定计算机系统、硬件、软件、操作系统及功能。 上述任何要素的变动都有可能导致测试结果的变化。 请参考其他信息及性能测试(包括结合其他产品使用时的运行性能)以对目标产品进行全面评估。

对本文件中包含的软件源代码的提供均依据相关软件许可而做出,任何对该等源代码的使用和复制均应按照相关软件许可的条款执行。

英特尔和 Intel 标识是英特尔在美国和/或其他国家的商标。

英特尔公司 © 2013 年版权所有。 所有权保留。

* 其他的名称和品牌可能是其他所有者的资产。

优化声明

[转载]如何让APP变快!-Android开发进阶&经验分享-eoe Android开发者社区_Android开发论坛

mikel阅读(901)

[转载]如何让APP变快!-Android开发进阶&经验分享-eoe Android开发者社区_Android开发论坛.

@亚茹有李 :​做了设计转眼间也4年多了,从2011年由网页设计师转到手机APP设计师;最近的工作不是很忙,就静下心来去阅读学习下互联网的一些发展趋势来弥补自己在这方面的不足(因为工作的时间大部份都是做设计);之前一直以为用户体验方面的工作是有专门的研究人员去做的,与设计人员没有关系,通过这几天的学习,发现做设计的也可以做到用户体验啊,下面我就给大家一一介绍下,大家互相学习 ^_^

一、后台的执行

核心思想:通过在状态栏运行加载的程序的同时,可以使用户可以做其他的事情
举例说明:
Instagram
当点击“赞”的按钮后,按钮的字样立马就变成了“已赞”的状态;其实看图中的红色框的加载图标,它只是在后台运行默默的加载程序;这样做的好处就是让用户不需要一秒钟的等待,避免了当网络不好的时候那个圈一直在转的情况。
发微博平台
例如:我刚发表了一条评论,右边红色框的区域立马显示在评论列表,但实际上我回复的这句评论,对方不能立即收到消息提醒;是因为在看状态栏的​,我发的那句话正在后台程序默默的加载运行呢;这样后台执行它的任务,我可以继续查看其他的评论内容,也不耽误事。

二、在载入前显示内容

核心思想:让用户感觉很快想看到相关产品的信息
举例说明:
Appstore详情页
当用户从列表进入详情页的时候,最上面也就是左边图红色框的区域,毫无加载状态,立马有产品的信息显示,让人感觉点击后内容立马就出来了。

三、充分利用好缓存

核心思想:利用缓存机制,提高页面的打开速度
举例说明:
蝉游记的“游记”和陌陌的“添加微博好友”等页面,都利用了缓存
缓存可以把网络数据保存在本地,下次打开时无需要再次向网络请求,减少流量并减少等待时间。在设计时,可以先显示缓存内容,同时后台到网络上拉取新内容,若有新内容立即换或下次访问时替换。

四、界面先行,网络随后

核心思想:对于一些数据量很小,且失败可能性较小的网络交互,或者是在没有网络的情况下,用户也能够顺畅地使用APP
举例说明:
微信朋友圈的“发图片”和微博的“收藏”功能
朋友圈即使在没有网络的情况下,也可以发布图片,等有了网络之后自动上传刚发布的内容,这一点体验做的很棒(PS:希望微信可以快点更新ios7扁平化)
微博的收藏,当不想收藏的时候,再次点击,帮你可以做其他的事情了,此时会在状态栏自己努力加载。

五、预测用户行为,提前开始任务

核心思想:预测用户下一步操作是什么,提前为用户铺好道路
举例说明:
网易云阅读
当用户从列表页进入详情页,查看一条信息之后往下拉就会看下一条,并且内容已经加载出来,往上拉就会回到上一条,这样很符合大众的习惯;这样做避免看完一条之后点击返回在从列表点击进去看另外一条。
Android的更新提醒
它是在安装包自动下载完成之后提示,避免了让用户等待下载的过程。

六、尽量少的让用户输入,输入时尽量多给出参考

核心思想:移动端的虚拟键盘既有它的优点,又有它的缺点,缺点主要有:
虚拟键盘的空间限制,手指的点击经常造成误按;
输入定位无法反馈(比如说我输入丁丁,结果搜索列表出现丁丁历险记,其实我是想搜索中国好声音的丁丁啊,嘿嘿)
举例说明:
百度地图
例如:我想找万达国际影城,当在输入框搜索「wand」几个字母的时候就会列出所有关于万达方面的内容,很容易就找到​,这样避免用户多输入的状况,很贴心。
妹子原创写得很赞,有些观点来自:《用设计为你的APP提速的6个方法》

[转载]android如何查看cpu的占用率和内存泄漏 - Andye - 博客园

mikel阅读(897)

[转载]android如何查看cpu的占用率和内存泄漏 – Andye – 博客园.

在分析内存优化的过程中,其中一个最重要的是我们如何查看cpu的占用率和内存的占用率呢,这在一定程度上很重要,经过查询资料,研究了一下,暂时了解到大概有以下几种方式,如果哪位高手有更好的办法,或者文中描述有错误,还望高手在下面留言,非常感谢!

 

  一、 通过eclipse,ADT开发工具的DDMS来查看(Heap)

 

在“Devices”窗口中选择模拟器中的一个需要查看的程序,从工具条中选“Update heap”按钮,给这个程序设置上“heap Updates”,然后在Heap视图中点击Cause GC就可以实时显示这个程序的一些内存和cpu的使用情况了。

image

 

image

然后就会出现如下界面:

image

说明:
a) 点击“Cause GC”按钮相当于向虚拟机请求了一次gc操作;
b) 当内存使用信息第一次显示以后,无须再不断的点击“Cause GC”,Heap视图界面会定时刷新,在对应用的不断的操作过程中就可以看到内存使用的变化;
c) 内存使用信息的各项参数根据名称即可知道其意思,在此不再赘述。

 

大致解析如下:

这个就是当前应用的内存占用,allocated 是已经分配的内存 free是空闲内存,

heap size 是虚拟机分配的 不是固定值
heap  size 的最大值跟手机相关的
28LR7GNQIN2CRYR96GB

 

有网友说,

一般看1byte的大部分就是图片占用的

MJ4SIJBIKX3YDC1KDT_H

 

如何判断应用是否有内存泄漏的可能性呢?

如何才能知道我们的程序是否有内存泄漏的可能性呢。这里需要注意一个值:Heap视图中部有一个Type叫做data object,即数据对象,也就是我们的程序中大量存在的类类型的对象。在data object一行中有一列是“Total Size”,其值就是当前进程中所有Java数据对象的内存总量,一般情况下,这个值的大小决定了是否会有内存泄漏。可以这样判断:
a) 不断的操作当前应用,同时注意观察data object的Total Size值;
b) 正常情况下Total Size值都会稳定在一个有限的范围内,也就是说由于程序中的的代码良好,没有造成对象不被垃圾回收的情况,所以说虽然我们不断的操作会不断的生成很多对 象,而在虚拟机不断的进行GC的过程中,这些对象都被回收了,内存占用量会会落到一个稳定的水平;
c) 反之如果代码中存在没有释放对象引用的情况,则data object的Total Size值在每次GC后不会有明显的回落,随着操作次数的增多Total Size的值会越来越大,
直到到达一个上限后导致进程被kill掉。
d) 此处已system_process进程为例,在我的测试环境中system_process进程所占用的内存的data objectTotal Size正常情况下会稳定在2.2~2.8之间,而当其值超过3.55后进程就会被kill。

在如下的位置:

image

 

二、通过linux命令来查看                                                          

常用的命令有

adb shell

ps 是看进程的

top命令是看占用率的

_(RSA4L3`)RKQC{KBG3PFD2

 

3.获取最大内存的方法
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
am.getMemoryClass();
这个是最大内存,如果超过这个内存就OOM了

 

—————————————

内存耗用:VSS/RSS/PSS/USS 的介绍

  • VSS – Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
  • RSS – Resident Set Size 实际使用物理内存(包含共享库占用的内存)
  • PSS – Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)
  • USS – Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)

一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS

Overview

The aim of this post is to provide information that will assist in interpreting memory reports from various tools so the true memory usage for Linux processes and the system can be determined.

Android has a tool called procrank (/system/xbin/procrank), which lists out the memory usage of Linux processes in order from highest to lowest usage. The sizes reported per process are VSS, RSS, PSS, and USS.

For the sake of simplicity in this description, memory will be expressed in terms of pages, rather than bytes. Linux systems like ours manage memory in 4096 byte pages at the lowest level.

VSS (reported as VSZ from ps) is the total accessible address space of a process. This size also includes memory that may not be resident in RAM like mallocs that have been allocated but not written to. VSS is of very little use for determing real memory usage of a process.

RSS is the total memory actually held in RAM for a process. RSS can be misleading, because it reports the total all of the shared libraries that the process uses, even though a shared library is only loaded into memory once regardless of how many processes use it. RSS is not an accurate representation of the memory usage for a single process.

PSS differs from RSS in that it reports the proportional size of its shared libraries, i.e. if three processes all use a shared library that has 30 pages, that library will only contribute 10 pages to the PSS that is reported for each of the three processes. PSS is a very useful number because when the PSS for all processes in the system are summed together, that is a good representation for the total memory usage in the system. When a process is killed, the shared libraries that contributed to its PSS will be proportionally distributed to the PSS totals for the remaining processes still using that library. In this way PSS can be slightly misleading, because when a process is killed, PSS does not accurately represent the memory returned to the overall system.

USS is the total private memory for a process, i.e. that memory that is completely unique to that process. USS is an extremely useful number because it indicates the true incremental cost of running a particular process. When a process is killed, the USS is the total memory that is actually returned to the system. USS is the best number to watch when initially suspicious of memory leaks in a process.

For systems that have Python available, there is also a nice tool called smem that will report memory statistics including all of these categories.

# procrank
procrank
PID      Vss      Rss      Pss      Uss cmdline
481   31536K   30936K   14337K    9956K system_server
475   26128K   26128K   10046K    5992K zygote
526   25108K   25108K    9225K    5384K Android.process.acore
523   22388K   22388K    7166K    3432K com.Android.phone
574   21632K   21632K    6109K    2468K com.android.settings
521   20816K   20816K    6050K    2776K jp.co.omronsoft.openwnn
474    3304K    3304K    1097K     624K /system/bin/mediaserver
37     304K     304K     289K     288K /sbin/adbd
29     720K     720K     261K     212K /system/bin/rild
601     412K     412K     225K     216K procrank
1     204K     204K     185K     184K /init
35     388K     388K     182K     172K /system/bin/qemud
284     384K     384K     160K     148K top
27     376K     376K     148K     136K /system/bin/vold
261     332K     332K     123K     112K logcat
33     396K     396K     105K      80K /system/bin/keystore
32     316K     316K     100K      88K /system/bin/installd
269     328K     328K      95K      72K /system/bin/sh
26     280K     280K      93K      84K /system/bin/servicemanager
45     304K     304K      91K      80K /system/bin/qemu-props
34     324K     324K      91K      68K /system/bin/sh
260     324K     324K      91K      68K /system/bin/sh
600     324K     324K      91K      68K /system/bin/sh
25     308K     308K      88K      68K /system/bin/sh
28     232K     232K      67K      60K /system/bin/Debuggerd
#

[转载]Asp.net实现直接在浏览器预览Word、Excel、PDF、Txt文件(附源码) - 扯 - 博客园

mikel阅读(1282)

[转载]Asp.net实现直接在浏览器预览Word、Excel、PDF、Txt文件(附源码) – 扯 – 博客园.

  1. 功能说明
    1. 输入文件路径,在浏览器输出文件预览信息,经测试360极速(Chrome)、IE9/10、Firefox通过
  2. 分类文件及代码说明 
    1. DemoFiles 存放可测试文件
    2. Default.aspx  启动页
    3. ExcelPreview.cs  Excel预览类
        public static void Priview(System.Web.UI.Page p, string inFilePath, string outDirPath = “”)     {         Microsoft.Office.Interop.Excel.Application excel = null;         Microsoft.Office.Interop.Excel.Workbook xls = null;         excel = new Microsoft.Office.Interop.Excel.Application();         object missing = Type.Missing;         object trueObject = true;         excel.Visible = false;         excel.DisplayAlerts = false;         string randomName = DateTime.Now.Ticks.ToString();  //output fileName         xls = excel.Workbooks.Open(inFilePath, missing, trueObject, missing,                                     missing, missing, missing, missing, missing, missing, missing, missing,                                     missing, missing, missing);         //Save Excel to Html         object format = Microsoft.Office.Interop.Excel.XlFileFormat.xlHtml;         Workbook wsCurrent = xls;//(Workbook)wsEnumerator.Current;         String outputFile = outDirPath + randomName + “.html”;         wsCurrent.SaveAs(outputFile, format, missing, missing, missing,                           missing, XlSaveAsAccessMode.xlNoChange, missing,                           missing, missing, missing, missing);         excel.Quit();         //Open generated Html         Process process = new Process();         process.StartInfo.UseShellExecute = true;         process.StartInfo.FileName = outputFile;         process.Start();     }
    4. PDfPreview.cs   Pdf预览类
         public static void Priview(System.Web.UI.Page p, string inFilePath)
          {         p.Response.ContentType = “Application/pdf”;         string fileName = inFilePath.Substring(inFilePath.LastIndexOf(‘\\’+ 1);         p.Response.AddHeader(“content-disposition”“filename=” + fileName);         p.Response.WriteFile(inFilePath);         p.Response.End();     }
    5. TextFilePreview.cs  文本文件预览类
        public static void Preview(System.Web.UI.Page p, string inFilePath)
          {         string fileName = inFilePath.Substring(inFilePath.LastIndexOf(‘\\’+ 1);         p.Response.ContentType = “text/plain”;         p.Response.ContentEncoding = System.Text.Encoding.UTF8;  //保持和文件的编码格式一致         p.Response.AddHeader(“content-disposition”“filename=” + fileName);         p.Response.WriteFile(inFilePath);         p.Response.End();     }
    6. WordPreview.cs  Word预览类
    7. Readme.txt  说明了基本功能及引用Com组件的方法(首先需要安装office),需引入的组件包括
      1. Microsoft Word 15.0
      2. Microsoft Excel 15.0
  3. 预览效果
    1、Word

    2、Excel

    3、Pdf

    4、Txt
  4. 未解决的问题
    1. Pdf、txt文件只能在当前页显示,并导致后退键无效,请各位帮忙解决此两种文件和doc、xls一样在新的tab中打开
  5. 源码下载

[转载]HeaderViewListAdapter - Robin Hu的专栏 - 博客频道 - CSDN.NET

mikel阅读(999)

[转载]HeaderViewListAdapter – Robin Hu的专栏 – 博客频道 – CSDN.NET.

public class
HeaderViewListAdapter
    extends Object
        implements Filterable WrapperListAdapter
Class Overview
ListAdapter used when a ListView has header views. This ListAdapter wraps another one and
also keeps track of the header views and their associated data objects.
This is intended as a base class; you will probably not need to use this class directly in your own code.

HeaderViewListAdapter的主要作用就是在ListAdapter基础上封装和升级,为其提供了添加列表头列表尾的功能。
该类一般不直接使用,它的主要目的是为我们提供一个对包含列表头和列表尾的列表进行适配的一个基类。
构造函数如下:
Public Constructors
HeaderViewListAdapter(ArrayList<ListView.FixedViewInfo> headerViewInfos, ArrayList<ListView.FixedViewInfo> footerViewInfos, ListAdapter adapter)
参数
headerViewInfos 用于提供列表头
footerViewInfos   用于提供列表尾
adapter       用于为列表正文进行适配
另外,ListView.FixedViewInfo其实很简单,它就是对一个View及其信息的封装。
Fields
public Object data The data backing the view.
public boolean isSelectable true if the fixed view should be selectable in the list
public View view The view to add to the list
Public Constructors
ListView.FixedViewInfo()
HeaderViewListAdapter源码如下
HeaderViewListAdapter.java文件

/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * 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.
 */

package android.widget;

import android.database.DataSetObserver;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;

/**
 * ListAdapter used when a ListView has header views. This ListAdapter
 * wraps another one and also keeps track of the header views and their
 * associated data objects.
 *<p>This is intended as a base class; you will probably not need to
 * use this class directly in your own code.
 */
public class HeaderViewListAdapter implements WrapperListAdapter, Filterable {

    private final ListAdapter mAdapter;

    // These two ArrayList are assumed to NOT be null.
    // They are indeed created when declared in ListView and then shared.
    ArrayList<ListView.FixedViewInfo> mHeaderViewInfos;
    ArrayList<ListView.FixedViewInfo> mFooterViewInfos;

    // Used as a placeholder in case the provided info views are indeed null.
    // Currently only used by some CTS tests, which may be removed.
    static final ArrayList<ListView.FixedViewInfo> EMPTY_INFO_LIST =
        new ArrayList<ListView.FixedViewInfo>();

    boolean mAreAllFixedViewsSelectable;

    private final boolean mIsFilterable;

    public HeaderViewListAdapter(ArrayList<ListView.FixedViewInfo> headerViewInfos,
                                 ArrayList<ListView.FixedViewInfo> footerViewInfos,
                                 ListAdapter adapter) {
        mAdapter = adapter;
        mIsFilterable = adapter instanceof Filterable;

        if (headerViewInfos == null) {
            mHeaderViewInfos = EMPTY_INFO_LIST;
        } else {
            mHeaderViewInfos = headerViewInfos;
        }

        if (footerViewInfos == null) {
            mFooterViewInfos = EMPTY_INFO_LIST;
        } else {
            mFooterViewInfos = footerViewInfos;
        }

        mAreAllFixedViewsSelectable =
                areAllListInfosSelectable(mHeaderViewInfos)
                && areAllListInfosSelectable(mFooterViewInfos);
    }

    public int getHeadersCount() {
        return mHeaderViewInfos.size();
    }

    public int getFootersCount() {
        return mFooterViewInfos.size();
    }

    public boolean isEmpty() {
        return mAdapter == null || mAdapter.isEmpty();
    }

    private boolean areAllListInfosSelectable(ArrayList<ListView.FixedViewInfo> infos) {
        if (infos != null) {
            for (ListView.FixedViewInfo info : infos) {
                if (!info.isSelectable) {
                    return false;
                }
            }
        }
        return true;
    }

    public boolean removeHeader(View v) {
        for (int i = 0; i < mHeaderViewInfos.size(); i++) {
            ListView.FixedViewInfo info = mHeaderViewInfos.get(i);
            if (info.view == v) {
                mHeaderViewInfos.remove(i);

                mAreAllFixedViewsSelectable =
                        areAllListInfosSelectable(mHeaderViewInfos)
                        && areAllListInfosSelectable(mFooterViewInfos);

                return true;
            }
        }

        return false;
    }

    public boolean removeFooter(View v) {
        for (int i = 0; i < mFooterViewInfos.size(); i++) {
            ListView.FixedViewInfo info = mFooterViewInfos.get(i);
            if (info.view == v) {
                mFooterViewInfos.remove(i);

                mAreAllFixedViewsSelectable =
                        areAllListInfosSelectable(mHeaderViewInfos)
                        && areAllListInfosSelectable(mFooterViewInfos);

                return true;
            }
        }

        return false;
    }

    public int getCount() {
        if (mAdapter != null) {
            return getFootersCount() + getHeadersCount() + mAdapter.getCount();
        } else {
            return getFootersCount() + getHeadersCount();
        }
    }

    public boolean areAllItemsEnabled() {
        if (mAdapter != null) {
            return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled();
        } else {
            return true;
        }
    }

    public boolean isEnabled(int position) {
        // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
        int numHeaders = getHeadersCount();
        if (position < numHeaders) {
            return mHeaderViewInfos.get(position).isSelectable;
        }

        // Adapter
        final int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getCount();
            if (adjPosition < adapterCount) {
                return mAdapter.isEnabled(adjPosition);
            }
        }

        // Footer (off-limits positions will throw an ArrayIndexOutOfBoundsException)
        return mFooterViewInfos.get(adjPosition - adapterCount).isSelectable;
    }

    public Object getItem(int position) {
        // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
        int numHeaders = getHeadersCount();
        if (position < numHeaders) {
            return mHeaderViewInfos.get(position).data;
        }

        // Adapter
        final int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getCount();
            if (adjPosition < adapterCount) {
                return mAdapter.getItem(adjPosition);
            }
        }

        // Footer (off-limits positions will throw an ArrayIndexOutOfBoundsException)
        return mFooterViewInfos.get(adjPosition - adapterCount).data;
    }

    public long getItemId(int position) {
        int numHeaders = getHeadersCount();
        if (mAdapter != null && position >= numHeaders) {
            int adjPosition = position - numHeaders;
            int adapterCount = mAdapter.getCount();
            if (adjPosition < adapterCount) {
                return mAdapter.getItemId(adjPosition);
            }
        }
        return -1;
    }

    public boolean hasStableIds() {
        if (mAdapter != null) {
            return mAdapter.hasStableIds();
        }
        return false;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
        int numHeaders = getHeadersCount();
        if (position < numHeaders) {
            return mHeaderViewInfos.get(position).view;
        }

        // Adapter
        final int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getCount();
            if (adjPosition < adapterCount) {
                return mAdapter.getView(adjPosition, convertView, parent);
            }
        }

        // Footer (off-limits positions will throw an ArrayIndexOutOfBoundsException)
        return mFooterViewInfos.get(adjPosition - adapterCount).view;
    }

    public int getItemViewType(int position) {
        int numHeaders = getHeadersCount();
        if (mAdapter != null && position >= numHeaders) {
            int adjPosition = position - numHeaders;
            int adapterCount = mAdapter.getCount();
            if (adjPosition < adapterCount) {
                return mAdapter.getItemViewType(adjPosition);
            }
        }

        return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
    }

    public int getViewTypeCount() {
        if (mAdapter != null) {
            return mAdapter.getViewTypeCount();
        }
        return 1;
    }

    public void registerDataSetObserver(DataSetObserver observer) {
        if (mAdapter != null) {
            mAdapter.registerDataSetObserver(observer);
        }
    }

    public void unregisterDataSetObserver(DataSetObserver observer) {
        if (mAdapter != null) {
            mAdapter.unregisterDataSetObserver(observer);
        }
    }

    public Filter getFilter() {
        if (mIsFilterable) {
            return ((Filterable) mAdapter).getFilter();
        }
        return null;
    }
    
    public ListAdapter getWrappedAdapter() {
        return mAdapter;
    }
}

[转载]最新cocos2d-x 3.0游戏开发博客教学 小游戏[HoldTail]持续更新.....-Cocos2d-x游戏开发-eoe Android开发者社区_Android开发论坛

mikel阅读(957)

[转载]最新cocos2d-x 3.0游戏开发博客教学 小游戏[HoldTail]持续更新…..-Cocos2d-x游戏开发-eoe Android开发者社区_Android开发论坛.

cocos2d-x 3.0游戏开发博客教学

大 家好,很高兴成为eoe游戏板块的版主,做为一个移动开发者我们一定要与时俱进,cocos2d最近两年来一直非常的火爆,也给一些个人开发者带来了丰厚 的收入,很多的游戏都是用cocos2d引擎开发出来的,我从去年开始就加入了cocos2d 的怀抱,刚开始学习cocos2d的时候觉得很难,什么是精灵,什么是场景,什么是层,等等,很多很多新鲜的概念,现在回过头来想一想,其实这些都是一些 很简单的东西,大家回想一下当初学习java 学习Android 学习ios的时候觉得难吗?现在从事了这方面的开发,还觉得以前学的东西难吗?如果那一天别人问你对象是什么你能回答上来么?言归正传学习cocs2d也 是一样的,首先要理解一些基本的概念,然后跟着做一些基本的小游戏,今天开始我就给大家带来一个小游戏的教学。等这个小游戏的博客写完后,我就会带大家了 解一些基本的概念,不过这里要特别的注意在cocos2d-x2.2以前的版本跟现在的版本有很大的差别,以前的教程如果用新SDK的话,根本用不了,全部都修改了

这个游戏是我一年前就写好的,但是没有发布到市场上面去,很多童鞋可能会为,为什么不发布到市场上面去呢?我觉得这个游戏还不是很完善,还不足以供人娱乐,但是如果当一个教学的例子我还是觉得绰绰有余的哈,先带大家来看看游戏运行的效果

现在完成了一些基本的东西,然后会进行实时的更新,

无论干什么首先大家一定要搭建好开发环境下面就是搭建开发环境的地址

    cocos2d-x 3.0游戏开发 001.[HoldTail]环境的搭建以及项目建

当大家把环境搭建完成后就可以正式的开始写游戏的代码了,大有什么问题先跟这教程写哈,只有在做的过程中遇到问题,你去解决它这样下来印象才是最深刻的。

    cocos2d-x 3.0游戏开发 002.[HoldTail]主界面场景制作

当主场景自制完成后,大家是不是很有成就感,觉得不可思议,当大家把第三章学会了,自热而然就学会了场景的跳转,这样一来就无声无息的理解了场景的概念,当大家以后学习理论知识的时候,就相对来说很简单了。

    cocos2d-x 3.0游戏开发 003.[HoldTail]游戏世界以及背景画面

通过上门的教程你不知不觉的学会了场景的运动,而且是多个场景一起运动,场景能够动后,我们加入我们游戏的主角,大家跟着下面的博客教程做完成后,是不是更加有成就感了,能更加的提高你学习cocos2d的兴趣,而且我们学习的是最新的cocos2d版本哦。

    cocos2d-x 3.0游戏开发 004.[HoldTail]主角的上下飞行跟移动

在以后的教程中还有主角的 武器系统是如何练成的,金币系统是如何做成的,当然还有最主要的敌人系统是如何做成的,请大家敬请期待吧,我们一起共同努力加油哦,本人不是什么游戏开发 高富帅,只是一个愿意分享给大家的人,有什么不好的地方,请大家多多包涵,也请大家多多指正,在技术上面有什么不懂的,给我博客留言或者回帖,我们一起讨 论。