技术文章

当前位置:首页>帮助手册>技术文章

O2OA安卓客户端源码说明

时间:2022-03-02   

android源码主要开发语言是kotlin。


目录介绍


导入项目代码后,在Android Studio上面看到根目录有两个模块:

手机源码开发目录.png

app是项目主目录,o2_auth_sdk是把所有o2oa后端的api请求都封装到了这个模块中。o2_auth_sdk模块因为开发需要目前没有直接引入到app中,而是打成aar包放到了app模块的libs目录下引入的。为了开发方便客户可以修改app的build.gradle文件中引入外部包的地方,把这个o2_auth_sdk以模块方式引入,这样修改了这个模块中的代码就能实时生效,如下图:


o2_auth_sdk


这个模块内容很少,前面说了主要是封装了o2oa后台的一些api。所以主要关心下,api包下的代码和一个最外层的O2SDKManager类。O2SDKManager是一个全局对象,里面保存了一些比如当前用户信息啥的全局用到的对象。


1574668413377-7d39fc16-cc0f-4920-954b-2c965ed44944.png


app


主模块如下:

1574668413291-bbb695a3-c7eb-487a-a55f-cffb1ef789a9.png


O2App这个类是Android程序入口类Application。app这个包目录下的都是显示层,包含了所有的Activity和Fragment,按照功能模块来划分目录。中间那些包是一些工具类、服务类、自定义View等等内容。


在app->o2目录下

    程序的主显示Activity是LaunchAcitivty,这里执行一些比如连接服务器,自动登录等过程。

    然后进入MainActivity,这个就是app的主界面,这个界面里面包含了IndexFragmentNewsFragmentNewContactFragmentAppFragmentSettingsFragment,分别就是主界面中看到的首页、消息页、通讯录页、应用页和设置页。


1574685520282-92543412-3332-4642-a63e-5080c6c426e0.png

代码结构以及如何进行功能开发的说明


整个项目主要是用MVP模式进行开发的,打开某一个模块,把里面的页面目录打开就会看到如下结构:

1574668413400-b887b5aa-30b1-4594-8b38-c47437ddf054.png



都是由一个Activity、一个Contract、一个Presenter组成。Activity就不用说了就是显示,Contract是定义接口的,Presenter是写业务方法,比如后端请求数据处理等。


Contract样例:


object PersonContract {
    interface View : BaseView {
        //把查询结果反馈给View
        fun loadPersonInfo(personInfo: PersonJson)
        fun loadPersonInfoFail()
    }

    interface Presenter : BasePresenter<View> {
      //后台请求个人信息
      fun loadPersonInfo(name:String)
    }
}


Presenter样例:


//实现 PersonContract.Presenter
class PersonPresenter : BasePresenterImpl<PersonContract.View>(), PersonContract.Presenter {
    //后台请求个人信息
    override fun loadPersonInfo(name: String) {
        //人员组织API服务
        getOrganizationAssembleControlApi(mView?.getContext())?.let { service ->
           //人员信息请求
           service.person(name)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(ResponseHandler<PersonJson>({ person ->
                        //请求结果返回给view
                        mView?.loadPersonInfo(person) }),
                     ExceptionHandler(mView?.getContext(), { e -> 
												//错误处理  
                        mView?.loadPersonInfoFail() 
                                                                  }))
        }
    }

}


Activity样例代码:


//实现PersonContract.View
class PersonActivity : BaseMVPActivity<PersonContract.View, PersonContract.Presenter>(), PersonContract.View {
    //presenter的实现
    override var mPresenter: PersonContract.Presenter = PersonPresenter()
    //activity的view的layout文件
    override fun layoutResId(): Int = R.layout.activity_person_info

    var personId = ""
   //这里初始化View
    override fun afterSetContentView(savedInstanceState: Bundle?) {
        personId = intent.extras?.getString(PERSON_NAME_KEY, "")?:""
        if (TextUtils.isEmpty(personId)) {
            XToast.toastShort(this, "没有传入人员帐号,无法获取人员信息!")
            finish()
            return
        }

        showLoadingDialog()
        //请求人员信息
        mPresenter.loadPersonInfo(personId)
    }
    //返回成功的实现
    override fun loadPersonInfo(personInfo: PersonJson) {
        hideLoadingDialog()
      	tv_person_mobile.text = personInfo.mobile
        tv_person_email.text = personInfo.mail
      .....
  	}
    //返回失败的实现
    override fun loadPersonInfoFail() {
        hideLoadingDialog()
    }
  
}


整体开发结构基本就是上面样例代码的样子,首先Contract定义两个接口对象 View 和 Presenter ,这两个接口分别对应PersonActivityPersonPresenter来实现。


这里还会发现PersonActivityPersonPresenter除了实现两个接口,还有继承了对应的Base类。这些Base类把一些公共的方法和用法封装了一下,方便使用。这些Base类在 app -> base 目录下:

1574668413598-10991ecb-c8a3-4318-9122-dd966b959bab.png


这里主要看下BasePresenterImpl 这里面有所有后台模块获取的封装方法:


/**
     * 人员组织服务
     */
    fun getOrganizationAssembleControlApi(context: Context?): OrganizationAssembleControlAlphaService? {
        return try {
            RetrofitClient.instance().organizationAssembleControlApi()
        }catch (e: Exception) {
            XLog.error("", e)
            if (context!=null) {
                XToast.toastLong(context, "人员组织模块服务异常,请联系管理员!!")
            }
            null
        }
    }

    /**
     * 认证服务
     */
    fun getAssembleAuthenticationService(context: Context?): OrgAssembleAuthenticationService? {
        return try {
            RetrofitClient.instance().assembleAuthenticationApi()
        }catch (e:Exception){
            XLog.error("", e)
            if (context!=null) {
                XToast.toastLong(context, "权限认证模块服务异常,请联系管理员!!")
            }
            null
        }
    }

    /**
     * 热图服务
     */
    fun getHotPicAssembleControlServiceApi(context: Context?): HotpicAssembleControlService? {
        return try {
            RetrofitClient.instance().hotpicAssembleControlServiceApi()
        }catch (e:Exception){
            XLog.error("", e)
            if (context!=null){
                XToast.toastLong(context, "热点图片新闻服务模块异常,请联系管理员!")
            }
            null
        }
    }
.............


所以前面样例代码中获取人员信息的PersonPresenter中直接使用getOrganizationAssembleControlApi 方法就能获取到人员组织模块,这个模块里面都是人员组织相关的接口,其实就是后台模块http://xxx:20020/x_organization_assemble_control/jest/index.html 这里面的那些接口请求方法,不过没有那么全,只是把用到的一些api写进去了。这些请求方法和后台模块那些请求连接怎么联系起来的,后面讲o2_auth_sdk模块的时候会讲到。


o2_auth_sdk上面API服务的介绍和开发


上面的BasePresenterImpl中可以看到我们的服务都从RetrofitClient中生成的。那是一个所有请求封装的实例,用到了一个非常有名的retrofit框架 。具体可以查看下o2_auth_sdk这个模块中的net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.RetrofitClient类。


服务:

1574668413623-93215bae-5f01-448d-b095-21c98bc0270c.png


RetrofitClient中看到生成一个服务对象的代码如下,里面的x_organization_assemble_control对应的就是服务端提供的api模块名称:


/**
     * 人员组织管理
     */
    fun organizationAssembleControlApi(): OrganizationAssembleControlAlphaService {
        val url = helper.getAPIDistribute(APIDistributeTypeEnum.x_organization_assemble_control)
        val retrofit = Retrofit.Builder()
                .baseUrl(url)
                .client(o2HttpClient)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build()
        return retrofit.create(OrganizationAssembleControlAlphaService::class.java)

    }


拿到了这个服务对象就可以在Presenter中执行服务中写的那些请求了:


interface OrganizationAssembleControlAlphaService {

    /**
     * 列示顶层组织
     */
    @GET("jaxrs/unit/list/top")
    fun unitListTop() : Observable<ApiResponse<List<UnitJson>>>

    /**
     * 列示某组织下的子组织列表
     * @param unit 组织
     */
    @GET("jaxrs/unit/list/{unit}/sub/direct")
    fun unitSubDirectList(@Path("unit") unit: String): Observable<ApiResponse<List<UnitJson>>>

    
    /**
     * 个人详细信息
     */
    @GET("jaxrs/person/{person}")
    fun person(@Path("person") person: String): Observable<ApiResponse<PersonJson>>
    
    }


如上面定义的 person这个方法,就是前面样例代码PersonPresenter中的人员信息请求:


         //人员信息请求

          service.person(name)


所以如果要添加请求,只要在对应的Service里面添加方法,在后端api文档中找到对应的请求路径,写到方法的对应注解中,比如@GET("jaxrs/person/{person}"),当然返回的对象格式也是固定的Observable<ApiResponse> 这个xxx就是返回json中的的data对象


如上面举例的x_organization_assemble_control 服务,后台的服务api说明地址是: http://xxx:20020/x_organization_assemble_control/jest/index.html 在这里你找到你需要使用的api,然后看看这个api的path在OrganizationAssembleControlAlphaService中是否能找到对应的方法,有就直接用,没有就参考其他方法添加一个。


如何在o2_auth_sdk模块中添加一个服务,视图查询为例


目前android源码中没有查询模块,这里以这个为例看看如何添加服务,查询的服务名:x_query_assemble_surface ,现在需要做如下步骤:


  1. 添加service接口,如前面介绍在net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.service包下添加一个服务接口,比如QueryAssembleSurfaceService :

interface QueryAssembleSurfaceService {
    /**
     * 执行视图
     * 返回视图结果
     * @param id:视图标识
     */
    @PUT("jaxrs/view/{id}/execute")
    fun excuteView(@Path("id") id: String) : Observable<ApiResponse<TestObject>>
}
  1. APIDistributeTypeEnumAPIAssemblesData 分别添加新服务模块 

1574668413617-9db78859-cf0a-4806-8a9a-43d81e4b22cc.png1574668413617-9db78859-cf0a-4806-8a9a-43d81e4b22cc.png


1574668413450-ffaa5459-36e1-481a-a183-ebff38d0efc9.png


RetrofitClient类中添加查询模块的服务生成方法:

1574668414392-715c0de3-2dcf-45a3-af66-cdfe4e1f3291.png

最后把这个服务提供给BasePresenterImpl,让Presenter都可以调用到。

1574668413589-fe1bd5a6-fb7b-4a10-a29c-308638369e18.png


Ok , 这样就完成了后台模块的添加,就可以到对应页面的Presenter中去写业务请求和数据处理了。


这里面关于视图查询返回的结果json如下,这里主要关注data里面的selectListgridselectList是字段列表,grid是展现结果。selectList字段中column对应grid结果中的字段名


结果样例:


{
    "type": "success",
    "data": {
        "where": {
            "accessible": false,
            "scope": "all",
            "appInfoList": [
                {
                    "name": "理论文章",
                    "id": "cc9e881f-27e6-47b0-96c0-27c7ec7d05cb"
                }
            ],
            "categoryInfoList": [],
            "dateRange": {
                "year": "",
                "month": "",
                "date": "",
                "season": 0,
                "week": 0,
                "adjust": 0,
                "start": "2018-11-25 15:03:48",
                "completed": "2019-11-25 15:03:48",
                "dateRangeType": "none"
            },
            "creatorPersonList": [],
            "creatorUnitList": [],
            "creatorIdentityList": []
        },
        "runtime": {
            "person": "xadmin",
            "unitList": [],
            "groupList": [],
            "roleList": [],
            "unitAllList": [],
            "identityList": [],
            "filterList": [
                {}
            ],
            "parameter": {},
            "count": 2000,
            "bundleList": []
        },
        "selectList": [
            {
                "orderType": "original",
                "column": "C88679060A600001E5DB1460D330DF40",
                "displayName": "标题",
                "path": "$document.title",
                "id": "C88679060A600001E5DB1460D330DF40",
                "hideColumn": false,
                "code": "",
                "allowOpen": false,
                "isName": false,
                "groupEntry": false
            },
            {
                "orderType": "desc",
                "column": "C8867917216000013819FEB090101705",
                "displayName": "发布时间",
                "path": "$document.publishTime",
                "id": "C8867917216000013819FEB090101705",
                "hideColumn": false,
                "code": "",
                "allowOpen": false,
                "isName": false,
                "groupEntry": false
            },
            {
                "orderType": "original",
                "column": "C886791B4590000139A81E41125010B7",
                "displayName": "拟稿人",
                "path": "$document.creatorPerson",
                "id": "C886791B4590000139A81E41125010B7",
                "hideColumn": false,
                "code": "",
                "allowOpen": false,
                "isName": true,
                "groupEntry": false
            }
        ],
        "filterList": [],
        "customFilterList": [
            {
                "title": "标题",
                "value": "",
                "otherValue": "",
                "path": "$document.title",
                "formatType": "textValue",
                "logic": "and",
                "comparison": "equals"
            }
        ],
        "orderList": [
            {
                "orderType": "desc",
                "column": "C8867917216000013819FEB090101705",
                "displayName": "发布时间",
                "path": "$document.publishTime",
                "id": "C8867917216000013819FEB090101705",
                "hideColumn": false,
                "code": "",
                "allowOpen": false,
                "isName": false,
                "groupEntry": false
            }
        ],
        "columnList": [],
        "grid": [
            {
                "bundle": "f407c1a4-d9b6-44c8-bc73-2fd33b5f1e39",
                "data": {
                    "C88679060A600001E5DB1460D330DF40": "这是一个测试",
                    "C8867917216000013819FEB090101705": "2019-07-20 16:28:29",
                    "C886791B4590000139A81E41125010B7": "金飞"
                }
            },
            {
                "bundle": "db13db1e-03a7-4e73-9847-a262af60d186",
                "data": {
                    "C88679060A600001E5DB1460D330DF40": "习近平:增强推进党的政治建设的自觉性和坚定性",
                    "C8867917216000013819FEB090101705": "2019-07-17 20:22:26",
                    "C886791B4590000139A81E41125010B7": "金飞"
                }
            },
            {
                "bundle": "5f09c684-e8d3-4890-a64d-2ab3c31de494",
                "data": {
                    "C88679060A600001E5DB1460D330DF40": "他爬坡穿林 就是为了看看这道屏障",
                    "C8867917216000013819FEB090101705": "2019-07-17 19:10:06",
                    "C886791B4590000139A81E41125010B7": "金飞"
                }
            }
        ],
        "exportGrid": true
    },
    "message": "",
    "date": "2019-11-25 15:03:48",
    "spent": 674,
    "size": -1,
    "count": 0,
    "position": 0
}










上一篇:启动OpenJPA日志

下一篇:在O2OA平台使用自定义应用开发webservice服务