日期: 2021 年 4 月 19 日

企业选择SaaS时,要全面了解一下SaaS的优缺点

SaaS(软件服务化)具有很多优势,如成本低、部署迅速、定价灵活,但在审计和法规遵从以及与企业已有方案的整合上也有其弱点,企业选择SaaS时对这些优缺点要全面认识。

软件服务化(Software as a Service,SaaS)让用户可以通过互联网使用实时运行的软件,由于这种软件具有很多Web 2.0的特点,能给用户带来*其丰富的体验,而且节约了用户的投资,在软件市场上很流行。Gartner和Forrester等研究公司都认为,SaaS是IT行业发展速度*快的领域之一。SaaS的拥护者也声称,SaaS为企业实现经营目标提供了传统套装应用软件之外的一种选择,它具有的成本效益比后者高得多。如今,包括雅虎、Google、电子港湾(eBay)、亚马逊、Salesforce.com等在内的很多知名供应商都提供SaaS的解决方案。其中雅虎、Google、电子港湾和亚马逊等提供商的主要服务侧重于满足消费者的需求,而不是满足企业的需求,而Salesforce.com、CollabNet和BEN等提供的解决方案则是专门为解决常见的企业业务问题而设计。譬如,Salesforce 提供针对客户关系管理的解决方案。

SaaS的优点

基于SaaS的解决方案本身具有某些优点,企业在决定积*采用这种解决方案之前,对此要有了解。优点包括:

■ 可重复使用;

■ 成本较低;

■ 可以更快地提供解决方案;

■ 灵活的定价模式,符合企业的发展模式;

■ 更好的支持;

■ 更好的解决方案;

■ 为企业减少所需的IT资源。

可重复使用

SaaS的*大优点之一就是“可重复使用”,这其实是SaaS其他所有优点的基础。如果你确信企业应该使用SaaS解决方案,实际上你就已决定不从事重复工作,而是单单利用现有的解决方案。至少,该解决方案实施起来速度更快、成本更低,虽然算不得*好,也会是“足够好”。

成本较低的解决方案

企业如果采用SaaS解决方案,其成本很有可能只有自行实施、部署、运行、管理及支持这类解决方案所需成本的一小部分。SaaS解决方案的一个*大优点是,它们在价格方面可以提供非常显著的规模经济。之所以如此,原因就在于大多数SaaS提供商可以非常轻松地利用其在特定行业领域“重复使用”的优点,能提供具有高度可复制的“标准化”的解决方案。*终结果是,它们通常可以将这种可重复使用的优点惠及客户,同时可以大大节省成本。

可以更快地提供解决方案

SaaS的提供商早已对企业即将采用的针对特定领域的解决方案进行了规划、设计、实施、部署及测试。这意味着企业可以使用已有解决方案,而企业要自行实施这样的解决方案需要很长时间。以大多数SaaS解决方案为例,软件已经实时运行、随时可以使用。唯一的“瓶颈”就是支付服务费和如何把这个工具与自己的业务流程联系起来。

灵活的定价模式

采用SaaS的解决方案时,企业通常会使用基于订购、可以确定的定价模式,这种模式让企业可以在需要时购买所需服务。这意味着企业可以根据发展模式购买相应软件。企业规模扩大时只要开启新的连接,用不着购置新的基础设施和资源。而一旦企业规模缩小只要关闭连接即可。这样,企业可以避免被过多的基础设施和资源所累,而传统上,即使你再也用不着它们,也不得不继续需要管理及支持。

更好的支持

使用SaaS解决方案时,企业很可能使用由专家提供、管理及支持的解决方案,他们24×7小时关注某一专门领域。从诸多方面来看,该提供商相当于企业的实时延伸部分。实际上,连接到SaaS提供商对使用者而言是一种成本非常低的方式,只要连接上,SaaS提供的资源就始终在为你服务,这相当于扩增了企业的资源。

为企业减少所需的IT资源

通常只要用浏览器就可以连接到SaaS提供商的托管平台,所以用户需要的全部基础设施就是用来运行浏览器的设备以及让该设备可以访问互联网的简易网络。这意味着企业不必提供、运行、管理及支持自己的内部基础设施。对那些规模非常小、不想自行管理IT部门这项复杂工作的企业而言,SaaS无疑是一种行之有效的方案,有助于加快实施企业的解决方案,同时尽量减少所需的IT资源。

【Flutter】InheritedWidget、InheritedModel的使用介绍

Flutter中有四种widget

StatelessWidget
StatefullWidget
RenderObjectWidget
InheritedWidget
其中StatelessWidget和StatefulWidget是*常见到的,从状态管理角度的分类;RenderObjectWidget是所有需要渲染的Widget的基类。

至于*后一个InheritedWidget,许多初学者不一定了解,但是在一些稍微复杂的项目中是必须要用到的,所以本文介绍一下InheritedWidget的用法

InheritedWidget
To obtain the nearest instance of a particular type of inherited widget from a build context, use BuildContext.inheritFromWidgetOfExactType.

Inherited widgets, when referenced in this way, will cause the consumer to rebuild when the inherited widget itself changes state.

通常情况下,子widget无法单独感知父widget的变化,当父state变化时,通过其build重建所有子widget;

%title插图%num

InheritedWidget可以避免这种全局创建,实现局部的子widget更新:
子widget通过BuildContext.inheritFromWidgetOfExactType从buildContext中获取并监听指定类型的父InheritedWidget,并跟随其重建而rebuild

如上图,点击C按钮,State变化后,A的Text可以单独刷新,B不受到影响

代码演示
接下来通过代码对比一下使用或不使用InheritedWidget的区别:

%title插图%num

点击+,后上面的0变化,中间的文字部分不变化。

传统实现
点击按钮state变化后,widgetA、B、C都会rebuild

class TopPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text(‘Demo’),
),
body: HomePage(),
),
);
}
}

class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
int _counter = 0;

void _incrementCounter() {
setState(() {
_counter++;
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
WidgetA(_counter),
WidgetB(),
WidgetC(_incrementCounter),
],
),
);
}
}

class WidgetA extends StatelessWidget {
final int counter;

WidgetA(this.counter);

@override
Widget build(BuildContext context) {
return Center(
child: Text(
‘${counter}’,
style: Theme.of(context).textTheme.display1,
),
);
}
}

class WidgetB extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(‘I am a widget that will not be rebuilt.’);
}
}

class WidgetC extends StatelessWidget {
final void Function() incrementCounter;

WidgetC(this.incrementCounter);

@override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () {
incrementCounter();
},
child: Icon(Icons.add),
);
}
}

使用AndroidStudio的Flutter Performance可以看到widgetA、B、C都参与了rebuild

使用InheritedWidget实现
class TopPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(
child: Scaffold(
appBar: AppBar(
title: Text(‘InheritedWidget Demo’),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
WidgetA(),
WidgetB(),
WidgetC(),
],
),
),
),
);
}
}

class _MyInheritedWidget extends InheritedWidget {
_MyInheritedWidget({
Key key,
@required Widget child,
@required this.data,
}) : super(key: key, child: child);

final HomePageState data;

@override
bool updateShouldNotify(_MyInheritedWidget oldWidget) {
return true;
}
}

class HomePage extends StatefulWidget {
HomePage({
Key key,
this.child,
}) : super(key: key);

final Widget child;

@override
HomePageState createState() => HomePageState();

static HomePageState of(BuildContext context, {bool rebuild = true}) {
if (rebuild) {
return (context.inheritFromWidgetOfExactType(_MyInheritedWidget) as _MyInheritedWidget).data;
}
return (context.ancestorWidgetOfExactType(_MyInheritedWidget) as _MyInheritedWidget).data;
// or
// return (context.ancestorInheritedElementForWidgetOfExactType(_MyInheritedWidget).widget as _MyInheritedWidget).data;
}
}

class HomePageState extends State<HomePage> {
int counter = 0;

void _incrementCounter() {
setState(() {
counter++;
});
}

@override
Widget build(BuildContext context) {
return _MyInheritedWidget(
data: this,
child: widget.child,
);
}
}

class WidgetA extends StatelessWidget {
@override
Widget build(BuildContext context) {
final HomePageState state = HomePage.of(context);

return Center(
child: Text(
‘${state.counter}’,
style: Theme.of(context).textTheme.display1,
),
);
}
}

class WidgetB extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(‘I am a widget that will not be rebuilt.’);
}
}

class WidgetC extends StatelessWidget {
@override
Widget build(BuildContext context) {
final HomePageState state = HomePage.of(context, rebuild: false);
return RaisedButton(
onPressed: () {
state._incrementCounter();
},
child: Icon(Icons.add),
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
可以看到state变化时,widgetB、C都没有rebuild

关键代码说明
针对InheritedWidget版本中的关键类进行说明

WidgetA、WidgetC
传统版本中WidgetA、C通过构造函数传入父级的state以及回调
InheritedWidget版本中,可以通过如下静态方法获取

final HomePageState state = HomePage.of(context); // WidgetA
final HomePageState state = HomePage.of(context, rebuild: false); // WidgetC
1
2
WidgetC是一个Button需要通过state获取回调方法,但不需要跟随state变化而刷新,所以rebuild指定false

接下来详细看一下获取state的静态方法 HomePage.of

HomePage
static HomePageState of(BuildContext context, {bool rebuild = true}) {
if (rebuild) {
return (context.inheritFromWidgetOfExactType(_MyInheritedWidget) as _MyInheritedWidget).data;
}
return (context.ancestorWidgetOfExactType(_MyInheritedWidget) as _MyInheritedWidget).data;
// or
// return (context.ancestorInheritedElementForWidgetOfExactType(_MyInheritedWidget).widget as _MyInheritedWidget).data;
}
1
2
3
4
5
6
7
8
HomePage.of用来通过buildContext,找到*近的_MyInheritedWidget。然后就可以同_MyInheritedWidget获取其持有的state。

获取上级Widget的几个关键方法如下:

method description
inheritFromWidgetOfExactType 获取*近的给定类型的上级Widget,该widget必须是InheritedWidget的子类,并向上级widget注册传入的context,当上级widget改变时,这个context持有的widget会rebuild以便从该widget获得新的值。这就是child向InheritedWidget注册的方法。
inheritFromWidgetOfExactType 仅仅用来获取*近的给定类型的上级Widget,不会因为上级Widget的改变而rebuild
ancestorInheritedElementForWidgetOfExactType 功能与inheritFromWidgetOfExactType一样,但是只会寻找InheritedWidget的子类,所以可以以O(1)的复杂度查找上级Widget
因此,widgetA随着父widget的变化而rebuild,widgetB并没有rebuild

_MyInheritedWidget
class _MyInheritedWidget extends InheritedWidget {
_MyInheritedWidget({
Key key,
@required Widget child,
@required this.data,
}) : super(key: key, child: child);

final HomePageState data;

@override
bool updateShouldNotify(_MyInheritedWidget oldWidget) {
return true;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
继承自InheritedWidget,所以子Widget可以通过inheritFromWidgetOfExactType获取。

updateShouldNotify控制是否需要子widget感受其变化,如果返回true,则通过inheritFromWidgetOfExactType注册的子widget跟随其变化rebuild

子widget*终目的是要获取共享的父级state,所以这里通过data属性持有了state。

那再来看一下这个HomePageState

HomePageState
@override
Widget build(BuildContext context) {
return _MyInheritedWidget(
data: this,
child: widget.child,
);
}
1
2
3
4
5
6
7
此处_MyInheritedWidget的使用是关键。

传统写法中,build中直接创建widgetA、B、C并返回,因此每当state变化时,会重新创建子widget并rebuild;

InheritedWidget版本中,HomePage保持父widget(TopPage)的children,当state变化时widgetA、B、C不会重建,而是重新传入给_MyInheritedWidget,重建的只有_MyInheritedWidget

TopPage
class TopPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
・・・
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
WidgetA(), // 子widget的创建移动到这里
WidgetB(),
WidgetC(),
],
),
・・・
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
根据上文的说明,为了避免子widget的反复创建和rebuild,将widgetA、B、C的实例化移动到这里

InheritedModel
上面的例子中我们通过自定义了rebuild参数来指定子Widget是否参与rebuild,实际上也可以使用InheritedModel完成此需求

InheritedModel继承自InheritedWidget,可以通过字符串key(aspect)来指定特定子widget进行rebuild。

简单看一下InheritedModel版本与InheritedWidget版本在实现上的不同

@override
HomePageState createState() => HomePageState();

static HomePageState of(BuildContext context, String aspect) {
return InheritedModel.inheritFrom<_MyInheritedWidget>(context, aspect: aspect).data;
}
}
1
2
3
4
5
6
7
使用 InheritedModel.inheritFrom获取widget

class _MyInheritedWidget extends InheritedModel {

@override
bool updateShouldNotifyDependent(_MyInheritedWidget old, Set aspects) {
return aspects.contains(‘A’); // 当aspect包晗“A”时,通知其rebuild
}
}
1
2
3
4
5
6
7
继承InheritedModel,重写updateShouldNotifyDependent

class WidgetA extends StatelessWidget {
@override
Widget build(BuildContext context) {
final HomePageState state = HomePage.of(context, ‘A’); // 注册aspect为“A“
1
2
3
4
class WidgetC extends StatelessWidget {
@override
Widget build(BuildContext context) {
final HomePageState state = HomePage.of(context, ‘C’); // 注册aspect为“C”
1
2
3
4
如上,因为注册的key(aspect)不同,只有widgetA会受到rebuild的通知

更局部的刷新
如果widgetA是下面这样,我们希望能进一步控制其子widget的局部刷新

class WidgetA extends StatelessWidget {
@override
Widget build(BuildContext context) {
final HomePageState state = HomePage.of(context);

return Column(
children: <Widget>[
Center(
child: Text(
‘${state.counter}’,
style: Theme.of(context).textTheme.display1,
),
),
Text(“AAAAA”), // 此处不需rebuild
],
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
如果彻底理解了BuildContext和InheritedWidget的注册机制,是可以很容易实现的:

return Column(
children: <Widget>[
Center(
child: Builder(builder: (context){
final HomePageState state = HomePage.of(context);
return Text(
‘${state.counter}’,
style: Theme.of(context).textTheme.display1,
);
}),
),
Text(“AAAAA”),
],
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
通过Builder来创建一个匿名类widget,然后将HomePage.of移到其内部。此时InheritedWidget中注册的context不再是widgetA而是这个匿名类widget,因此可以实现widgetA的局部刷新

不使用InheritedWidget
我想通过上文的介绍大家应该能够想到,如果子widget仅仅想访问父级state(不通过构造函数传参的方式),但没有监听其变化的需要,可以不使用InheritedWidget:

class TopPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text(‘Demo’),
),
body: HomePage(),
),
);
}
}

class HomePage extends StatefulWidget {
HomePageState state; // 持有state供子类获取

@override
HomePageState createState() {
state = HomePageState();
return state;
}
}

class HomePageState extends State<HomePage> {
int counter = 0; // 去掉private

void incrementCounter() { // 去掉private
setState(() {
counter++;
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
WidgetA(),
WidgetB(),
WidgetC(),
],
),
);
}
}

class WidgetA extends StatelessWidget {
@override
Widget build(BuildContext context) {
final HomePage widget = context.ancestorWidgetOfExactType(HomePage); // 获取state
final HomePageState state = widget?.state;

return Center(
child: Text(
‘${state == null ? 0 : state.counter}’,
style: Theme.of(context).textTheme.display1,
),
);
}
}

class WidgetB extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(‘I am a widget that will not be rebuilt.’);
}
}

class WidgetC extends StatelessWidget {
@override
Widget build(BuildContext context) {
final HomePage widget = context.ancestorWidgetOfExactType(HomePage);
final HomePageState state = widget?.state;

return RaisedButton(
onPressed: () {
state?.incrementCounter();
},
child: Icon(Icons.add),
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
通过ancestorWidgetOfExactType寻找指定类型的widget,然后获取其state使用,当然这个遍历是O(n)的,性能比InheritedWidget版本要差

*后
Flutter中很多组件都是基于InheritedWidget实现的,例如Scoped Model、BLoC(Business Logic of component)等,想要掌握这些高级特性的使用先从了解InheritedWidget开始吧

代码:
https://github.com/vitaviva/flutter_inherited_widget_sample/tree/master/flutter_inherited_widget
————————————————
版权声明:本文为CSDN博主「fundroid_方卓」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/vitaviva/article/details/105462686

BYOD是什么,云计算和BYOD之间,主要有什么关系?

一、什么是BYOD

(BringYourOwnDevice)指携带自己的设备办公,这些设备包括个人电脑、手机、平板等移动智能终端设备。BYOD近年来一直呈线性方式在增长。它向人们展现了一个更现代化的办公场景。我们也可以这样解释BYOD(BecomeYourOfficeDevice):在你自己的设备上安装的公司的软件,以便可以让你使用公司的资源。当员工的设备,例如iphone,安装了这样的管理软件,员工自己的手机就变成了公司的手机,Agent会不停的和服务器同步。目前,无论公司规模大小,都已经有了开始采用BYOD。其中一大优势是消除了硬件采购和维护费用,节约了成本。另一方面员工作也从中获得利益,因为BYOD给了他们足够的自由去选择他们喜欢的,且适合个人和业务的设备。BYOD可能是当今业务的首要趋势,但是如果合适的移动策略在不合适的地方处理不断增加的移动设备,这*终就可能导致麻烦。BYOD在安全方面存在着很大的风险,虽然经济性很高,不必为员工购买任何设备,但是员工带着包含大量信息的设备到处行走,这本身就是一种风险;员工会下载不同的文件,违反安全性就会一次又一次地发生。我们可以看到BYOD带来的安全性问题:数据丢失、信息泄漏等等不断地被提上日程,是当务之急。

二、云计算

云计算,一种基于互联网的计算方式,通过这种方式,共享的软硬件资源和信息可以按需求提供给计算机和其他设备。云计算是继1980年代大型计算机到客户端-服务器的大转变之后的又一种巨变。它是透过网络将庞大地计算处理程序自动分拆成无数个较小地子程序,再交由多部服务器所组成地庞大系统经搜寻、计算分析之后将处理结果回传给用户。云计算是新一代IT计算模式,它运用先进的分布式计算及存储架构为用户提供方便的体验并降低使用成本。

三、BYOD和云计算关系

为了提高BYOD数据的控制能力,我们基于云的应用和服务,可以转移移动设备集中化或者额虚拟化资源的大部分核心信息,这些信息能够通过无线网络或者互联网用所有类型的设备访问。这不仅提供了具体的内容且轻松的管理目标,保证安全和移动策略保障,也能够清晰的分离开业务应用数据和个人。但是一旦BYOD办公的优势及发展目标掩盖了云计算的量的优势,问题就复杂了。采用BYOD化办公意味着应用运行界面更加多样,这与*大程度优化应用交付同一界面的云优势背道而驰。阿克劳尼斯(Acronis)表示,企业低估了公共云的危险。69%的机构并没有适当的公共云政策,80%的企业并未培训过员工如何正确使用这些平台。Info-TechResearchGroup首席分析师MarkTauschek表示:“一些类型的云服务相对于内部服务和或复合服务来说可能会被看作是不安全的。因为它们对文件没有控制。没有达到企业内容管理的高度,但是你可以控制数据,BYOD-云存储正在迅速成为企业IT的焦点。”企业显然没有做好准备应对不断增长的BYOD趋势,在处理BYOD相关问题时,企业显得有些措手不及。尽管大多数受访者都是IT专业人士,三分之一的受访者表示,他们没有到位的BYOD政策。10%的受访者承认他们不知道公司是否有BYOD政策。然而,88%的受访者表示,他们公司有某种形式的BYOD,不管有没有被批准。BYOD策略让云变得复杂,所以我们要建立兼容BYOD云计算系统,优化云,使得企业并不必在云项目和BYOD中二选一。为了确保公司中的BYOD和云计算并存,组织必须对其支持的流行进行定义。

创建访问公司应用的规则;

记录所有员工都认可的使用政策;

开发政策,用于当员工离职和企业需要访问数据的情况下;

实现工具,来识别并降低可能引入组织的风险。

*后,在公司引入BYOD政策之前,一定要确保已经了解了BYOD的好处和不足,根据你的业务需求采取*合适的决策。解除的企业高管对安全性的担忧。使BYOD对云安全性不造成负面影响。

云计算、大数据的快速发展BOYD必将成为企业中的必然现象,接受BOYD才能企业前进,但企业在接受的同时必须制定计划保证云计术解决内部问题。作为IT管理者,我们要评估企业内的法规限制,同时终端用户要知道安全风险,有一个强健的BYOD策略,减轻风险,能够更好地获得云计算的好处。让云计算和BYOD的融合成为企业IT的新突破,使得BYOD和云可以“共同繁荣”。

Flutter InheritedWidget

InheritedWidget 提供了一种数据在 widget 树中从上到下传递、共享的方式,比如我们在应用的根 widget 中通过 InheritedWidget 共享了一个数据,那么我们便可以在任意子 widget 中来获取该共享的数据!这个特性在一些需要在 widget 树中共享数据的场景中非常方便!如 Flutter SDK 中正是通过 InheritedWidget 来共享 Theme (应用主题) 和 Locale (当前语言环境) 信息的.

InheritedWidget 是个只有一个抽象方法的抽象类:

abstract class InheritedWidget extends ProxyWidget {
const InheritedWidget({ Key key, Widget child })
: super(key: key, child: child);

@override
InheritedElement createElement() => InheritedElement(this);

@protected
bool updateShouldNotify(covariant InheritedWidget oldWidget);
}

抽象类表示需要有一个实现类来继承它.

抽象方法 updateShouldNotify(covariant InheritedWidget oldWidget) 会在 InheritedWidget 实例 build 时, 返回值告诉 Framework 框架是否需要通知子树, 数据发生了变动. 方法签名中有个 covariant 关键字, 意思为协变, 告诉编译器 oldWidget 可以窄化成实现类的类型, 方便判断相关数据的变动情况.

使用
首先实现 InheritedWidget 类:

class ShareDataWidget extends InheritedWidget {
ShareDataWidget({
@required this.data,
Key key,
Widget child
}): super(key: key, child: child);

final int data;

@override
bool updateShouldNotify(ShareDataWidget oldWidget) {
return oldWidget.data != data;
}
}

上面代码中, 有个用于共享的数据 data, 覆写了 InheritedWidget 类唯一的抽象方法, 返回 data 有无变动, oldWidget 是 ShareDataWidget 实例在 build 时, Framework 传递过来的, 表示旧的 widget.

接下来是个 demo:

class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;

@override
Widget build(BuildContext context) {
return ShareDataWidget(
data: _counter,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: const Text(“+1”),
onPressed: () => setState(() => ++_counter),
),
RaisedButton(
child: const Text(“-1”),
onPressed: () => setState(() => –_counter),
),
TestWidget(),
],
),
),
);
}
}

class TestWidget extends StatelessWidget {
TestWidget();

@override
Widget build(BuildContext context) {
ShareDataWidget shareDataWidget;
shareDataWidget = context.inheritFromWidgetOfExactType(ShareDataWidget);
String data = shareDataWidget.data.toString();
return Text(data);
}
}

TestWidget 是另一个子控件, 在其 build 方法中引用了 ShareDataWidget 中的数据.

TestWidget 通过 context.inheritFromWidgetOfExactType(ShareDataWidget) 在祖先节点中获取一个距离*近的 ShareDataWidget widget, 在这同时, 也注册了依赖关系, 使得以后 ShareDataWidget 的数据一旦发生的变化, 就会通知所有的后代 widget 去刷新.

当按下 RaisedButton 按钮时, 调用 setState() 方法, 数据 _counter 改变并触发整个页面的 rebuild. InheritedElement 将调用 widget.updateShouldNotify(oldWidget), 如果 updateShouldNotify() 方法返回 true, 则 InheritedElement 会遍历并执行子 Element 的 didChangeDependencies() 方法.

void didChangeDependencies() {
// …
markNeedsBuild(); // 标记子Element需要rebuild
// 如果该Element是StatefullElement类型的话还会调用
// _state.didChangeDependencies();
}

注意, 这里是 Element 的方法, 如果子 Widget是 StatefulWidget 类型的话, State 里面也有一个 didChangeDependencies() 方法, 也会被调用.

可以发现, 只能按下 RaisedButton 按钮, ShareDataWidget 才会 rebuild.

因为 RaisedButton 按钮和 ShareDataWidget 两个 widget 都在 StatefulWidget 的包裹中, 按下按钮会调用 setState() 方法, 让 ShareDataWidget 也会被更新, 而在 TestWidget 中只能读取, 却无法改变 ShareDataWidget 内的 data 数据.

只能在 InheritedWidget 所在的 StatefulWidget 里调用 setState() 方法重建 InheritedWidget 实例和数据, 其他子树 widget 都只能读取到一个副本, 而无法修改 InheritedWidget 共享的值.

例如 ThemeData themeData = Theme.of(context); 拿到的是一个副本 ThemeData,
做些修改后只对单个路由局部换肤, 如果想要对整个应用换肤, 则需要手动去修改 MaterialApp 的 theme 属性.

那么子树 widget 需要修改共享的数据, 就要使用 跨组件状态共享 (Provider) , 其大概原理就是 InheritedWidget + 类似 EventBus 的事件通知, 让 InheritedWidget 进行重建.

 

Android课程表App

*近写了个简单的Android 课程表App,我是个初学者,这个App里使用了:

Android内置的SQLite数据库储存课程数据。
课程的视图用CardView卡片视图。
课程的View是动态加入的,动态添加View的好处是很灵活

如果靠静态的XML构建的话就有点难扩展了,因为你不知道学生一天总共有多少节课

%title插图%num

下面的xml代码是课程表的布局了,一个LinearLayout表示课程表的左侧节数视图,七个Relative表示从星期一到星期天,然后根据用户的输入在正确的地方添加课程视图.代码有点长我没有全部贴出来,整个代码上面还有个父类ScrollView布局的,因为课程表可能有许多节课,是需要下拉的.

<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:orientation=”vertical”>

<!–工具条–>
<android.support.v7.widget.Toolbar
android:id=”@+id/toolbar”
android:layout_width=”match_parent”
android:layout_height=”?attr/actionBarSize”
android:background=”#7fab96c5″
app:theme=”@style/ThemeOverlay.AppCompat.Dark.ActionBar”
app:popupTheme=”@style/AlertDialog.AppCompat.Light”/>
<!–周–>
<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”30dp”
android:background=”#7fab96c5″>

<TextView
android:layout_width=”110px”
android:layout_height=”match_parent”
android:gravity=”center”
android:text=”节/周”/>
<TextView
android:layout_width=”0dp”
android:layout_height=”match_parent”
android:layout_weight=”1″
android:gravity=”center”
android:text=”周一”/>
<TextView
android:layout_width=”0dp”
android:layout_height=”match_parent”
android:layout_weight=”1″
android:gravity=”center”
android:text=”周二”/>
<TextView
android:layout_width=”0dp”
android:layout_height=”match_parent”
android:layout_weight=”1″
android:gravity=”center”
android:text=”周三”/>
<TextView
android:layout_width=”0dp”
android:layout_height=”match_parent”
android:layout_weight=”1″
android:gravity=”center”
android:text=”周四”/>
<TextView
android:layout_width=”0dp”
android:layout_height=”match_parent”
android:layout_weight=”1″
android:gravity=”center”
android:text=”周五”/>
<TextView
android:layout_width=”0dp”
android:layout_height=”match_parent”
android:layout_weight=”1″
android:gravity=”center”
android:text=”周六”/>
<TextView
android:layout_width=”0dp”
android:layout_height=”match_parent”
android:layout_weight=”1″
android:gravity=”center”
android:text=”周日”/>
</LinearLayout>
<!–课程表–>
<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”wrap_content”>

<LinearLayout
android:id=”@+id/class_number_layout”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:orientation=”vertical”/>
<RelativeLayout
android:id=”@+id/monday”
android:layout_width=”0dp”
android:layout_height=”match_parent”
android:layout_weight=”1″
android:orientation=”vertical”
android:layout_margin=”1dp”/>
<RelativeLayout
android:id=”@+id/tuesday”
android:layout_width=”0dp”
android:layout_height=”match_parent”
android:layout_weight=”1″
android:orientation=”vertical”
android:layout_margin=”1dp”/>
<RelativeLayout
android:id=”@+id/wednesday”
android:layout_width=”0dp”
android:layout_height=”match_parent”
android:layout_weight=”1″
android:orientation=”vertical”
android:layout_margin=”1dp”/>
<RelativeLayout
android:id=”@+id/thursday”
android:layout_width=”0dp”
android:layout_height=”match_parent”
android:layout_weight=”1″
android:orientation=”vertical”
android:layout_margin=”1dp”/>
<RelativeLayout
android:id=”@+id/friday”
android:layout_width=”0dp”
android:layout_height=”match_parent”
android:layout_weight=”1″
android:orientation=”vertical”
android:layout_margin=”1dp”/>
<RelativeLayout
android:id=”@+id/saturday”
android:layout_width=”0dp”
android:layout_height=”match_parent”
android:layout_weight=”1″
android:orientation=”vertical”
android:layout_margin=”1dp”/>
<RelativeLayout
android:id=”@+id/weekday”
android:layout_width=”0dp”
android:layout_height=”match_parent”
android:layout_weight=”1″
android:orientation=”vertical”
android:layout_marginTop=”1dp”
android:layout_marginLeft=”1dp”
android:layout_marginBottom=”1dp”/>
</LinearLayout>
</LinearLayout>

课程的布局文件
<android.support.v7.widget.CardView
xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:app=”http://schemas.android.com/apk/res-auto”
android:layout_width=”45dp”
android:layout_height=”70dp”
app:cardBackgroundColor=”#7feacdd1″
app:cardCornerRadius=”6dp”>

<TextView
android:id=”@+id/text_view”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_gravity=”center”
android:gravity=”center”/>
</android.support.v7.widget.CardView>
这里的宽和高都是预览用的,因为实际的宽和高都是代码控制的.

创造视图的java代码
private void createLeftView(Course course) {
//动态生成课程表左侧的节数视图
int len = course.getEnd();
if (len > maxClassNumber) {
LinearLayout classNumberLayout = (LinearLayout) findViewById(R.id.class_number_layout);
View view;
TextView text;
for (int i = 0; i < len-maxClassNumber; i++) {
view = LayoutInflater.from(this).inflate(R.layout.class_number, null);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(110,180);
view.setLayoutParams(params);
text = view.findViewById(R.id.class_number_text);
text.setText(“” + number++);
classNumberLayout.addView(view);
}
maxClassNumber = len;
}
}

//创建卡片课程视图
private void createView(final Course course) {
int integer = course.getDay();
if ((integer < 1 && integer > 7) || course.getStart() > course.getEnd()) {
Toast.makeText(this, “星期几没写对,或课程结束时间比开始时间还早~~”, Toast.LENGTH_LONG).show();
} else {
switch (integer) {
case 1: day = (RelativeLayout) findViewById(R.id.monday);break;
case 2: day = (RelativeLayout) findViewById(R.id.tuesday);break;
case 3: day = (RelativeLayout) findViewById(R.id.wednesday);break;
case 4: day = (RelativeLayout) findViewById(R.id.thursday);break;
case 5: day = (RelativeLayout) findViewById(R.id.friday);break;
case 6: day = (RelativeLayout) findViewById(R.id.saturday);break;
case 7: day = (RelativeLayout) findViewById(R.id.weekday);break;
}
final View view = LayoutInflater.from(this).inflate(R.layout.course_card, null); //加载单个课程布局
view.setY(180 * (course.getStart()-1)); //设置开始高度,即第几节课开始
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams
(ViewGroup.LayoutParams.MATCH_PARENT,(course.getEnd()-course.getStart()+1)*180-2); //设置布局高度,即跨多少节课
view.setLayoutParams(params);
TextView text = view.findViewById(R.id.text_view);
text.setText(course.getCourseName() + “\n” + course.getTeacher() + “\n” + course.getClassRoom()); //显示课程名
day.addView(view);
//长按删除课程
view.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
view.setVisibility(View.GONE);//先隐藏
day.removeView(view);//再移除课程视图
SQLiteDatabase sqLiteDatabase = databaseHelper.getWritableDatabase();
sqLiteDatabase.execSQL(“delete from course where course_name = ?”, new String[] {course.getCourseName()});
return true;
}
});
}
}

我觉得核心的代码就这些了.

AndroidX和android.support对照表

android.support AndroidX
android.arch.core:common androidx.arch.core:core-common:2.0.0-rc01
android.arch.core:core androidx.arch.core:core:2.0.0-rc01
android.arch.core:core-testing androidx.arch.core:core-testing:2.0.0-rc01
android.arch.core:runtime androidx.arch.core:core-runtime:2.0.0-rc01
android.arch.lifecycle:common androidx.lifecycle:lifecycle-common:2.0.0-rc01
android.arch.lifecycle:common-java8 androidx.lifecycle:lifecycle-common-java8:2.0.0-rc01
android.arch.lifecycle:compiler androidx.lifecycle:lifecycle-compiler:2.0.0-rc01
android.arch.lifecycle:extensions androidx.lifecycle:lifecycle-extensions:2.0.0-rc01
android.arch.lifecycle:livedata androidx.lifecycle:lifecycle-livedata:2.0.0-rc01
android.arch.lifecycle:livedata-core androidx.lifecycle:lifecycle-livedata-core:2.0.0-rc01
android.arch.lifecycle:reactivestreams androidx.lifecycle:lifecycle-reactivestreams:2.0.0-rc01
android.arch.lifecycle:runtime androidx.lifecycle:lifecycle-runtime:2.0.0-rc01
android.arch.lifecycle:viewmodel androidx.lifecycle:lifecycle-viewmodel:2.0.0-rc01
android.arch.paging:common androidx.paging:paging-common:2.0.0-rc01
android.arch.paging:runtime androidx.paging:paging-runtime:2.0.0-rc01
android.arch.paging:rxjava2 androidx.paging:paging-rxjava2:2.0.0-rc01
android.arch.persistence.room:common androidx.room:room-common:2.0.0-rc01
android.arch.persistence.room:compiler androidx.room:room-compiler:2.0.0-rc01
android.arch.persistence.room:guava androidx.room:room-guava:2.0.0-rc01
android.arch.persistence.room:migration androidx.room:room-migration:2.0.0-rc01
android.arch.persistence.room:runtime androidx.room:room-runtime:2.0.0-rc01
android.arch.persistence.room:rxjava2 androidx.room:room-rxjava2:2.0.0-rc01
android.arch.persistence.room:testing androidx.room:room-testing:2.0.0-rc01
android.arch.persistence:db androidx.sqlite:sqlite:2.0.0-rc01
android.arch.persistence:db-framework androidx.sqlite:sqlite-framework:2.0.0-rc01
com.android.support.constraint:constraint-layout androidx.constraintlayout:constraintlayout:1.1.2
com.android.support.constraint:constraint-layout-solver androidx.constraintlayout:constraintlayout-solver:1.1.2
com.android.support.test.espresso.idling:idling-concurrent androidx.test.espresso.idling:idling-concurrent:3.1.0
com.android.support.test.espresso.idling:idling-net androidx.test.espresso.idling:idling-net:3.1.0
com.android.support.test.espresso:espresso-accessibility androidx.test.espresso:espresso-accessibility:3.1.0
com.android.support.test.espresso:espresso-contrib androidx.test.espresso:espresso-contrib:3.1.0
com.android.support.test.espresso:espresso-core androidx.test.espresso:espresso-core:3.1.0
com.android.support.test.espresso:espresso-idling-resource androidx.test.espresso:espresso-idling-resource:3.1.0
com.android.support.test.espresso:espresso-intents androidx.test.espresso:espresso-intents:3.1.0
com.android.support.test.espresso:espresso-remote androidx.test.espresso:espresso-remote:3.1.0
com.android.support.test.espresso:espresso-web androidx.test.espresso:espresso-web:3.1.0
com.android.support.test.janktesthelper:janktesthelper androidx.test.jank:janktesthelper:1.0.1
com.android.support.test.services:test-services androidx.test:test-services:1.1.0
com.android.support.test.uiautomator:uiautomator androidx.test.uiautomator:uiautomator:2.2.0
com.android.support.test:monitor androidx.test:monitor:1.1.0
com.android.support.test:orchestrator androidx.test:orchestrator:1.1.0
com.android.support.test:rules androidx.test:rules:1.1.0
com.android.support.test:runner androidx.test?1.1.0
com.android.support:animated-vector-drawable androidx.vectordrawable:vectordrawable-animated:1.0.0
com.android.support:appcompat-v7 androidx.appcompat:appcompat:1.0.0
com.android.support:asynclayoutinflater androidx.asynclayoutinflater:asynclayoutinflater:1.0.0
com.android.support:car androidx.car?1.0.0-alpha5
com.android.support:cardview-v7 androidx.cardview:cardview:1.0.0
com.android.support:collections androidx.collection:collection:1.0.0
com.android.support:coordinatorlayout androidx.coordinatorlayout:coordinatorlayout:1.0.0
com.android.support:cursoradapter androidx.cursoradapter:cursoradapter:1.0.0
com.android.support:customtabs androidx.browser:browser:1.0.0
com.android.support:customview androidx.customview:customview:1.0.0
com.android.support:design com.google.android.material:material:1.0.0-rc01
com.android.support:documentfile androidx.documentfile:documentfile:1.0.0
com.android.support:drawerlayout androidx.drawerlayout:drawerlayout:1.0.0
com.android.support:exifinterface androidx.exifinterface:exifinterface:1.0.0
com.android.support:gridlayout-v7 androidx.gridlayout:gridlayout:1.0.0
com.android.support:heifwriter androidx.heifwriter:heifwriter:1.0.0
com.android.support:interpolator androidx.interpolator:interpolator:1.0.0
com.android.support:leanback-v17 androidx.leanback:leanback:1.0.0
com.android.support:loader androidx.loader:loader:1.0.0
com.android.support:localbroadcastmanager androidx.localbroadcastmanager:localbroadcastmanager:1.0.0
com.android.support:media2 androidx.media2:media2:1.0.0-alpha03
com.android.support:media2-exoplayer androidx.media2:media2-exoplayer:1.0.0-alpha01
com.android.support:mediarouter-v7 androidx.mediarouter:mediarouter:1.0.0
com.android.support:multidex androidx.multidex:multidex:2.0.0
com.android.support:multidex-instrumentation androidx.multidex:multidex-instrumentation:2.0.0
com.android.support:palette-v7 androidx.palette:palette:1.0.0
com.android.support:percent androidx.percentlayout:percentlayout:1.0.0
com.android.support:preference-leanback-v17 androidx.leanback:leanback-preference:1.0.0
com.android.support:preference-v14 androidx.legacy:legacy-preference-v14:1.0.0
com.android.support:preference-v7 androidx.preference:preference:1.0.0
com.android.support:print androidx.print:print:1.0.0
com.android.support:recommendation androidx.recommendation:recommendation:1.0.0
com.android.support:recyclerview-selection androidx.recyclerview:recyclerview-selection:1.0.0
com.android.support:recyclerview-v7 androidx.recyclerview:recyclerview:1.0.0
com.android.support:slices-builders androidx.slice:slice-builders:1.0.0
com.android.support:slices-core androidx.slice:slice-core:1.0.0
com.android.support:slices-view androidx.slice:slice-view:1.0.0
com.android.support:slidingpanelayout androidx.slidingpanelayout:slidingpanelayout:1.0.0
com.android.support:support-annotations androidx.annotation:annotation:1.0.0
com.android.support:support-compat androidx.core:core:1.0.0
com.android.support:support-content androidx.contentpager:contentpager:1.0.0
com.android.support:support-core-ui androidx.legacy:legacy-support-core-ui:1.0.0
com.android.support:support-core-utils androidx.legacy:legacy-support-core-utils:1.0.0
com.android.support:support-dynamic-animation androidx.dynamicanimation:dynamicanimation:1.0.0
com.android.support:support-emoji androidx.emoji:emoji:1.0.0
com.android.support:support-emoji-appcompat androidx.emoji:emoji-appcompat:1.0.0
com.android.support:support-emoji-bundled androidx.emoji:emoji-bundled:1.0.0
com.android.support:support-fragment androidx.fragment:fragment:1.0.0
com.android.support:support-media-compat androidx.media:media:1.0.0
com.android.support:support-tv-provider androidx.tvprovider:tvprovider:1.0.0
com.android.support:support-v13 androidx.legacy:legacy-support-v13:1.0.0
com.android.support:support-v4 androidx.legacy:legacy-support-v4:1.0.0
com.android.support:support-vector-drawable androidx.vectordrawable:vectordrawable:1.0.0
com.android.support:swiperefreshlayout androidx.swiperefreshlayout:swiperefreshlayout:1.0.0
com.android.support:textclassifier androidx.textclassifier:textclassifier:1.0.0
com.android.support:transition androidx.transition:transition:1.0.0
com.android.support:versionedparcelable androidx.versionedparcelable:versionedparcelable:1.0.0
com.android.support:viewpager androidx.viewpager:viewpager:1.0.0
com.android.support:wear androidx.wear:wear:1.0.0
com.android.support:webkit androidx.webkit:webkit:1.0.0

 

Android表格控件动态生成表格

表格是很常用的控件,Android本身提供了TableLayout供布局实现。但本文介绍另外一种思路,用动态布局的方式实现,这种方式更灵活,内容、样式能高度扩展,熟练的人可随意运用到任何视图复用的场景。使用滚动条避免显示不完全问题。

效果图:

%title插图%num

核心代码如下:

public class DriveRecordAcivity extends Fragment{
private View view = null;
private LinearLayout wr_areas;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view=inflater.inflate(R.layout.driverecord_activity, null);
wr_areas=(LinearLayout) view.findViewById(R.id.wr_areas);
if(ApplicationUtils.getCheckedFcRecord(new DBAdapter(view.getContext()))!=null){
List<CheckedFcRecord> titleData=ApplicationUtils.getCheckedFcRecord(new DBAdapter(view.getContext()));
showData(titleData);
}
return view;
}

/**
* 发车数据动态添加状态
*/
private void showData(List<CheckedFcRecord> titleData) {
for (int i = 0; i < titleData.size(); i++) {
final CheckedFcRecord pojo = titleData.get(i);
LinearLayout llWashingRoomItem = new LinearLayout(view.getContext());
llWashingRoomItem.setLayoutParams(new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
llWashingRoomItem = (LinearLayout) getActivity().getLayoutInflater().inflate(R.layout.checkedfcrecord_template, null);
TextView time = (TextView) llWashingRoomItem.findViewById(R.id.time);
TextView vhclNo = (TextView) llWashingRoomItem.findViewById(R.id.vhclNo);
TextView jpy = (TextView) llWashingRoomItem.findViewById(R.id.jpy);
TextView ticket = (TextView) llWashingRoomItem.findViewById(R.id.ticket);
time.setText(DateTools.getStringFromDate(pojo.getFcTime(),null));
vhclNo.setText(pojo.getVhcl_no());
jpy.setText(pojo.getJsy_name());
//Integer类型需要转换用.toString()不然报错
ticket.setText(pojo.getJps().toString());
//动态设置layout_weight权重设置表格宽度
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1.6f);
time.setLayoutParams(lp);
lp = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f);
vhclNo.setLayoutParams(lp);
lp = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 0.8f);
jpy.setLayoutParams(lp);
lp = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 0.8f);
ticket.setLayoutParams(lp);
wr_areas.addView(llWashingRoomItem);
}
}
}
上面是运用到项目中,动态获取数据。测试源码中的是手动添加的数据。

xml布局如下:

<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:background=”@color/main_bg”
android:orientation=”vertical”
tools:context=”.WashingRoomMonitor” >
<RelativeLayout
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:gravity=”center” >
<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:padding=”10dp”
android:text=”测试记录”
android:textColor=”@color/white”
android:textSize=”@dimen/text_size_20″ />
</RelativeLayout>

<TextView
android:layout_width=”match_parent”
android:layout_height=”1dp”
android:background=”@color/split_line2″ />

<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:orientation=”horizontal” >

<TextView
android:text=”时区”
android:layout_width=”0dp”
android:layout_height=”wrap_content”
android:layout_weight=”1.5″
android:gravity=”center”
android:padding=”5dp”
android:textColor=”@color/white”/>

<TextView
android:layout_width=”1dp”
android:layout_height=”match_parent”
android:background=”@color/split_line2″ />

<TextView
android:text=”序号”
android:layout_width=”0dp”
android:layout_height=”wrap_content”
android:layout_weight=”1″
android:gravity=”center”
android:padding=”5dp”
android:textColor=”@color/white” />

<TextView
android:layout_width=”1dp”
android:layout_height=”match_parent”
android:background=”@color/split_line2″ />

<TextView
android:text=”人员”
android:layout_width=”0dp”
android:layout_height=”wrap_content”
android:layout_weight=”1″
android:gravity=”center”
android:padding=”5dp”
android:textColor=”@color/white” />

<TextView
android:layout_width=”1dp”
android:layout_height=”match_parent”
android:background=”@color/split_line2″ />

<TextView
android:text=”数量”
android:layout_width=”0dp”
android:layout_height=”wrap_content”
android:layout_weight=”1″
android:gravity=”center”
android:padding=”5dp”
android:textColor=”@color/white” />
</LinearLayout>

<TextView
android:layout_width=”match_parent”
android:layout_height=”1dp”
android:background=”@color/split_line2″ />

<ScrollView
android:layout_width=”match_parent”
android:layout_height=”wrap_content” >

<LinearLayout
android:id=”@+id/wr_areas”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:orientation=”vertical” >
</LinearLayout>
</ScrollView>

</LinearLayout>

%title插图%num
定义可重用的视图部分,xml文件如下:

<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:orientation=”vertical”
tools:context=”.MainActivity” >

<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:orientation=”horizontal” >

<TextView
android:id=”@+id/tv_wr_areaname”
android:text=”6″
android:layout_width=”0dp”
android:layout_height=”match_parent”
android:layout_weight=”1.5″
android:gravity=”center”
android:padding=”5dp”
android:textColor=”@color/white” />

<TextView
android:layout_width=”1dp”
android:layout_height=”match_parent”
android:background=”@color/split_line2″ />

<TextView
android:id=”@+id/tv_wr_mt_man”
android:layout_width=”0dp”
android:layout_height=”wrap_content”
android:layout_weight=”1″
android:gravity=”center”
android:padding=”5dp”
android:text=”6″
android:textColor=”@color/white” />

<TextView
android:layout_width=”1dp”
android:layout_height=”match_parent”
android:background=”@color/split_line2″ />

<TextView
android:id=”@+id/tv_wr_dc_man”
android:layout_width=”0dp”
android:layout_height=”wrap_content”
android:layout_weight=”1″
android:gravity=”center”
android:padding=”5dp”
android:text=”6″
android:textColor=”@color/white” />

<TextView
android:layout_width=”1dp”
android:layout_height=”match_parent”
android:background=”@color/split_line2″ />

<TextView
android:id=”@+id/tv_wr_xbc_man”
android:layout_width=”0dp”
android:layout_height=”wrap_content”
android:layout_weight=”1″
android:gravity=”center”
android:padding=”5dp”
android:text=”6″
android:textColor=”@color/white” />

<TextView
android:layout_width=”1dp”
android:layout_height=”match_parent”
android:background=”@color/split_line2″ />

</LinearLayout>

<TextView
android:layout_width=”match_parent”
android:layout_height=”1dp”
android:background=”@color/split_line2″ />

</LinearLayout>
%title插图%num

 

互联网企业降低云服务成本,需要考虑哪些因素

尽管云计算对于企业来说有很多好处,如增加灵活性,但公司转向云服务的*主要原因是为了帮助公司降低IT成本,但这并不意味着将应用程序转向云服务会自动缩减公司的成本。如果不采取适当措施,公司机构会发现他们在云服务上的花费会比原计划高。降低在云服务上花费的开销过多,需要考虑以下5个因素:

1、关注与风险有关的条款

尽管云计算供应商的合同通常都很友好,但企业也越来越擅长于与云服务提供商“讨价还价”,以降低自身成本或保护他们远离一些与云服务相关的风险(停机时间、云安全威胁等等),项目公司通常能够谈判的内容如下:补救措施,如数据丢失和停机时间;正常运行时间的可用性保证;安全性和隐私保护;终止权;在没有客户的批准下防止服务的变化;知识产权。

2、集中管理云供应

在传统的计算环境中,IT可以相对容易地控制花费支出——或者至少知道有多少钱花在了与科技相关的项目上。一般来说只有IT员工可以购买并安装设备和软件。但是随着云服务的发展、部门本身在没有来自于IT的输入下也可以提供服务。这就意味着公司可以签订很多的服务,并且可能会在因为没有适当的知识的情况下签署对自己不利的合同条款。公司应该有一个明确的过程可以让个人或小组去监督云配给。

3、精简你的云服务

云计算可以帮助公司节省钱的一个*大原因是,它可以允许公司根据他们的需要进行花费支出。例如,他们可以只支付他们所使用的存储花费,而不是要支出更多的钱为了支付在未来升级时所需要的存储花费。不过很多公司仍然在接触云服务时使用旧方法,在努力寻求业务扩展的过程中,就支付给云提供商超过其服务需求的费用,但并未意识到业务缩减的可能。许多专家的建议是在开始时采用小范围服务,等到后来有需求时再增加,而不是一开始的时候就选择顶配但相对过剩的资源。

4、小心隐藏的费用

在同意与云计算提供者签订合同之前,重要的是公司应该完全知道服务的成本,而不仅仅是基本的订阅费。例如,企业应该确定供应商要收取的费用:如备份、增加或减少服务等级、售后支持和维护等。企业还必须要意识到在转移到云计算之前可能不得不花一部分钱用在内部升级上。例如一些公司可能会需要额外的带宽,用来支持移动网络和外部的服务提供者之间的数据传送。

5、*小化-和中断计划

安全、可靠性可能是公司在使用云服务的过程中*受关注的问题。毕竟,企业在缩减IT服务时也要损失一部分钱,因此组织应该密切关注云供应商服务的可靠性,以避免在云故障中造成更多浪费。另外还需确保服务商的合同条款中存在处罚条例,这样可以保证在提供者没有达到安全条件时会受到惩罚。许多公司也选择避免完全使用云服务,尤其是在处理关键性的应用程序任务时。

尽管云服务*终的花费可能会超出公司的原定计划,但是总体应用成本仍然较传统IT更低,但为了避免不必要的成本浪费,企业在选购云服务时还需明智选择供应商并了解清楚以上问题。

选择云服务提供商时,需要考虑哪几个方面

在选择云服务提供商时,解决方案提供商将客户偏好,放在审查云服务提供商标准列表的*位。只有11%受访的解决方案提供商认为客户偏好不重要,可见,客户偏好具有很大的影响。但是,到底应不应该考虑客户偏好?

客户偏好是很重要,但是,除了客户偏好之外,还要考虑服务提供商合作伙伴的财务状况是否良好,其技术是否*适合你的客户等等。你要对所有潜在的云合作伙伴进行适当的审查,这样,一旦提供商提供的支持不符合要求、提供的技术言过其实的话,你就不会因此苦恼。当然,有些提供商在你签完合同后,过几个月可能就面临破产,如果你想要避开这样的提供商,更要进行适当的审查。
审查云服务提供商,以下是值得注意的五个方面,具体如下:
是否适合
解决方案与你的业务适合吗?你必须具备营销、技术和操作技能、还要有相应的资源,来支持技术——或者你具备获得这些技能和资源的渠道。除此之外,还要考虑这项技术是否能够满足你的客户群需求?解决方案提供商比任何人都清楚客户的需求,因此,你是*有资格做出判断的人。这有助于进行市场分析,确定现有客户是否将采用该解决方案,该解决方案是否会吸引新的客户,还能够确定竞争对手的进展。

财政稳定
想要预测云提供商能够维持业务多长时间,是非常困难的,特别是考虑到大多数云提供商属于新创公司,没有以往的业绩记录可以参考。公司在投资模式下可能并不盈利,但是,不要过于担心。了解公司规划,利用公司业务和时间表实现盈利。查看公司以往的财政里程碑,了解公司是否实现了目标。也要审查公司的主管们:基于他们过去的成功或者失败,确定他们是否是公司的得力助手?

合作伙伴的支持
云提供商拥有世界上*好的技术,但是,云提供商合作伙伴的支持情况并不理想,因此,一定要审查提供商提供哪些资源,可以帮助合作伙伴采用这种技术。必须要明确界定提供商和合作伙伴在提供服务方面的角色和职责。提供商必须制定从寻找潜在客户、销售、评估到登记、开发票和售后支持等所涉及到的所有步骤。还要审查提供商是否有明确的条款规定,来避免合作伙伴和直销之间发生冲突。

潜在利润
你必须要了解交货成本,才能够保证你的利润。如果提供商在服务交付方面起着很大的作用的话,相比于你完成大部分的工作,这必然会带来更大的利润空间。不过,如果提供商发挥更大作用的话,还可能会降低你的投资成本,并且会腾出一些资源,从而可以进行其他投资。同时,利润与机会成本息息相关:如果你选择一个特定提供商的解决方案的话,会影响你投资其他产品吗?一定要正确地均衡。这有助于构建一个两到三年的财政模型,从而能够明确机会,建立里程碑,确定服务产品的真正投资回报率。

安全
根据计算机技术工业协会可知,安全性是客户选择云服务提供商*重要的原因,因此,这也是审查过程的一部分。云提供商应该提供符合行业标准的安全认证,并且,你需要了解一些不利因素,还要懂得如何处理*坏的情况。

云计算安全,主要面临哪些威胁?

云计算是一种新的计算方式,它依托于互联网,以网络技术、分布式计算为基础,实现按需自服务、快速弹性构建、服务可测量等特点的新一代计算方式。然而,任何以互联网为基础的应用都存在着一定危险性,云计算也不例外,安全问题从云计算诞生那天开始就一直受人关注。

云安全
当所有的计算行为和数据存储都散布在聚散无形,虚无缥缈的云中的时候,人们将会普遍感到失控的恐慌。还记得《手机》这部电影吗?手机给生活提供了*大便利,但也给人带来了无尽烦恼,将人与人之间的距离拉得太近了,毫无隐私可言,让人都喘不过气来。

云计算可以说比手机还甚,云计算中的数据对于数据所有者以外的其它云计算用户是保密的,但是对于提供云计算的商业机构而言确是毫无秘密可言。当然,这还不是问题关键,人们收入和资金方面有隐私,但并不妨碍人们乐于将资金放到银行里管理,*为关键的还是云计算自身安全技术的问题。

云计算作为一种新计算技术,要面临更多安全威胁。云安全联盟是中立的非盈利世界性行业组织,致力于国际云计算安全的全面发展,也是云计算安全领域的权威组织。云计算的安全,主要面临以下威胁:

安全域无边界

在对外提供云计算业务之前,数据中心都是独立机房,由边界防火墙隔离成内外两块。防火墙内部属于可信区域,自己独占,外部属于不可信区域,所有的攻击者都在这里。安全人员只需要对这一道隔离墙加高、加厚即可保障安全,也可以在这道墙之后建立更多防火墙形成纵深防御;在开始提供云计算业务之后,这种简洁的内外隔离安全方案已经行不通了,通过购买云服务器,攻击者已经深入提供商网络的腹地,穿越了边界防火墙。

云计算内部的资源不再是由某个用户独享,而是几万、几十万甚至更多互相不认识的用户共有,当然也包含一些恶意用户,这些用户可以轻而易举进入云计算内部,窃取私密信息。传统划分安全域做隔离已经行不通了,哪里有云计算提供的服务,哪里就需要安全,安全防护要贯穿到整个云计算的所有环节,这无疑将提升云计算安全部署的成本,即便这样防御效果还不见比以前好。

虚拟化难捕捉

云计算时代,一台服务器上可以运行十台虚机,这些虚机还可能属于十个不同用户。攻击者虚拟机可直接运行在这台服务器的内存里面,仅仅是使用一个虚拟层隔离,一旦攻击者掌握了可以穿透虚拟层漏洞,就可以毫不费力完成入侵,常见的虚拟化层软件如XEN、KVM都能找到类似安全漏洞。

一般服务器是一台标准Linux服务器,运行着标准的Linux操作系统以及各种标准的服务,被攻击者攻破的通道很多。为了实现虚拟机自动迁移,虚拟化技术被应用于云计算网络中,这种虚拟网络是一个大二层架构,甚至是跨越机房、跨越城市的大二层架构。在这样一个超大的二层虚拟网络中,ARP欺骗、以太网端口欺骗、ARP风暴、NBNS风暴等二层内部的攻击问题,危害性都远远超过了它们在传统网络中的影响。

数据泄露量大

在传统网络中也存在数据泄露威胁,但在云计算环境中数据泄露严重性更高。由于云服务器上保存了大量客户的隐私数据,在多租户环境下,一个客户应用存在漏洞可能就会导致其他客户数据的泄露。

数据泄露不仅导致商业机密、知识产权和个人隐私的泄露,而且对企业而言可能产生毁灭性打击。云计算依托的基础就是海量数据,只有在超大型的数据中心才能充分发挥作用,而海量数据若发生泄露,造成的损失很大,尤其是各种数据混杂在一起,做不好数据防护,很容易被人所窃取。恶意黑客会使用病毒、木马或者直接攻击方法永久删除云端数据来危害云系统安全。

攻击频率急剧增大

正所谓“树大招风”,没有部署云计算时,承载信息服务的各个数据中心散列在各处,即便被攻击,受影响的面都不会太大。现在的数据中心建设规模都很大,因为只有规模上去,云计算的优势才能更加明显。

所以现在拥有数十万台服务器的数据中心屡见不鲜,这就将很多数据集中在一起,再交由云计算处理。拥有海量数据的数据中心,目标太大,很容易成为别人的目标。还有云计算用户多样性而且规模巨大,这样遭受的攻击频率也是急剧增大。以阿里云为例,平均每天遭受数百起DDoS攻击,其中50%攻击流量超过5GBit/s。

针对WEB的攻击以及密码破解攻击更是以亿计算。这种频度的攻击,给安全运维带来巨大的挑战。 除此之外,不安全的接口和界面、新的攻击方式、混乱的身份管理、高级持续性威胁、审查不充分、恶意SaaS应用、共享技术问题等等,都是云计算要面临的安全威胁。

解决好云计算安全威胁问题,将会为云计算的发展打下坚实的基础,让更多的人乐于接受云计算,将自己的信息数据安心放到云计算应用中来。在这里将云计算的安全威胁比作了集中营,就是希望放到集中营中的安全威胁越来越少,*后集中营能够解散掉,彻底消除各种云计算安全威胁。

云计算的建设者已经意识到解决安全问题的急迫性,已经提出了不少抵御安全威胁的解决方案,这些都有待实践来验证。这个过程一定还有很长的路要走,需要依赖技术、管理和产业等各行业的共同努力才能实现。

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速