Activiti6---学习初步总结

1. 总结

  • 如果一个业务需要多方面角色进行处理的话,那么我们最好就是用到工作流框架。因为如果其中一个环节的需求发生了变化的话,我们没有用到工作流。那就需要修改很多的代码,十分麻烦。

  • Activiti工作流框架快速入门:

    • 定义工作流,使用插件来把我们的流程图画出来,这个流程图就是我们定义的工作流。
    • 工作流引擎是工作流的核心,能够让我们定义出来的工作流部署起来。
    • 由于我们使用工作流的时候有很多的数据产生,因此Activiti是将数据保存到数据库表中的,这些数据库表由activiti创建,由activiti维护。
    • 部署完的工作流是需要手动去执行该工作流的。
    • 根据由谁处理当前任务,我们就可以查询出具体的任务信息。
    • 根据任务id,我们就可以执行任务了。
  • 流程定义涉及到了四张数据库表

    • 我们可以通过API把我们的流程定义图读取出来
    • 可以根据查询最新版本的流程定义
    • 删除流程定义
    • 部署流程定义的时候也可以是ZIP文件
  • 流程在运行中,设计到两个对象,四张数据库表:

    • 流程实例
    • 流程任务
    • 流程实例可以有多个,流程对象只能有一个。
      • 如果流程没有分支的话,那么流程实例就等于流程对象。
    • 基于这两个对象,就可做很多事了
      • 获取流程实例和任务的历史信息
      • 判断流程实例是否为空来判断流程是否结束了
      • 查看正在运行服务的详细信息
      • 通过流程实例来开启流程
  • 流程变量:它涉及到了两张表,流程变量实际上就是我们的条件

    • 流程变量的作用域只在流程实例中。
    • 我们可以在流程开始的时候设置流程变量,在任务完成的时候设置流程变量
    • 运行时服务和流程任务都可以设置流程变量
  • 通过连线我们可以再其中设置条件,根据不同的条件流程走不同的分支

  • 如果没有设置默认的条件,当条件不吻合的时候,那么流程就走不下去了,因此需要排他网关来设置一条默认的路径。

2. 常用方法汇总

sequenceFlow id=‘test’ sourceRef=’’ targetRef=’’
serviceTask id=‘serviceTaskId’ activiti:expression=’#{beanName.method}’
自动调用beanName对应的bean的method方法

这个bean还需要在ProcessEngineConfiguration中进行配置
–部署一个流程图
repositoryService.createDeploy().addClasspathResource(resourceName).deploy();
–开启一个流程
runtimeService.startProcessInstanceByKey(“processId”);
–获得一个流程实例也是用runtimeService
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(findTaskById(taskId).getProcessInstanceId())
.singleResult();
– Task对象可以转化成TaskEntity
TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery().taskId(taskId).singleResult();
– 节点ID
String activityId = taskEntity.getTaskDefinitionKey();
– 流程定义ID
String definitionId = taskEntity.getProcessDefinitionId();
– 取得 流程定义对象
ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
.getDeployedProcessDefinition(taskEntity.getProcessDefinitionId());
– 获得所有节点定义
List activityList = processDefinition.getActivities();
– 根据节点ID,获取对应的活动节点
ActivityImpl activityImpl = ((ProcessDefinitionImpl) processDefinition).findActivity(activityId);
– 获得某个节点流出的顺序流信
List pvmTransitionList = activityImpl.getOutgoingTransitions();
–获得一个任务
Task task = taskService.createTaskQuery().singleResult();
–获得表单信息
StartFormData sfd = FormService.getStartFormData(processDefinitionId) ;
TaskFormData tfd = FormService.getTaskFormData(taskId) ;
–可以获得配置好的表单地址
String formKey = tfd.getFormKey();
– 查询taskId节点发起的任务集合
List tasks = taskService.createTaskQuery().parentTaskId(taskId)
.taskDescription(“jointProcess”).list();
–参数集合 并完成任务
HashMap<String, Object> variables = new HashMap<String, Object>();
taskService.complete(taskId,variables);
–查询流程实例中已经完成的任务集合 按完成时间降序排序
List historicActivityInstances = historyService
.createHistoricActivityInstanceQuery().activityType(“userTask”)
.processInstanceId(processInstance.getId())
.activityId(activityId).finished()
.orderByHistoricActivityInstanceEndTime().desc().list();

顺序流 sequenceFlow sourceRef targetRef

分支 排他分支 带X的菱形 不带X的菱形 都可以表示排他分支

都是有两个并行分支 分别表示拆分和合并 会签就是并行分支 ${input==1} input表示从上一个节点所传的参数

assignee是直接分配给某人 而 <userTask id==’’ activiti:candidateUsers=‘user1,user2’> 是指定user1和user2为候选人两个都可以完成任务
activiti:candidateGroups指定任务候选组
同样可以在任务上面添加任务监听器 <activiti:taskListener event=‘create’ class=‘XXX’>

public class XXX implements TaskListener{
public void notify(DelegateTask delegateTask){
delegateTask.setAssignee("");
delegateTask.setCandidateUsers();
}
}

java服务任务

serviceTask (用来调用外部类) 必须实现JavaDelegate或ActivitiBehavior类
<serviceTask id=’’ name=’’ activiti:class=“xx.xx.xx.XXX” 或 activiti:delegateExpression="${beanName}">

exclusiveGateway
parallelGateway

指定人和候选人有区别 指定就是叫确定某个人

public class MyBehavior implements ActivityBehavior{
public void execute(ActivitiExecution execution){
String varValue = (String)execution.getVariable(“var”);
PvmTransition transition = execution.getActivity().findOutgoingTransition(“name”);
– 执行某个顺序流
execution.take(transition);
}
}

任务监听器只能适用与用户任务(userTask) 的标签中
<activiti:taskListener event=’’ class=’’>
event属性必须要有 选项有 create assignment complete

子流程的所有信息都必须在subProcess标签中实现
可以通过callActiviti标签把子流程嵌入到相应的流程当中

表单数据(FormData) 分别包括 StartFormData sfd = FormService.getStartFormData()
和TaskFormData tfd = FormService.getTaskFormData(taskid);

外部表单可以通过<userTask activiti:formKey=‘xxxx.html’ 来定义
可以通过TaskFormData.getFormKey()方法来获取

历史相关对象

HistoricProcessInstance --流程实例历史信息
HistoricActivitiInstance – 节点实例历史信息(包括任务实例)
HistoricTaskInstance --任务实例历史信息
HistoricDetails --流程中所使用的变量信息

历史信息配置


3. 重要知识点

Activiti提供了多种创建流程引擎的方式,ProcessEngineConfiguration.buildProcessEngine(),或者ProcessEngines.init()方法来创建ProcessEngine实例。

一个ProcessEngine实例代表一个流程引擎。

所有创建ProcessEngine实例均被注册到ProcessEngines中,这里所说的注册,实际上是ProcessEngines类中维护一个Map对象,该对象的key为ProcessEngine实例的名称,value为ProcessEngine的实例。

1. Service

7个api,每个api,每个方法都有一个命令command,把所有的操作放在命令里,所有的命令操作都在一个事物里。简单清晰,易于维护、扩展。命令需要提供上下文的东西,通过拦截器来做。

当调用引擎Service接口时,实际上调用的是不同的Command,但是接口并不是直接调用的命令,而是把命令交给CommandExecutor统一执行,因为通过它可以在命令执行过程中执行若干拦截器。通过CommandContextInterceptor拦截器可以为所有的Command准备好命令上下文CommandContext对象,以便在Command实现类中获取到引擎配置对象、会话对象等。

每个command都可以配置拦截器:前置拦截器、后置拦截器、默认拦截器。其中默认拦截器有LogInterceptor、RetryInterceptor。RetryInterceptor乐观锁重试,缓解乐观锁问题,会做超时等待、重试。

CommandContext通过ThreadLocal实现,和线程绑定,是线程安全的。

2. BPMN

流程驱动、业务流程根据BPMN文件进行解析,去事件处理和流程转换。

Process类时Activiti中用来描述BPMN2.0元素的JavaBean,Process表示一个流程。

流程部署之后,会把流程对象缓存到内存中,简单的一个key-value的map对象缓存

xml可以和bean互转,可以xml做持久化,bean,流程对象缓存到引擎中。

BPMN:Business Process Modeling and Notation业务流程建模与标注,是workflow的建模语言标准之一。定义了业务流程图的一套标准语言,比如活动(任务和子流程)是矩形,条件是菱形。

资源以二进制流的方式保存,通过api获取流对象,转换为需要的资源

3. PVM

底层由PVM来驱动实现

PVM把业务流程的特性抽取出来,怎么去驱动。

PVM抽象了业务驱动模型

JVM抽象了虚拟机的特性

PVM把业务流程的特性抽取出来,驱动流程的流转。

4. 事件处理器

在工作流引擎中,有很多事件,通过事件处理器处理:

流程内事件:会做监听类的。在task上配置,在complete上配置,做触发。内部有一个注入。

全局事件:引擎层面的。比如实体创建、流程引擎启动,流程挂起、激活。在引擎配置文件中配置注入事件处理器。

引擎内部先去触发事件,然后找对应的处理器。处理器会有一个注入。提供对应的处理数据。

4. Activiti状态

Activity有三种状态:active/running、paused、stopped。

1、active/running状态,在当前屏幕时,即用户可见的Activity,位于当前Task的栈顶。

2、paused状态,Activity失去焦点但对用户依然可见。也就是说在它上面有另外一个非全屏或者透明的Activity,并成为了当前的焦点。它还没有被其他的Activity完全遮盖住,而paused的Activity依然是alive状态的,它保留着所有的状态和成员信息并连接至窗口管理器,但当系统处于极低内存的情况下,仍然可以杀死这个Activity。

3、stopped 状态,当Activity完全被另一个Activity覆盖时,它仍然保留所有的状态和成员信息。但它不再被用户可见,所以它的窗口将被隐藏,当其它地方需要内存,则系统经常会杀死这个Activity。

当Activity是paused或者stopped状态时,系统可以通过要求它结束(调用它的finish()方法)或直接杀死它的进程来将它驱出内存。当它再次为用户可见的时候,它只能完全重新启动并恢复至以前的状态。

启动并恢复至以前的状态。

为什么会有这么麻烦的状态变化

1、手机屏幕大小有限,只能看到有限的界面数量,用户需要通过Back键或Home键或其他方式切换Activity。

2、在有手机来电时,系统会优先切换显示电话接听界面。

由于上述的一些原因,确实存在界面随时可能发生切换的情况,最佳方式当然不是每次都直接销毁之前的界面,很多时候我们也需要在回到之前界面后恢复之前的状态。我们需要了解这些状态,当然还包括要掌握Activity的“生命周期”等知识,这样就能够在Activity发生变化时,知其所以然,并能够根据需求采取一定的措施,例如切换界面时,保存当前的界面信息,即使其被意外杀死也能恢复之前状态。

5. Activiti生命周期

在这里插入图片描述

当一个Activity从这个状态转变到另一个状态时,它被以下列protected方法所通知:

public class Activity extends ApplicationContext
{
protected void onCreate(Bundle savedInstanceState);

     protected void onStart();

     protected void onRestart(); 

     protected void onResume();

    protected void onPause();  

    protected void onStop();  

    protected void onDestroy();  

}
  1、onCreate – 当Activity第一次创建时会被调用(未被销毁前不会被再次调用)。在这个方法中可以做一些初始化工作,例如对创建新的View、获取并设置View的一些属性、绑定列表的数据等等。如果能捕获到Activity 状态的话,这个方法传递进来的Bundle对象将存放了Activity当前的状态。调用该方法后一般会调用onStart()方法。此时Activity不可见,所以并不是所有的初始化工作都适合在这里做,例如一些动画的开始。

2、onRestart – 在Activity被停止后重新启动时会调用该方法。其后续会调用onStart方法。

3、onStart – 当Activity对于用户可见前即调用这个方法。如果Activity回到前台则接着调用onResume() ,如果Activity隐藏则调用onStop()。此时Activity可能可见,但即使可见,也无法进行交互操作。

4、onResume – 在Activity开始与用户交互前调用该方法。在这时该Activity 处于Activity 栈的顶部,并且接受用户的输入。其后续会调用 onPause() 方法。此时Activity也不一定可见,需要通过onWindowFocusChanged(boolean)来确定Activity是否可见。

5、onPause – 在系统开始准备恢复其它Activity时会调用该方法。这个方法中通常用来提交一些还没保存的更改到持久数据中,停止一些动画或其它一些耗CPU的操作等等。无论在该方法里面进行任何操作,都需要较快速完成,因为如果它不返回的话,下一个Activity 将无法恢复出来。如果Activity 返回到前台将会调用onResume(),如果Activity变得对用户不可见了将会调用onStop() 。

6、onStop – 在Activity对用户不可见时将调用该方法。可能会因为当前Activity 正在被销毁,或另一个Activity(已经存在的Activity 或新的Activity )已经恢复了正准备覆盖它,而调用该方法。如果Activity 正准备返回与用户交互时后续会调用onRestart ,如果Activity正在被释放则会调用onDestroy 。

7、onDestroy – 在Activity被销毁前会调用该方法。这是Activity能接收到的最后一个调用。可能会因为有人调用了finish方法使得当前Activity正在关闭,或系统为了保护内存临时释放这个Activity的实例,而调用该方法。你可以用isFinishing方法来区分这两种不同的情况。

要注意的是,onPause(),onStop(),onDestory()这3个方法都可能会因为系统内存不足而被系统直接kill。而平常从一个Activity转向/回到另一个Activity时,当新Activity是full screen(弹出窗口,例如AlertDialog是不算的)的时候就会调用前一个Activity的onPause(),然后调用onStop(),而无论onPause或者onStop,都有可能被kill,所以一般在onPause时就应该保存需要持久化的一些数据(例如用户编辑的信息)。

onCreate、onStart、onResume都不是Activity准确能够交互的点,真正的是onWindowFocusChaned()函数被执行时。

6. Android的进程

android根据其重要性在内存不足的时候移去重要性最低的进程。重要性由高到低为:

1、活动/前台进程

指那些有组件正和用户进行交互的应用程序进程。他们都是android尝试通过回收资源来使其保持响应的进程。这些进程的数量非常少,只用到最后的关头才会终止这些进

程。

活动进程包括:

处于“活动”状态的Activity,也就是说,它们位于前台并对用户事件进行响应。
      正在执行onReceive事件处理程序的广播接收器。
      正在执行onCreate()、onStart()或onDestory事件处理程序的服务。
      正在运行,且已被标记为前台运行的服务。
  2、可见进程
    可见,但是非活动的进程是指那些驻留可见活动的进程。顾名思义,可见的活动能被用户看到,但是他们并不是在前台运行或者能对用户事件做出响应,比如,当一个活动Activity被部分遮挡的时候(被一个非全屏或者半透明状态的Activity遮挡时)就会出现这类情况。这类进程的数量很少,只有在资源极度缺乏的环境下,为保证活动进程的继续执行,才会终止这些进程。

3、服务进程

已经启动的服务进程。服务支持在没有可见界面的情况下,仍然能继续不间断地进行处理。因为后台服务没有直接和用户进行交互,所以它们的优先级要比可见进程低一些。但是他们仍然被认为是前台进程。除非活动或者可见进程需要资源,否则不会终止它们。

4、后台进程
不可见,并且没有任何正在运行的服务的活动的进程,通常会有大量的后台进程。这样的程序拥有一个用户不可见的Activity。这样的程序在系统内存不足时,按照LRU的顺序被结束。

运行着一个对用户不可见的Activity(调用过onStop() 方法)。这些进程对用户体验没有直接的影响,可以在服务进程、可见进程、前台进程需要内存的时候回收。通常,系统中会有很多不可见进程在运行,他们被保存在LRU (least recently used) 列表中,以便内存不足的时候被第一时间回收。如果一个Activity正确的执行了它的生命周期,关闭这个进程对于用户体验没有太大的影响。
  5、空进程
    这样的进程不包含任何活动的程序部件。系统可能随时关闭这类进程。未运行任何程序组件。运行这些进程的唯一原因是作为一个缓存,缩短下次程序需要重新使用的启动时间。系统经常中止这些进程,这样可以调节程序缓存和系统缓存的平衡。

Android对进程的重要性评级的时候,选取它最高的级别。另外,当被另外的一个进程依赖的时候,某个进程的级别可能会增高。一个为其他进程服务的进程永远不会比被服务的进程重要级低。因为服务进程比后台Activity进程重要级高,因此一个要进行耗时工作的Activity最好启动一个Service来做这个工作,而不是开启一个子进程――特别是这个操作需要的时间比Activity存在的时间还要长的时候。例如,在后台播放音乐,向网上上传摄像头拍到的图片,使用Service可以使进程最少获取到“服务进程”级别的重要级,而不用考虑Activity目前是什么状态。broadcast receivers做费时的工作的时候,也应该启用一个服务而不是开一个线程。

7. Activity状态的保存

protected void onSaveInstanceState (Bundle outState)
protected void onRestoreInstanceState (Bundle savedInstanceState)
  onSaveInstanceState() 和 onRestoreInstanceState() 并不是生命周期方法。它们并不是总会被调用。例如通过Back造成的销毁Activity时,它就不会被调用。应该只用它来为Activity保存一些临时的状态,而不能用来保存持久性的数据。而是应该用onPause()来达到这个目的。

onPause()方法是在系统结束应用程序前调用的最后一个安全的方法。无法保证onStop和onDestroy会被调用,所以不能依赖这两个方法来实现关键逻辑。

六、Activity的加载模式

Activity加载模式(通过在AndroidManifest.xml文件中activity元素的android:launcherMode属性设置) 。请参考文章结尾附带的demo体验Activity的加载模式。

1、standard(默认)
    测试方式:MainActivity(standard),SecondActivity(standard)
    每次都是创建了新的Activity实例。

2、singleTop
    测试方式:MainActivity(singTop),MainActivity自己intent自己
    测试方式:MainActivity(singleTop),SecondActivity(standard)
    和standard一样是发送新的实例,但singleTop要求如果创建intent的时候栈顶已经有要创建的Activity实例,则将intent发送给该实例。例如给MainActivity设置singleTop,然后按钮也是intent自己,那么会发现一直都是MainActivity当前这个实例,因为他一直在栈顶,所以不会创建新的。

如果用A打开B,而B再打开A,如此循环,如果A是设置了singleTop,B是默认,则每次A都是新的,因为每次要打开A时,栈顶都不是A,就要创建新的A实例(和都是 standard没啥区别了)。
    此种模式的应用场景,目前想到就是为了解决自己在栈顶时,自己给自己发送intent可以不创建新的实例。实际中应用的也较少。

3、singleTask
    测试方式:MainActivity(singleTask),SecondActivity(standard)
    只创建一个实例。如果发现有对应的Activity实例,则是此Activity实例之上的其他Activity实例全部出栈,使此Activity实例成为栈顶对象,显示出来。
  4、singleInstance
    这个模式下的Activity单独在一个task栈中。这个栈只有一个Activity。目的是为了多个Task共享一个Activity。

七、Activity与View的生命周期

View和Activity一样也有自己的生命周期,具体在View的周期中包含哪些通知函数,参见http://developer.android.com/reference/android/view/View.html。其中onMeasure、onLayout、onDraw最重要,而onMeasure最难理解(之后会单独讨论这个函数及其如何使用)。

8. Activity的注意事项

1、onConfigurationChanged

1)不设置Activity的android:conChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次。
    2)设置Activity的android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次。
    3)设置Activity的android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行重写的onConfigurationChanged方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值