声明:本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布
前言
本着学习移动开发的目的看了郭神的《第一行代码》,但看完后我有种茫然的感觉。是的,知道了activity 、service等这些组件的用法,但如何搭建一个应用程序?一个个activity写么,每个activity臃肿而庞大,各种功能参合到一个activity?这无疑是很疯狂的。之前我大概了解php的开发,知道php开发有很多成熟的框架,比如thinkphp、Yii等框架。于是我在网上找android的开发框架。也许我没有耐心,我没有找到类似php的通用的android框架,许多框架基本是为了写代码或编程方便而集成的工具集,我不认为这属于程序框架,因此我决定自己写个框架。
关于对象编程,我一直有个理念,就是对象之间的互动应该是通过消息来传递,而不是方法的调用。这样才能根本解除对象之间的耦合,对象、程序将更灵活。因此我着手写了对象消息编程框架,这个框架基本适于所有应用环境,包括web开发或本地应用,当然也适于android环境的开发。这里我将重点介绍对象消息编程框架如何应用在android开发上。
对象消息编程框架简介
关于我的对象消息编程框架,这里只是简单的介绍,理解了框架原理就能容易理解框架如何在android上的应用了。
一般我们的对象关系互动,基本是通过调用对方的方法,这种方式导致对象之间的耦合,对象失去了独立性。关于对象编程,我认为对象之间该通过消息的传递来互动,遵守以下约定:
1、对象之间只能通过收发消息来互动。一个对象不能调用另一个对象的方法、行为。对象的行为只能自己执行。
2、每个对象对收到的消息,采取或不采取行为完全取决对象本身。
3、对象由统一的创建者创建。
具体模型为:
源码程序包在cn.tianlong.tlobject 下,主要对象关系如下:
对象消息编程其实就是建立了一个对象模型、模板类—TLBaseModule。任何对象只要继承了这个类,将自动拥有消息处理、发送的能力。任何对象之间通过消息的传递来互动。消息为标准消息类(类TLMsg )。对象或者模块通过模块工厂TLObjectFactory创建,方便对象的创建、传递、保存、重复利用等。每个对象可以有自己的配置文件,配置文件为包含统一的配置项目,当然根据对象自身特点,可以增加特性项目。
具体对象消息编程框架理念及实现详见 https://blog.csdn.net/tianlong117
android 对象消息编程框架
现在将对象消息框架应用到android环境上,源码结构如下:
上图的源码结构,cn.tianlong.tlojbect 为通用消息对象编程框架 ,这是各种环境下的共同应用包。cn.tianlong.tlandroid为消息框架在android环境上的应用。其中base为基本包。utils 、view包为我将一些android组件进行了重新包装,我对android组件非常不满意,设计比较复杂,应用也麻烦,这些包仅仅是我的粗浅封装,大家可以根据自己的喜好是否采用或者重新封装、修改。这些我封装的组件不是框架的一部分,仅仅是为了编程而设计,框架的基本还是base包。
框架的基本流程可以说是mvc或者mvp模式,但我建议大家忘记mvc、mvp的概念。这里的对象都是通过消息而传递。用消息的理念来理解框架。那么对于一个activity来说 ,它仅仅是发出用户的输入消息,如最常用的按键消息,同时接受控件输出消息。activity不包含任何逻辑、业务功能。那么activtiy可以相当于mvc中的v。activity相当于一个输入输出模块,对于用户一个输入消息,activity捕获后发给对应的消息处理模块,消息处理模块针对该消息处理,将返回结果反馈给activity,activity将消息进行输出。
由于对象之间互动通过发送消息,实际中activity可以给每个对象或组件发送消息,但为了消息流程清晰,我们建立一个appcenter,各组件可以通过这个app中心来传递消息,当然没有这样的硬性要求,同时appcenter还负责程序配置、初始化等。对于具体实现方式,有以下可参考模式:
对于功能简单的activity:
activity 直接将消息发送给appcenter,appcenter处理消息。
对于复杂的activity:
对于复杂程序,如果activtiy消息都传给appcenter,将导致appcenter臃肿、逻辑不清,因此可以针对activity单独配置一个app模块,由该模块处理相应的activity消息。
开发应用时,需自行继承appcenter或app来实现自己的个性模块。
下面我们看具体实现案例,当时我没有其他案例代码,于是对郭神的《第一行代码》里面一些案例进行了重新封装,主要目的是测试框架的可行性。(框架及demo代码见文章下)
案例一:
在郭神的《第一行代码》中有个用来演示RecyelerView的聊天案例,我们这里对这个简单程序做以重构,用消息对象编程的方式来实现,基本代码还是原案例的,这里主要演示消息框架的原理。
我们先看activity 源码:
public class MainActivity extends TLBaseActivity {
private List<Msg> msgList = new ArrayList<Msg>();
private EditText inputText;
private Button send;
private RecyclerView msgRecyclerView;
private MsgAdapter adapter;
RecyclerView recyclerView ;
RecyclerViewAdapterDatas adapterDatas ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_msg);
inputText = (EditText) findViewById(R.id.input_text);
TLMsg msg =createMsg().setAction("recyclerView")
.setParam("activity",this)
.setParam("id",R.id.msg_recycler_view)
.setParam("laylout","linear")
.setParam("spancount",3)
.setParam("adapter","msgrecyclerViewAdapterdata");
TLMsg resultMsg=putMsg(moduleFactory,msg);
recyclerView=(RecyclerView)resultMsg.getParam("viewInstance");
adapterDatas=(RecyclerViewAdapterDatas)resultMsg.getParam("adapterdata");
send = (Button) findViewById(R.id.send);
send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String content = inputText.getText().toString();
if (!"".equals(content)) {
Msg rmsg = new Msg(content, Msg.TYPE_SENT);
putMsg(adapterDatas,new TLMsg("viewRefresh","msg",rmsg)) ;
inputText.setText(""); // 清空输入框中的内容
}
}
});
}
@Override
protected void onResume() {
super.onResume();
}
@Override
public TLMsg getMsg(Object fromWho, TLMsg msg) {
return null;
}
}
前面说过所有的对象或组件通过对象工厂创建,因此,下面代码通过工厂获取recyclerView及其适配器。
TLMsg msg =createMsg().setAction("recyclerView")
.setParam("activity",this)
.setParam("id",R.id.msg_recycler_view)
.setParam("laylout","linear")
.setParam("spancount",3)
.setParam("adapter","msgrecyclerViewAdapterdata");
TLMsg resultMsg=putMsg(moduleFactory,msg);
recyclerView=(RecyclerView)resultMsg.getParam("viewInstance");
adapterDatas=(RecyclerViewAdapterDatas)resultMsg.getParam("adapterdata");
首先构建工厂消息msg,然后将消息发送给工厂
putMsg(moduleFactory,msg);
通过返回的消息resultMsg里提取recyclerView、adapterDatas对象。为了方便,这里的recyclerView由我进行了重新包装的。
对于每一个聊天内容,我们将消息发给adapterDatas,由adapterDatas控制recyclerView显示。
putMsg(adapterDatas,new TLMsg("viewRefresh","msg",rmsg)) ;
在适配器RecyclerViewAdapterDatas中,适配器获取了activity传来的消息,执行方法viewRefresh进行页面刷新。
@Override
public void viewRefresh(Object fromWho, TLMsg msg) {
dataList.add((Msg)msg.getParam("msg"));
putMsg(appCenter,new TLMsg("setDatas","msgList",dataList)) ; //数据保存至appcenter,防止页面返回时数据丢失
adapter.notifyItemInserted(dataList.size() - 1); // 当有新消息时,刷新ListView中的显示
recyclerView.scrollToPosition(dataList.size() - 1); // 将ListView定位到最后一行
}
由这简单的案例看出,这里activity仅仅是获取消息、发送消息,对消息没有任何逻辑处理,同时也不控制recyclerView。对象的角色、及关系变得清晰。
案例二 手机信号及基站信息:
现在我们看复杂点的案例-手机信号及基站信息。页面如下:
这个案例实现以下功能:
1、获取手机信号、基站信息
2、根据基站信息查询互联网获取基站位置
3、将基站信息保存到本地、上传到服务器。可手动及自动保存、上传。自动保存启动时,5秒自动保存、上传一次。
该案例涉及了数据库读写、远端web查询、数据提交。
现在我们看主activity代码。
activity获取用户输入、创建相应的消息并传递给对应的app处理:
@Override
public void onClick(View v) {
TLMsg msg =createMsg();
switch (v.getId()) {
case R.id.button_m:
msg.setMsgId("startactivity"); ;
msg.setParam("classname", RecoderActivity.class);
putMsg(appCenter,msg);
break;
case R.id.button_post:
msg.setAction("postData");
putMyApp(msg);
break;
case R.id.button_save:
msg.setAction("saveData");
putMyApp(msg);
break;
case R.id.button_autosave:
msg.clear();
msg.setAction("autosaveData");
putMyApp(msg);
break;
}
}
activity获取消息处理模块返回的结果,根据消息类别输出显示结果:
public TLMsg getMsg(Object fromWho, TLMsg msg) {
switch (msg.getAction()) {
case "getSignal":
showResponse( msg) ;
break;
case "jizhan":
showjizhan( msg) ;
break;
case "myrecords":
myrecords( msg) ;
break;
case "postReturn":
postReturn( msg) ;
break;
case "serviceReturn":
serviceReturn( msg) ;
break;
case "saveReturn":
runOnUiThread(new Runnable() {
@Override
public void run() {
savetext.setText("保存成功"+i+"次!");
i++;
}
});
break;
default:
}
return null ;
}
private void serviceReturn(TLMsg msg) {
final String content;
if(msg.getParam("error")!=null && (boolean)msg.getParam("error")==false)
{
content ="上传"+p+"次!"+ TLMsgUtils.msgToGson(msg);
p++;
}
else
if(msg.getParam("netstate")!=null &&(boolean)msg.getParam("netstate")==false )
{
content="没有打开网络";
}
else
{
content="上传错误"+m+"次!";
m++;
}
postresulttext.setText(content);
}
activity 实现输入、输出功能,业务逻辑由对应的app模块TLSignalApp 来实现。在TLSignalApp 中,也是根据传来的消息执行各个业务功能或模块:
@Override
protected TLMsg myCheckMsgCmd(Object fromWho, TLMsg msg) {
TLMsg returnMsg=null;
switch (msg.getAction()) {
case "setup":
cell_id="";
lac ="";
putMsg(tltm,new TLMsg("init","listener",this)
.setParam("listenAction","getSignal"));
break;
case "getSignal":
getSignal( msg);
break;
case "jizhanData":
jizhandata(fromWho,msg);
break;
case "postData":
postjizhan(data);
break;
case "postReturn":
postReturn(fromWho,msg);
break;
case "serviceReturn":
serviceReturn(fromWho,msg);
break;
case "saveData":
saveData();
break;
case "autosaveData":
putMsg(jztask,createMsg().setAction("startTask"));
break;
case "getData":
returnMsg=createMsg().setParam("data",dbmsg);
break;
default:
}
return returnMsg ;
}
这里不进行详细分析了,由于对象之间通过消息传递实现互动,通过跟踪消息路径,逻辑比较清晰,因此代码也容易阅读。
结束语
限于篇幅无法对框架详细介绍,有兴趣的朋友可在博客上进一步了解消息框架结构。熟悉了对象消息框架也很容易了解框架如何应用在android环境上了 。限于本人经历及知识,这只是本人的学习尝试和探索,框架的代码也很粗糙,仅仅是希望起到抛砖引玉的作用,共同探索编程之道吧。
源码地址:app20180926.rar_免费高速下载|百度网盘-分享无限制
代码在android Studio 上开发 ,解压目录对应app目录