[转载]Activity的四种启动模式和onNewIntent() - linghu_java的专栏 - 博客频道 - CSDN.NET

mikel阅读(996)

[转载]Activity的四种启动模式和onNewIntent() – linghu_java的专栏 – 博客频道 – CSDN.NET.

  在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作。在Android中Activity的启动模式决定了Activity的启动运行方式。

  Android总Activity的启动模式分为四种:

复制代码
Activity启动模式设置:

        <activity android:name=".MainActivity" android:launchMode="standard" />

Activity的四种启动模式:

    1. standard

        默认启动模式,每次激活Activity时都会创建Activity,并放入任务栈中。

    2. singleTop

        如果在任务的栈顶正好存在该Activity的实例, 就重用该实例,否者就会创建新的实例并放入栈顶(即使栈中已经存在该Activity实例,只要不在栈顶,都会创建实例)。

    3. singleTask

        如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的onNewIntent())。重用时,会让该实例回到栈顶,因此在它上面的实例将会被移除栈。如果栈中不存在该实例,将会创建新的实例放入栈中。 

    4. singleInstance

        在一个新栈中创建该Activity实例,并让多个应用共享改栈中的该Activity实例。一旦改模式的Activity的实例存在于某个栈中,任何应用再激活改Activity时都会重用该栈中的实例,其效果相当于多个应用程序共享一个应用,不管谁激活该Activity都会进入同一个应用中。
复制代码

       大家遇到一个应用的Activity供多种方式调用启动的情况,多个调用希望只有一个Activity的实例存在,这就需要Activity的 onNewIntent(Intent intent)方法了。只要在Activity中加入自己的onNewIntent(intent)的实现加上Manifest中对Activity设置 lanuchMode=“singleTask”就可以。

       onNewIntent()非常好用,Activity第一启动的时候执行 onCreate()—->onStart()—->onResume()等后续生命周期函数,也就时说第一次启动Activity 并不会执行到onNewIntent(). 而后面如果再有想启动Activity的时候,那就是执行onNewIntent()—->onResart()——>onStart()—–>onResume().  如果android系统由于内存不足把已存在Activity释放掉了,那么再次调用的时候会重新启动Activity即执行onCreate()—->onStart()—->onResume()等。

     当调用到onNewIntent(intent)的时候,需要在onNewIntent() 中使用setIntent(intent)赋值给Activity的Intent.否则,后续的getIntent()都是得到老的Intent。

[转载]onNewIntent调用时机 - zenfly - 博客园

mikel阅读(987)

[转载]onNewIntent调用时机 – zenfly – 博客园.

IntentActivity中重写下列方法:onCreate onStart onRestart  onResume  onPause onStop onDestroy  onNewIntent

 

一、其他应用发Intent,执行下列方法:
I/@@@philn(12410): onCreate
I/@@@philn(12410): onStart
I/@@@philn(12410): onResume

 

发Intent的方法:

 

Uri uri = Uri.parse("philn://blog.163.com");
Intent it = new Intent(Intent.ACTION_VIEW, uri);    
startActivity(it);

 

 

 

二、接收Intent声明: 

 

复制代码
<activity android:name=".IntentActivity" android:launchMode="singleTask"
                  android:label="@string/testname">
             <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="philn"/>
            </intent-filter>
  </activity>
复制代码

 

 

 

三、如果IntentActivity处于任务栈的顶端,也就是说之前打开过的Activity,现在处于
I/@@@philn(12410): onPause
I/@@@philn(12410): onStop 状态的话
其他应用再发送Intent的话,执行顺序为:
I/@@@philn(12410): onNewIntent
I/@@@philn(12410): onRestart
I/@@@philn(12410): onStart
I/@@@philn(12410): onResume

 

Android应用程序开发的时候,从一个Activity启动另一个Activity并传递一些数据到新的Activity上非常简单,但是当您需要让后台运行的Activity回到前台并传递一些数据可能就会存在一点点小问题。

 

首先,在默认情况下,当您通过Intent启到一个Activity的时候,就算 已经存在一个相同的正在运行的Activity,系统都会创建一个新的Activity实例并显示出来。为了不让Activity实例化多次,我们需要通 过在AndroidManifest.xml配置activity的加载方式(launchMode)以实现单任务模式,如下所示:

 

 

 

<activity android:label="@string/app_name" android:launchmode="singleTask"android:name="Activity1"></activity>

 

 

 

launchMode为singleTask的时候,通过Intent启到一个 Activity,如果系统已经存在一个实例,系统就会将请求发送到这个实例上,但这个时候,系统就不会再调用通常情况下我们处理请求数据的 onCreate方法,而是调用onNewIntent方法,如下所示:

 

复制代码
 protected void onNewIntent(Intent intent) {

   super.onNewIntent(intent);

   setIntent(intent);//must store the new intent unless getIntent() will return the old one

   processExtraData();

 }
复制代码

 


不要忘记,系统可能会随时杀掉后台运行的Activity,如果这一切发生,那么系统就会调用onCreate方法,而不调用onNewIntent方法,一个好的解决方法就是在onCreate和onNewIntent方法中调用同一个处理数据的方法,如下所示:

 

 

 

复制代码
 public void onCreate(Bundle savedInstanceState) {

   super.onCreate(savedInstanceState);

   setContentView(R.layout.main);

   processExtraData();

 }

 protected void onNewIntent(Intent intent) {

   super.onNewIntent(intent);

   setIntent(intent);//must store the new intent unless getIntent() will return the old one

   processExtraData()

 }

 private void processExtraData(){

   Intent intent = getIntent();

   //use the data received here

 }
复制代码

 

[转载]Android Fragment真正意义上的onResume和onPause - 紫气鸿蒙253 - 博客园

mikel阅读(1034)

[转载]Android Fragment真正意义上的onResume和onPause – 紫气鸿蒙253 – 博客园.

Fragment虽然有onResume和onPause的,但是这两个方法是Activity的方法,调用时机也是与Activity相同,和ViewPager搭配使用这个方法就很鸡肋了,根本不是你想要的效果,这里介绍一种方法。

覆写Fragment 的setUserVisibleHint方法即可:

复制代码
@Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
            //相当于Fragment的onResume
        } else {
            //相当于Fragment的onPause
        }
    }

代码说明:

通过阅读ViewPager和PageAdapter相关的代码,切换Fragment实际上就是通过设置setUserVisibleHint和 setMenuVisibility来实现的,调用这个方法时并不会释放掉Fragment(即不会执行onDestoryView)

复制代码

=========================更完美的封装应该是如下的==============================================

在Fragment里的setUserVisibleHint这个方法里。请看关于Fragment里这个方法的API文档(国内镜像地址:Fragment api):

1
2
3
4
Set a hint to the system about whether this fragment's UI is currently visible to the user. This hint defaults to true and is persistent across fragment instance state save and restore.
An app may set this to false to indicate that the fragment's UI is scrolled out of visibility or is otherwise not directly visible to the user. This may be used by the system to prioritize operations such as fragment lifecycle updates or loader ordering behavior.
Parameters
isVisibleToUser true if this fragment's UI is currently visible to the user (default), false if it is not.
1
2
该方法用于告诉系统,这个Fragment的UI是否是可见的。所以我们只需要继承Fragment并重写该方法,即可实现在fragment可见时才进行数据加载操作,即Fragment的懒加载。
代码如下:
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
/*
 * Date: 14-7-17
 * Project: Access-Control-V2
 */
package cn.irains.access_control_v2.common;
import Android.support.v4.app.Fragment;
/**
 * Author: msdx (645079761@qq.com)
 * Time: 14-7-17 下午5:46
 */
public abstract class LazyFragment extends Fragment {
    protected boolean isVisible;
    /**
     * 在这里实现Fragment数据的缓加载.
     * @param isVisibleToUser
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if(getUserVisibleHint()) {
            isVisible = true;
            onVisible();
        } else {
            isVisible = false;
            onInvisible();
        }
    }
    protected void onVisible(){
        lazyLoad();
    }
    protected abstract void lazyLoad();
    protected void onInvisible(){}
}

在LazyFragment,我增加了三个方法,一个是onVisiable,即fragment被设置为可见时调用,一个是 onInvisible,即fragment被设置为不可见时调用。另外再写了一个lazyLoad的抽象方法,该方法在onVisible里面调用。你 可能会想,为什么不在getUserVisibleHint里面就直接调用呢?

我这么写是为了代码的复用。因为在fragment中,我们还需要创建视图(onCreateView()方法),可能还需要在它不可见时就进行其 他小量的初始化操作(比如初始化需要通过AIDL调用的远程服务)等。而setUserVisibleHint是在onCreateView之前调用的, 那么在视图未初始化的时候,在lazyLoad当中就使用的话,就会有空指针的异常。而把lazyLoad抽离成一个方法,那么它的子类就可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class OpenResultFragment extends LazyFragment{
    // 标志位,标志已经初始化完成。
    private boolean isPrepared;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.d(LOG_TAG, "onCreateView");
        View view = inflater.inflate(R.layout.fragment_open_result, container, false);
        //XXX初始化view的各控件
    isPrepared = true;
        lazyLoad();
        return view;
    }
    @Override
    protected void lazyLoad() {
        if(!isPrepared || !isVisible) {
            return;
        }
        //填充各控件的数据
    }
}

在上面的类当中,我们增加了一个标志位isPrepared,用于标志是否初始化完成。然后在我们所需要的初始化操作完成之后调用,如上面的例子当 中,在初始化view之后,设置 isPrepared为true,同时调用lazyLoad()方法。而在lazyLoad()当中,判断isPrepared和isVisible只要 有一个不为true就不往下执行。也就是仅当初始化完成,并且可见的时候才继续加载,这样的避免了未初始化完成就使用而带来的问题。

在这里我对fragment的懒加载实现的介绍就到此为止,如果你有兴趣,可以基于此再深入探究,比如写一个带有缓初始化和可见时刷新的特性的Fragment。

 

参考:http://www.jcodecraeer.com/a/anzhuokaifa/Androidkaifa/2014/1021/1813.html

http://www.cnblogs.com/over140/p/3392164.html

时刻怀有一颗虔诚之心,乐于分享。知识才更有意义。

[转载]Android fill_parent、wrap_content和match_parent的区别_V刘一道_新浪博客

mikel阅读(860)

[转载]Android fill_parent、wrap_content和match_parent的区别_V刘一道_新浪博客.

 三个属性都用来适应视图的水平或垂直大小,一个以视图的内容或尺寸为基础的布局比精确地指定视图范围更加方便。

1)fill_parent

设置一个构件的布局为fill_parent将强制性地使构件扩展,以填充布局单元内尽可能多的空间。这跟Windows控件的dockstyle属性大体一致。设置一个顶部布局或控件为fill_parent将强制性让它布满整个屏幕。

2) wrap_content

设置一个视图的尺寸为wrap_content将强制性地使视图扩展以显示全部内容。以TextView和ImageView控件为例,设置为 wrap_content将完整显示其内部的文本和图像。布局元素将根据内容更改大小。设置一个视图的尺寸为wrap_content大体等同于设置 Windows控件的Autosize属性为True。

3)match_parent
  Android2.2中match_parent和fill_parent是一个意思 .两个参数意思一样,match_parent更贴切,于是从2.2开始两个词都可以用。那么如果考虑低版本的使用情况你就需要用fill_parent了

[转载]ASP.NET MVC+MQ+WinServices+Lucene.Net Demo - 陈炜1984 - 博客园

mikel阅读(1248)

[转载]MVC+MQ+WinServices+Lucene.Net Demo – 陈炜1984 – 博客园.

前言:

我之前没有接触过Lucene.Net相关的知识,最近在园子里看到很多大神在分享这块的内容,深受启发。秉着“实践出真知”的精神,再结合公司项目的实际情况,有了写一个Demo的想法,算是对自己能力的考验吧。

 

功能描述:

1. 前台网站把新增的索引项对象(标题、内容)序列化后,发送给MQ

2. MQ接收到消息后先持久化,再推送给消息的消费者

3. 消息的消费者(WinServices)接收到消息后,反序列化成索引项对象,调用SearchEngine类库的创建索引方法

4. 前台网站调用SearchEngine类库的查询方法,并传入用户输入的关键字,把查询后匹配的结果显示在View上

注:

1. 为了模拟多个用户同时新增索引项对象,互联网本身就是一个多线程的环境。这里使用了ActiveMQ的队列模式(另外还有主题模式,主要用于消息广播的场景),因为其内部维护了一个先进先出的队列,可以保证每次只能有一个消息被接收,所有其它待接收的都需要排队等待。

2. 这里引入了分布式项目的思想,前台网站只复制新增索引项和查询,MQ负责消息的接收和推送,WinServices负责生成索引文件。

3. 因为还只是Demo,所以很多功能还不完善,离真正企业级应用还有很大的差距,目的只是想练练手,熟悉下相关的知识点。

 

流程图:

 

架构图:

 

层次图:

 

项目结构:

LuceneTest.Entity:定义索引项和查询结果类的类库

LuceneTest.MQ:封装消息队列(ActiveMQ)发送和接收功能的类库

LuceneTest.Web:用于管理索引项和查询的MVC工程

LuceneTest.WinService.Test:用于WinService测试的WinForm工程

LuceneTest.SearchEngine:封装Lucene.Net的创建索引和根据关键字查询的类库

 

关键代码片段:

复制代码
 1         /// <summary>
 2         /// 创建索引
 3         /// </summary>
 4         /// <param name="model"></param>
 5         public void CreateIndex(IndexSet model)
 6         {
 7             //打开 索引文档保存位置
 8             var directory = FSDirectory.Open(new DirectoryInfo(this._indexPath), new NativeFSLockFactory());
 9             //IndexReader:对索引库进行读取的类
10             var isExist = IndexReader.IndexExists(directory);
11 
12             if (isExist)
13             {
14                 //如果索引目录被锁定(比如索引过程中程序异常退出或另一进程在操作索引库),则解锁
15                 if (IndexWriter.IsLocked(directory))
16                     //手动解锁
17                     IndexWriter.Unlock(directory);
18             }
19 
20             //创建向索引库写操作对象,IndexWriter(索引目录,指定使用盘古分词进行切词,最大写入长度限制)
21             //补充:使用IndexWriter打开directory时会自动对索引库文件上锁
22             var writer = new IndexWriter(directory, new PanGuAnalyzer(), !isExist, IndexWriter.MaxFieldLength.UNLIMITED);
23             //新建文档对象,一条记录对应索引库中的一个文档
24             var document = new Document();
25 
26             //向文档中添加字段
27             //所有字段的值都将以字符串类型保存,因为索引库只存储字符串类型数据
28 
29             //Field.Store:是否存储原文:
30             //Field.Store.YES:存储原值(如显示原内容必须为YES),可以用document.Get取出原值
31             //Field.Store.NO:不存储原值
32             //Field.Store.COMPRESS:压缩存储
33 
34             //Field.Index:是否创建索引:
35             //Field.Index.NOT_ANALYZED:不创建索引 
36             //Field.Index.ANALYZED:创建索引(利于检索)
37 
38             //WITH_POSITIONS_OFFSETS:指示不仅保存分割后的词,还保存词之间的距离
39             document.Add(new Field("title", model.Title, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS));
40             document.Add(new Field("content", model.Content, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS));
41 
42             //文档写入索引库
43             writer.AddDocument(document);
44 
45             //会自动解锁
46             writer.Close();
47             //不要忘了Close,否则索引结果搜不到
48             directory.Close();
49         }
复制代码
复制代码
        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="keyWord"></param>
        /// <returns></returns>
        public List<SearchResult> Search(string keyWord)
        {
            var searchResultList = new List<SearchResult>();

            //打开 索引文档保存位置
            var directory = FSDirectory.Open(new DirectoryInfo(this._indexPath), new NoLockFactory());
            //IndexReader:对索引库进行读取的类
            var reader = IndexReader.Open(directory, true);

            //关键词分词
            var words = this.SplitWords(keyWord);
            //搜索条件
            var query = new PhraseQuery();

            foreach (var item in words)
            {
                query.Add(new Term("content", item));
            }

            //指定关键词相隔最大距离
            query.SetSlop(100);

            //TopScoreDocCollector:存放查询结果的容器
            var collector = TopScoreDocCollector.create(1000, true);

            //IndexReader:对索引库进行查询的类
            var searcher = new IndexSearcher(reader);
            //根据query查询条件进行查询,查询结果放入collector容器
            searcher.Search(query, null, collector);

            //TopDocs:指定0到GetTotalHits(),即所有查询结果中的文档,如果TopDocs(20,10)则意味着获取第20-30之间文档内容,达到分页的效果
            var docs = collector.TopDocs(0, collector.GetTotalHits()).scoreDocs;

            foreach (var item in docs)
            {
                var searchResult = new SearchResult();

                //得到查询结果文档的id(Lucene内部分配的id)
                var docId = item.doc;
                //根据文档id来获得文档对象Document
                var doc = searcher.Doc(docId);

                searchResult.Id = docId;
                searchResult.Title = doc.Get("title");
                //高亮显示
                searchResult.Content = this.HightLight(keyWord, doc.Get("content"));

                searchResultList.Add(searchResult);
            }

            return searchResultList;
        }
复制代码

查询页面View

复制代码
@{
    ViewBag.Title = "Search";
}

<h2>Search List</h2>

@using (Html.BeginForm("Search", "IndexMgr"))
{
    <div>
        关键字:
    </div>
    <div>
        @Html.TextBox("keyWord")
    </div>   
    
    <input type="submit" value="保存" />
}

@{
    var list = this.ViewBag.SearchResultList;

    if (list != null)
    {
        foreach (var item in list)
        {
    @Html.Raw("标题:" + item.Title)
    <br />
    @Html.Raw("内容:" + item.Content)
    <hr />
        }
    }
}
复制代码

 

注意事项:

1. 如果使用盘古分词算法,以下文件的“复制到输出目录”需要选择“如果较新则复制”

2. 本Demo的索引文件保存在WinServices的可执行目录(bin\Debug\IndexData)下面,所以前台网站要查询,需要配置索引文件的路径。

 

运行效果图: 

1. 新增索引项

2. 查询

 

参考文献:

http://www.cnblogs.com/jiekzou/p/4364780.html

http://www.cnblogs.com/piziyimao/archive/2013/01/31/2887072.html

[转载]OData – the best way to REST–实例讲解ASP.NET WebAPI OData (V4) Service & Client - 深蓝医生 - 博客园

mikel阅读(1308)

[转载]OData – the best way to REST–实例讲解ASP.NET WebAPI OData (V4) Service & Client – 深蓝医生 – 博客园.

一、概念介绍

1.1,什么是OData?

还是看OData官网的简单说明:

An open protocol to allow the creation and consumption of queryable and interoperable RESTful APIs in a simple and standard way.

这是一个开放的数据查询和服务协议,目前已经有众多厂商和平台支持,已经形成了完整的生态链,这应该是未来数据查询的标准,参见官网说明

OData的意义还在于,它能够大大简小SOA架构里面服务的粒度,只需要提供一个OData数据源,而查询工作交给客户端去做即可,这将大大减少服务端服务方法定义的数量。

OData的版本现在已经是V4了,之前的很多类库都是基于V1-V3版本的。现在的V4版本已经很完善了,而且成为了工业标准,所以现在可以放心的在项目中使用了。

1.2,OData on .NET

OData 的前身是WCF Data Service,后来演变成跨平台的数据查询协议,现在,除了WCF支持OData,ASP.NET WebAPI 已经内置支持OData了,这将获得一种轻量级的,Rest架构的OData访问方案,本文将讲解如何在VS 2013上搭建一个OData 服务和客户端程序。

1.3,参考资源

在阅读本文之前,首先建议你参考下面的OData 学习资源,本文也是根据这里的资源整理而成,之所以要重新整理一次,是因为原文说的并不清楚,给出的示例程序有些小小的问题,而且国内有关OData的文章介绍非常少。

http://www.odata.org/blog/how-to-use-web-api-odata-to-build-an-odata-v4-service-without-entity-framework/
http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/create-an-odata-v4-endpoint
http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/create-an-odata-v4-client-app

本文的实例程序相关代码请在此下载。

二、OData WebAPI搭建

2.1,创建项目

新建一个ASP.NET WebAPI 项目,名字是 ODataWebApplication ,如下图:

OData1_20150331222025

注意选择一个空项目,并购选WebAPI,单击确定。

2.2,添加Asp.Net OData 支持

打开VS2013的程序包管理器控制台,在下面输入 Install-Package Microsoft.AspNet.OData 命令,如下图:

OData2_20150331223057

这里会添加很多附属的程序集文件,下面是一个详细的清单列表,如果你的符合下面的内容,就表示安装成功了:

复制代码
键入“get-help NuGet”以查看所有可用的 NuGet 命令。

PM> Install-Package Microsoft.AspNet.OData 
正在尝试解析依赖项“Microsoft.AspNet.WebApi.Client (≥ 5.2.2)”。 
正在尝试解析依赖项“Newtonsoft.Json (≥ 6.0.4)”。 
正在尝试解析依赖项“Microsoft.AspNet.WebApi.Core (≥ 5.2.2 && < 5.3.0)”。 
正在尝试解析依赖项“Microsoft.OData.Core (≥ 6.10.0 && < 7.0.0)”。 
正在尝试解析依赖项“Microsoft.Spatial (= 6.10.0)”。 
正在尝试解析依赖项“Microsoft.OData.Edm (= 6.10.0)”。 
正在安装“Newtonsoft.Json 6.0.8”。 
已成功安装“Newtonsoft.Json 6.0.8”。 
正在安装“Microsoft.AspNet.WebApi.Client 5.2.3”。 
您正在从 Microsoft 下载 Microsoft.AspNet.WebApi.Client,有关此程序包的许可协议在 http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm 上提供。请检查此程序包是否有其他依赖项,这些依赖项可能带有各自的许可协议。您若使用程序包及依赖项,即构成您接受其许可协议。如果您不接受这些许可协议,请从您的设备中删除相关组件。 
已成功安装“Microsoft.AspNet.WebApi.Client 5.2.3”。 
正在安装“Microsoft.AspNet.WebApi.Core 5.2.3”。 
您正在从 Microsoft 下载 Microsoft.AspNet.WebApi.Core,有关此程序包的许可协议在 http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm 上提供。请检查此程序包是否有其他依赖项,这些依赖项可能带有各自的许可协议。您若使用程序包及依赖项,即构成您接受其许可协议。如果您不接受这些许可协议,请从您的设备中删除相关组件。 
已成功安装“Microsoft.AspNet.WebApi.Core 5.2.3”。 
正在安装“Microsoft.Spatial 6.10.0”。 
您正在从 Microsoft Corporation 下载 Microsoft.Spatial,有关此程序包的许可协议在 http://go.microsoft.com/?linkid=9809688 上提供。请检查此程序包是否有其他依赖项,这些依赖项可能带有各自的许可协议。您若使用程序包及依赖项,即构成您接受其许可协议。如果您不接受这些许可协议,请从您的设备中删除相关组件。 
已成功安装“Microsoft.Spatial 6.10.0”。 
正在安装“Microsoft.OData.Edm 6.10.0”。 
您正在从 Microsoft Corporation 下载 Microsoft.OData.Edm,有关此程序包的许可协议在 http://go.microsoft.com/?linkid=9809688 上提供。请检查此程序包是否有其他依赖项,这些依赖项可能带有各自的许可协议。您若使用程序包及依赖项,即构成您接受其许可协议。如果您不接受这些许可协议,请从您的设备中删除相关组件。 
已成功安装“Microsoft.OData.Edm 6.10.0”。 
正在安装“Microsoft.OData.Core 6.10.0”。 
您正在从 Microsoft Corporation 下载 Microsoft.OData.Core,有关此程序包的许可协议在 http://go.microsoft.com/?linkid=9809688 上提供。请检查此程序包是否有其他依赖项,这些依赖项可能带有各自的许可协议。您若使用程序包及依赖项,即构成您接受其许可协议。如果您不接受这些许可协议,请从您的设备中删除相关组件。 
已成功安装“Microsoft.OData.Core 6.10.0”。 
正在安装“Microsoft.AspNet.OData 5.5.0”。 
您正在从 Microsoft 下载 Microsoft.AspNet.OData,有关此程序包的许可协议在 http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm 上提供。请检查此程序包是否有其他依赖项,这些依赖项可能带有各自的许可协议。您若使用程序包及依赖项,即构成您接受其许可协议。如果您不接受这些许可协议,请从您的设备中删除相关组件。 
已成功安装“Microsoft.AspNet.OData 5.5.0”。 
正在从 ODataWebApplication 删除“Microsoft.AspNet.WebApi.Client.zh-Hans 5.0.0”。 
已成功将“Microsoft.AspNet.WebApi.Client.zh-Hans 5.0.0”从 ODataWebApplication 中删除。 
正在从 ODataWebApplication 删除“Microsoft.AspNet.WebApi.Core.zh-Hans 5.0.0”。 
已成功将“Microsoft.AspNet.WebApi.Core.zh-Hans 5.0.0”从 ODataWebApplication 中删除。 
正在从 ODataWebApplication 删除“Microsoft.AspNet.WebApi.Client 5.0.0”。 
已成功将“Microsoft.AspNet.WebApi.Client 5.0.0”从 ODataWebApplication 中删除。 
正在从 ODataWebApplication 删除“Newtonsoft.Json 5.0.6”。 
已成功将“Newtonsoft.Json 5.0.6”从 ODataWebApplication 中删除。 
正在将“Newtonsoft.Json 6.0.8”添加到 ODataWebApplication。 
已成功将“Newtonsoft.Json 6.0.8”添加到 ODataWebApplication。 
正在将“Microsoft.AspNet.WebApi.Client 5.2.3”添加到 ODataWebApplication。 
已成功将“Microsoft.AspNet.WebApi.Client 5.2.3”添加到 ODataWebApplication。 
正在从 ODataWebApplication 删除“Microsoft.AspNet.WebApi.Core 5.0.0”。 
已成功将“Microsoft.AspNet.WebApi.Core 5.0.0”从 ODataWebApplication 中删除。 
正在将“Microsoft.AspNet.WebApi.Core 5.2.3”添加到 ODataWebApplication。 
已成功将“Microsoft.AspNet.WebApi.Core 5.2.3”添加到 ODataWebApplication。 
正在将“Microsoft.Spatial 6.10.0”添加到 ODataWebApplication。 
已成功将“Microsoft.Spatial 6.10.0”添加到 ODataWebApplication。 
正在将“Microsoft.OData.Edm 6.10.0”添加到 ODataWebApplication。 
已成功将“Microsoft.OData.Edm 6.10.0”添加到 ODataWebApplication。 
正在将“Microsoft.OData.Core 6.10.0”添加到 ODataWebApplication。 
已成功将“Microsoft.OData.Core 6.10.0”添加到 ODataWebApplication。 
正在将“Microsoft.AspNet.OData 5.5.0”添加到 ODataWebApplication。 
已成功将“Microsoft.AspNet.OData 5.5.0”添加到 ODataWebApplication。 
正在将“Microsoft.AspNet.WebApi.Client.zh-Hans 5.2.3”添加到 ODataWebApplication。 
正在安装“Microsoft.AspNet.WebApi.Client.zh-Hans 5.2.3”。 
您正在从 Microsoft 下载 Microsoft.AspNet.WebApi.Client.zh-Hans,有关此程序包的许可协议在 http://www.microsoft.com/web/webpi/eula/net_library_eula_CHS.htm 上提供。请检查此程序包是否有其他依赖项,这些依赖项可能带有各自的许可协议。您若使用程序包及依赖项,即构成您接受其许可协议。如果您不接受这些许可协议,请从您的设备中删除相关组件。 
已成功安装“Microsoft.AspNet.WebApi.Client.zh-Hans 5.2.3”。 
已成功将“Microsoft.AspNet.WebApi.Client.zh-Hans 5.2.3”添加到 ODataWebApplication。 
正在将“Microsoft.AspNet.WebApi.Core.zh-Hans 5.2.3”添加到 ODataWebApplication。 
正在安装“Microsoft.AspNet.WebApi.Core.zh-Hans 5.2.3”。 
您正在从 Microsoft 下载 Microsoft.AspNet.WebApi.Core.zh-Hans,有关此程序包的许可协议在 http://www.microsoft.com/web/webpi/eula/net_library_eula_CHS.htm 上提供。请检查此程序包是否有其他依赖项,这些依赖项可能带有各自的许可协议。您若使用程序包及依赖项,即构成您接受其许可协议。如果您不接受这些许可协议,请从您的设备中删除相关组件。 
已成功安装“Microsoft.AspNet.WebApi.Core.zh-Hans 5.2.3”。 
已成功将“Microsoft.AspNet.WebApi.Core.zh-Hans 5.2.3”添加到 ODataWebApplication。 
正在卸载“Microsoft.AspNet.WebApi.Client.zh-Hans 5.0.0”。 
已成功卸载“Microsoft.AspNet.WebApi.Client.zh-Hans 5.0.0”。 
正在卸载“Microsoft.AspNet.WebApi.Core.zh-Hans 5.0.0”。 
已成功卸载“Microsoft.AspNet.WebApi.Core.zh-Hans 5.0.0”。 
正在卸载“Microsoft.AspNet.WebApi.Client 5.0.0”。 
已成功卸载“Microsoft.AspNet.WebApi.Client 5.0.0”。 
正在卸载“Newtonsoft.Json 5.0.6”。 
已成功卸载“Newtonsoft.Json 5.0.6”。 
正在卸载“Microsoft.AspNet.WebApi.Core 5.0.0”。 
已成功卸载“Microsoft.AspNet.WebApi.Core 5.0.0”。

PM>
复制代码

 

2.3,添加Model和控制器

按照  http://www.odata.org/blog/how-to-use-web-api-odata-to-build-an-odata-v4-service-without-entity-framework/ 这个链接内容的文章,添加Model和控制器,具体过程请参考原文。在本篇文章的实例中,为了更好的重用Model,我将它放到了一个独立的Demo.Models 项目中。

注意,添加控制器的时候选择空的 WebAPI 控制器,不要选择带OData 的。

OData3_20150331223628

下面是添加完整后的项目目录结构:

OData4_20150331223628

2.4,解决程序集冲突

立刻运行这个项目,发现报下面的错误:

未能加载文件或程序集“System.Web.Http, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”或它的某一个依赖项。找到的程序集清单定义与程序集引用不匹配。 (异常来自 HRESULT:0x80131040)

此时需要在Web.config 文件中加入下面的配置内容:

复制代码
<system.web> 
    <compilation debug="true" targetFramework="4.5"/> 
    <httpRuntime targetFramework="4.5"/> 
  </system.web> 
  <runtime> 
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> 
            <dependentAssembly> 
                <assemblyIdentity name="System.Web.Http" publicKeyToken="31BF3856AD364E35" culture="neutral"/> 
                <bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0"/> 
            </dependentAssembly> 
            <dependentAssembly> 
                <assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31BF3856AD364E35" culture="neutral"/> 
                <bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0"/> 
            </dependentAssembly> 
        </assemblyBinding> 
    </runtime>
复制代码

也可以在编译项目的时候,注意查看“输出窗口”,单击“警告”的文字内容,会有下面的提示询问,回答确定即可自动为你添加上面的内容:

OData5_20150331225121

 

2.5,正确的OData 服务程序

再次运行,程序不报错了,用谷歌浏览器来打开本程序,出现了下面的内容,就表示ASP.NET WebAPI OData V4 已经成功了:

复制代码
{
  "@odata.context":"http://localhost:20491/$metadata#People","value":[
    {
      "ID":"001","Name":"Angel","Description":null
    },{
      "ID":"002","Name":"Clyde","Description":"Contrary to popular belief, Lorem Ipsum is not simply random text."
    },{
      "ID":"003","Name":"Elaine","Description":"It has roots in a piece of classical Latin literature from 45 BC, making Lorems over 2000 years old."
    }
  ]
}
复制代码

 

三、添加OData客户端

3.1,添加OData 控制台程序

按照下面链接文章的内容,新建一个控制台程序:

http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/create-an-odata-v4-client-app

3.2,为VS添加OData客户端工具

按照上面链接文章的内容,添加此工具,如下图:

OData8_20150331231126

下载好该工具后让你安装运行,最后会让你重启动VS。

3.3,添加OData客户端T4程序

重新启动后,如果看到下面的内容,表示该工具安装成功了。

OData9_20150331232041

该工具运行后,会在项目下面添加一个OData Client T4 文件,添加后,运行该项目,会报下面的错误:

复制代码
错误    1    正在运行转换: System.ArgumentException: The value "" is not a valid MetadataDocumentUri because is it not a valid absolute Uri. The MetadataDocumentUri must be set to an absolute Uri referencing the $metadata endpoint of an OData service. 
   在 Microsoft.VisualStudio.TextTemplating0CFD4DFE54AE767D51F4D19A4964CA0B33E6E87CF53FFF790646D6E3F14916C4544A62E4C781ABB55BC19EE5225FDBA7E28D9767E56040F8F9A9EF5278D6B7CA.GeneratedTextTransformation.set_MetadataDocumentUri(String value) 位置 c:\Users\dth\Documents\Visual Studio 2013\Projects\ODataWebApplication\Demo.ConsoleClient\ODataClient1.ttinclude:行号 125 
   在 Microsoft.VisualStudio.TextTemplating0CFD4DFE54AE767D51F4D19A4964CA0B33E6E87CF53FFF790646D6E3F14916C4544A62E4C781ABB55BC19EE5225FDBA7E28D9767E56040F8F9A9EF5278D6B7CA.GeneratedTextTransformation.ApplyParametersFromConfigurationClass() 位置 c:\Users\dth\Documents\Visual Studio 2013\Projects\ODataWebApplication\Demo.ConsoleClient\ODataClient1.ttinclude:行号 313 
   在 Microsoft.VisualStudio.TextTemplating0CFD4DFE54AE767D51F4D19A4964CA0B33E6E87CF53FFF790646D6E3F14916C4544A62E4C781ABB55BC19EE5225FDBA7E28D9767E56040F8F9A9EF5278D6B7CA.GeneratedTextTransformation.TransformText() 位置 c:\Users\dth\Documents\Visual Studio 2013\Projects\ODataWebApplication\Demo.ConsoleClient\ODataClient1.ttinclude:行号 58    c:\Users\dth\Documents\Visual Studio 2013\Projects\ODataWebApplication\Demo.ConsoleClient\ODataClient1.ttinclude    125    1    Demo.ConsoleClient
复制代码

3.4,配置OData Client T4 信息

根据错误信息,找到T4文件错误的位置,将前面的OData WebAPI项目的地址,写在文件里面,如下所示:

// The URI of the metadata document. The value must be set to a valid service document URI or a local file path 
    // eg : "http://services.odata.org/V4/OData/OData.svc/", "File:///C:/Odata.edmx", or @"C:\Odata.edmx" 
    // ### Notice ### If the OData service requires authentication for accessing the metadata document, the value of 
    // MetadataDocumentUri has to be set to a local file path, or the client code generation process will fail. 
    public const string MetadataDocumentUri = "http://localhost:20491/";

3.5,生成OData Client 代理类

此时再次运行该T4文件,我们发现OData Client 代理类文件生成了,内容很多,这里就不贴了。

3.6,使用OData Client 代理类

添加下面的代码,调用OData Client 代理类并运行:

复制代码
class Program 
    { 
        static void Main(string[] args) 
        { 
            // TODO: Replace with your local URI. 
            string serviceUri = "http://localhost:20491/"; 
            var container = new DefaultContainer(new Uri(serviceUri));

            foreach (var p in container.People) 
            { 
                Console.WriteLine("{0} {1} {2}", p.ID, p.Name, p.Description); 
            }

            Console.Read(); 
        } 
    }
复制代码

如果看到下面的运行结果,表示OData Client 程序成功了:

OData10_20150331233137

 

至此,OData WebAPI Serivce & Client 的工作就全部完成了。

四、不使用OData客户端工具访问OData 服务

4.1,封装OData Client 类库

经过前面的过程我们看到,依托于OData 客户端工具生成OData 代理类的过程还是比较麻烦的,当然好处也有,但缺点就是没有手工操控的那么灵活自由。

仔细研究下前面的代理类,我们发现这里关键依赖于  Microsoft.OData.Client 程序集的DataServiceContext 对象,将代理类进行抽取封装就可以完成我们手工的代理类了。

创建一个类库项目,新建一个 ODataV4ContextBase.cs 文件,

OData_sln_20150401160425

接着为该项目添加Nuget 依赖的包:

Install-Package Microsoft.OData.Client

然后在项目下增加了一个文件 packages.config,里面有如下内容:

复制代码
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Microsoft.OData.Client" version="6.11.0" targetFramework="net40" />
  <package id="Microsoft.OData.Core" version="6.11.0" targetFramework="net40" />
  <package id="Microsoft.OData.Edm" version="6.11.0" targetFramework="net40" />
  <package id="Microsoft.Spatial" version="6.11.0" targetFramework="net40" />
</packages>
复制代码

然后,编写ODataV4ContextBase 类的具体内容:

复制代码
using Microsoft.OData.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PWMIS.OData.Client
{
    /// <summary>
    /// OData V4 Version ASP.NET WebAPI OData RestFull Client Context
    /// <remarks>v1.0 2015.4.1 http://www.pwmis.com/sqlmap </remarks>
    /// </summary>
    public class ODataV4ContextBase : DataServiceContext
    {
        /// <summary>
        /// V4 OData Init
        /// </summary>
        /// <param name="serviceRoot">V4 OData ASP.NET WebAPI url base</param>
        public ODataV4ContextBase(string  serviceRoot)
            : base(new System.Uri( serviceRoot), ODataProtocolVersion.V4)
        {
            if (!serviceRoot.EndsWith("/"))
                serviceRoot = serviceRoot + "/";
            GeneratedEdmModel gem = new GeneratedEdmModel(serviceRoot);
            this.Format.LoadServiceModel = gem.GetEdmModel;
            this.Format.UseJson();
        }

        public IQueryable<T> CreateNewQuery<T>(string name) where T : class
        {
            return base.CreateQuery<T>(name);
        }

        class GeneratedEdmModel
        {
            private string ServiceRootUrl;
            public GeneratedEdmModel(string serviceRootUrl)
            {
                this.ServiceRootUrl = serviceRootUrl;
            }

            public Microsoft.OData.Edm.IEdmModel GetEdmModel()
            {
                string metadataUrl = ServiceRootUrl + "$metadata";
                return LoadModelFromUrl(metadataUrl);
            }

            private Microsoft.OData.Edm.IEdmModel LoadModelFromUrl(string metadataUrl)
            {
                System.Xml.XmlReader reader = CreateXmlReaderFromUrl(metadataUrl);
                try
                {
                    return Microsoft.OData.Edm.Csdl.EdmxReader.Parse(reader);
                }
                finally
                {
                    ((System.IDisposable)(reader)).Dispose();
                }
            }

            private static System.Xml.XmlReader CreateXmlReaderFromUrl(string inputUri)
            {
                return System.Xml.XmlReader.Create(inputUri);
            }
        }
    }
}
复制代码

4.2 编写ODataClient客户端

在解决方案里面添加一个WinForm项目,在项目里面添加一个ODataContainer.cs 文件,内容如下:

复制代码
class ODataContainer : PWMIS.OData.Client.ODataV4ContextBase
    {
        public ODataContainer(string serviceRoot):base(serviceRoot)
        {
           
        }

        public IQueryable<Person> People
        {
            get
            {
                return base.CreateNewQuery<Person>("People");
            } 
                
        }
    }
复制代码

非常的简单,这里只是添加了一个属性 People。

然后,在窗体代码中调用:

复制代码
private void button1_Click(object sender, EventArgs e)
        {
            string serviceUri = "http://localhost:20491/";
            var container = new ODataContainer(serviceUri);
            var query = container.People.Where(p => p.Description!=null);
            this.dataGridView1.DataSource = query.ToList();
        }
复制代码

最后运行该程序,出现下面的界面,就表示成功了:

OData_Win_0150401160319

 

至此,一个不依赖于EF的全内存的OData 应用程序就完全做好了,更多OData的研究,请大家一起来做吧。

感谢支持 PDF.NET SOD框架,相关代码请在此下载。

[转载]设计模式原则之里氏替换原则 - 偶my耶 - 博客园

mikel阅读(993)

[转载]设计模式原则之里氏替换原则 – 偶my耶 – 博客园.

里氏替换原则,OCP作为OO的高层原则,主张使用“抽象(Abstraction)”和“多态(Polymorphism)”将设计中的静态结构改为动态结构,维持设计的封闭性。“抽象”是语言提供的功能。“多态”由继承语义实现。

定义1:如果对每一个类型为T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。

定义2:所有引用基类的地方必须能透明地使用其子类的对象。

如果你觉得定义说的模糊了点,不太清楚,没关系,我们慢慢说明白。里氏替换原则的另一个简短的定义是“所有引用基类的地方必须能透明地使用其子类的对 象”。这个可能更清楚点。如果你熟悉的掌握一门面向对象的语言,你应该都可以明白面向对象的继承,子类继承自父类的话,自然的就会继承父类的所有方法(当 然前提是父类不要把方法声明为private)。

在面向对象中,继承有很多优点:

1代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性;

2提高代码的重用性;

3子类可以形似父类,但又异于父类,“龙生龙,凤生凤,老鼠生来会打洞”是说子拥有父的“种”,“世界上没有两片完全相同的叶子”是指明子与父的不同;

4提高代码的可扩展性,实现父类的方法就可以“为所欲为”了,君不见很多开源框架的扩展接口都是通过继承父类来完成的;

5提高产品或项目的开放性。

而里氏替换原则希望,你在写一个类继承自原有的类的同时,尽量不要去更改原有的方法。话说那么多,那么肯定会有疑问。

问题由来:有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。

解决方案:当使用继承时,遵循里氏替换原则。类B继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法。

继承包含这样一层含义:父类中凡是已经实现好的方法(相对于抽象方法而言),实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些 契约,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。而里氏替换原则就是表达了这一层含义。

继承作为面向对象三大特性之一,在给程序设计带来巨大便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加了对象间的耦 合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能会产生故障。

举例说明继承的风险,我们需要完成一个两数相减的功能,由类A来负责。

复制代码
class A{
    public int func1(int a, int b){
        return a-b;
    }
}

public class Client{
    public static void main(String[] args){
        A a = new A();
        System.out.println("100-50="+a.func1(100, 50));
        System.out.println("100-80="+a.func1(100, 80));
    }
}
复制代码

运行结果:

100-50=50
100-80=20

后来,我们需要增加一个新的功能:完成两数相加,然后再与100求和,由类B来负责。即类B需要完成两个功能:

  • 两数相减。
  • 两数相加,然后再加100。

由于类A已经实现了第一个功能,所以类B继承类A后,只需要再完成第二个功能就可以了,代码如下:

复制代码
class B extends A{
    public int func1(int a, int b){
        return a+b;
    }
    
    public int func2(int a, int b){
        return func1(a,b)+100;
    }
}

public class Client{
    public static void main(String[] args){
        B b = new B();
        System.out.println("100-50="+b.func1(100, 50));
        System.out.println("100-80="+b.func1(100, 80));
        System.out.println("100+20+100="+b.func2(100, 20));
    }
}
复制代码

类B完成后,运行结果:

100-50=150
100-80=180
100+20+100=220

我们发现原本运行正常的相减功能发生了错误。原因就是类B在给方法起名时无意中重写了父类的方法,造成所有运行相减功能的代码全部调用了类B重写 后的方法,造成原本运行正常的功能出现了错误。在本例中,引用基类A完成的功能,换成子类B之后,发生了异常。在实际编程中,我们常常会通过重写父类的方 法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的几率非常大。如果非要重写父类的 方法,比较通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合,组合等关系代替。

        里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。它包含以下4层含义:

  • 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
  • 子类中可以增加自己特有的方法。
  • 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
  • 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

看上去很不可思议,因为我们会发现在自己编程中常常会违反里氏替换原则,程序照样跑的好好的。所以大家都会产生这样的疑问,假如我非要不遵循里氏替换原则会有什么后果?

后果就是:你写的代码出问题的几率将会大大增加。

 

转载于http://blog.csdn.net/zhengzhb/article/details/7281833

博客地址: http://www.cnblogs.com/oumyye/
博客版权: 本文以学习、研究和分享为主,欢迎转载,转载请务必注明出处,谢谢合作。
如果文中有不妥或者错误的地方请指出。如果觉得本文对你有所帮助不如【推荐】一下!如果你有更好的建议,不如留言一起讨论,共同进步!

[转载]JavaScript系列----面向对象的JavaScript - 天天向上中 - 博客园

mikel阅读(1075)

[转载]JavaScript系列—-面向对象的JavaScript – 天天向上中 – 博客园.

1.面向对象的编程

1.1.什么是面向对象编程

面向对象编程:即是把能够完成独立完成一部分功能的代码封装在一起,组成一个类。

举个例子来说:

   这里有一把枪, 枪的种类很多,有步枪,机关枪,阻击枪….。但是无论怎么说,这些都是枪的概念,如果把这种抽象的概念剥离出来,就是我们经常说的“类”。那么枪有什 么特点呢? 威力大小,型号,长度,子弹型号,能承载子弹的数量,枪口半径……ok! 这些一切的一切都是为了描素枪,把这些抽象出来,就组成了“枪类”的属性。枪又能干什么呢?  瞄准,开火,….这些描素都是枪的功能—–把这些抽象出来,即组成了一个类的方法。

  所以,总体来说,面向对象的编程即是把一个程序模块化,每个模块承载一部分功能,各个模块协同合作,维持程序的正常执行;

  而所谓的类的组成,无外乎三个部分:这个类的名称(对应着例子中的“枪”),这个类的属性(对应着特点),这个类的方法(对应着功能)。

就像我们描素一个人一样,无外乎,描素一个人的特点以及人的能力。所以,现实生活中的人,在程序中也可以抽象成类。

1.2.类,对象,实例的关系

  • 类:是一个抽象概念,是对某一类相似对象的抽象。
  • 对象:是类的一个实例化,因为类是一个抽象的概念,所以,在使用时必须落实到实物的身上。那么,对象就作为载体来完成某项功能。
  • 实例:和对象是一个概念。一般说一个类的实例,指的就是这个类的某个对象。

举个例子来说明三者之间的关系:

复制代码
//1.Person是一个类的名字,定义的是一个人,对这个人的描述一般就是姓名,年龄。
var Person = function (name, age) {
   //Person类的属性
  this.name = name;
  this.age = age;
}
//Person类的方法
Person.prototype.greet = function () {
  console.log('hello');
}
//这是一个类的实例化过程,lisi这里就是Person类的一个对象,也可以说其是Person类的一个实例
var lisi=new Person("lisi",18);
复制代码

 1.3.面向对象的四个特点

  • 封装. 所谓的封装就是把一个类的对象的属性和方法封装在类的内部。封装的好处就是:类与类之间的属性和方法相互独立,互不干扰。
  • 继承. 所谓的继承就是指的是一个类可以派生自另外一个类。比如,图形类,可以派生出三角形,正方形,圆….
  • 重载. 重载就是指一个类的方法可以名字可以相同(JS不支持重载).第二部分会给出解释。
  • 多态.多态指的是父类的方法,子类可以重写该方法。那么,子类在调用该方法时调用的会是子类的方法。

请记住:面向对象,所有的一些都是为了代码的复用。

2.面向对象的四个特点在JS中的实现

 2.1.JS中的封装

JS类的封装即是把类的属性和方法封装在类的内部. 如果只是简单的实现封装,那么可以有多种方法。比如下面的两种

复制代码
//第一种方法
var Person = function (name, age) {
  this.name = name;
  this.age = age;
  this.greet = function () {
    console.log('hello');
  }
}
//第二种方法
var Person = function (name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.greet = function () {
  console.log('hello');
}
复制代码

 这两种方法,虽然在使用效果上是一致的,但是在第一种方法中,每次new 一个对象的时候都要为该对象添加一个函数greet—-这样就没有做到代码的复用。所以在使用的时候,一般都是使用第二种方式—也就是所谓的组合式创建。所以一般我们也推荐用第二种方式。

2.2.JS中不存在重载

 什么是重载呢? 重载的概念来源于强类型预言(C++,java,C#)中。我们先来看一些java中的重载

复制代码
class Person{  //java语言, 定义一个Person类,该类中存在greet方法的重载。
    public String name;
    public int age;
     Person(String name,int age){
        this.name=name;
        this.age=age;
    }      
     public void greet(){
         System.out.println("I am "+ this.name);
        
     }     
    public void greet(String message){
        
        System.out.println("I am "+ this.name+ "\n This is your"+message);        
    }
}
复制代码

所谓的重载,就是一个同一个方法名在一个类中被出现了多次。那么在该方法被调用的时候,编译器如何区分具体调用哪个方法呢?

在强类型语言中,编译器先根据函数的名字选择函数,然后在根据调用时,形参和实参的类型,形参的个数和实参的个数是否一致来区分一个函数。

那么,问题来了….JS中的解释器是符合区分一个函数呢? ok…JS中解释器只是根据函数的名称来选择函数,而函数的形参并不在考虑的范围—-因为在编译时无法根据确定形参的类型,更无法确定实参的类型。

既然,JS不支持重载,那么如果一个函数被重写了,会出现什么情况呢?

复制代码
var Person = function (name, age) {
  this.name = name;
  this.age = age;
  this.greet = function () {
    console.log('hello');
  }
}
var Person = function (name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.greet = function () {
  console.log('我被覆盖了');
}
Person.prototype.greet = function (message) {
  console.log("我是重写的方法");
}

var person=new Person("zhangsan",18);

person.greet(); //我是重写的方法
复制代码

根据上面的例子,可以看出,无论函数的参数是什么,只要函数同名,那么被调用的肯定是最后一次被写的同名函数

2.3.JS中的继承

继承这个概念的来源也是面向对象的编程。JS引荐强类型预言中的继承做到这一点。所以我们要从强类型语言中的继承来类推—JS中为什么要这么设计。

  2.3.1.强类型语言中继承的实现

在强类型语言中,在假设有两个类 A 、B….A是B的父类。实现如下:

复制代码
class A{//父类的构造函数
    protected int x;
    A(int x){
        this.x=x;
    }
}
class B extends A{
    protected int y;
     B(int x,int y){//子类的构造函数
         super(x);  //在子类的构造函数中,第一句话总是先调用父类的构造函数,
                    //如果不写 则默认调用super();如果父类中不存在无参构造函数,则编译时会报错。
         this.y=y; 
}

public String getPoint(){
return “(“+this.x+”,”+this.y+”)”;  //返回坐标(x,y)
}

}
复制代码

从上面的这些我们可以看出什么呢? 就是对象初始化的顺序…先初始化父类,在初始化子类。

 初始化的时候顺序为: 父类的属性—-》父类的方法—–》子类的属性—–》子类的方法。(我们这里讲的是排除了类中静态数据和方法来说,因为静态数据和方法的初始化,在类第一次被加载的时候就已经初始化完毕)

     下面我们看下,JS中是怎么实现和上述一样的功能的…

复制代码
var A = function (x) {
  this.x = x;
}
var B = function (x, y) {
  A.call(this, x);  //相当于第一种的super()函数。
  this.y = y;
}
//实现继承
function extend(subClass, superClass) {
  var prototype = Object.create(superClass);
  subClass.prototype = prototype;
  subClass.constructor = subClass;
}
extend(B, A);
B.prototype.getPoint = function () {
  return '(' + this.x + ',' + this.y + ')';
}          
复制代码

上面这两段代码,撇开语言的特性来说,他们实现的功能是等效的。只是第一种玩的是思想,第二种玩的是技巧。

OK!下面我们开始详解JS的设计者为了JS语言能实现继承所做的努力。

2.3.2.JS语言支持继承的原理。

所有的函数均有一个prototype属性。就是这个属性帮我们做到了一些,首先要认识到一点这个属性是一个对象。

    用上面我们创建的一个Person函数详解,那么这个函数的prototype属性如下表示:

 

这是这个函数在刚开始被初始化时候的固有形式,后来执行了一句

Person.prototype.greet = function () {
  console.log('hello');
}

在这句执行完毕的时候,Person.prototype变化为

 Person  prototype
constructor 指向Person函数
greet (greet函数)

 

解释了这么多,貌似并没有解释继承是怎么实现的是吧….别慌…慢慢来!!!

来看一下,当一个函数被实例化的时候发生了什么?

1
var lisi=new Person("lisi",18); //看看Person实例化的对象发生了什么?

  

 

到这里,我们看到了吧..当用new创建一个构造函数的对象的时候。这个对象会有一个【【__proto__】】内置属性,指向函数的prototype。——这就是对象lisi传说中的原型对象。

一个函数只有一个原型(prototype),这个函数在用new调用的时候会把这个原型赋值给当前对象的__proto__属性。

  当查询一个对象的属性的时候,首先查询对象本身的属性,如果没有找到则根据对象__proto__属性层层向上查找。

所以一切的一切都归咎于,只要们修改一个函数的prototype属性,那么就可以实现继承。

下面图解,B继承A的过程。

//1. A类,和B类的构造函数    

复制代码
var A = function (x) {
  this.x = x;
}
var B = function (x, y) {
  A.call(this, x);  //相当于第一种的super()函数。
  this.y = y;
}
复制代码

 

//2.修改B的prototype,使其指向A的prototype

复制代码
//实现继承
function extend(subClass, superClass) {
  var prototype = Object.create(superClass);
  subClass.prototype = prototype;
  subClass.constructor = subClass;
}
extend(B, A);
B.prototype.getPoint = function () {
  return '(' + this.x + ',' + this.y + ')';
}
复制代码

 

如此,便实现B类继承A类…关键点就在函数的prototype属性上。—-在下一篇中会详解函数的prototype。

JS中实现多态

何谓多态?

首先一定要强调一点,只有在继承存在的情况下才可能出现多态! 这是为什么呢..因为多态指的是子类覆盖父类的方法…..子类覆盖父类的方法,这种情况就是所谓的多态。

在java中的多态

复制代码
public class Test {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        B b=new B(1,2);
        String result=b.getPoint();
        System.out.println(result);
    }
}
class A{
    protected int x;
    A(int x){
        this.x=x;
    }
     public String getPoint(){         
         return "我是父类";    
     }
}
class B extends A{
    protected int y;
     B(int x,int y){
         super(x);
         this.y=y;
     }
    //多态,父类覆盖的方法
     public String getPoint(){         
         return  "我是子类";         
     }
}
 //输出结果 :我是子类
复制代码

 

在JS中的多态情况,也是指的的子类的方法覆盖父类的方法。 上面的功能在JS中是这么实现的。

复制代码
var A = function (x) {
  this.x = x;
}
A.prototype.getPoint = function () {
  return '我是子类';
}
var B = function (x, y) {
  A.call(this, x); //相当于第一种的super()函数。
  this.y = y;
}
//实现继承

function extend(subClass, superClass) {
  var prototype = Object.create(superClass);
  subClass.prototype = prototype;
  subClass.constructor = subClass;
}
extend(B, A);
B.prototype.getPoint = function () {
  return '我是子类';
}
var b = new B(1, 2);
b.getPoint();
//输出结果 :我是子类
复制代码

在上述代码执行完毕后,函数B的结构如图所示

B类在实例化的时候,B类的对象会拥有一个内部属性指向 B.prototype.当该实例调用函数的时候,会先在该对象内部查询该函数是否存在,如果不存在则沿着__proto__属性查询原型对象,即 B.prototype。如果找到此函数,则停止查询,否则会接着沿着__proto__属性所指向的对象,一直找到最上级为止。

[转载]Android 针对生成的图片文件在系统Gallery不显示的处理 - Wossoneri - 博客园

mikel阅读(1022)

[转载][Android] 针对生成的图片文件在系统Gallery不显示的处理 – Wossoneri – 博客园.

  之前遇到过一个问题,就是发现我在程序中生成一个新的 Bitmap 之后,当我打开系统的 Gallery 查看时,并没有看到新生成的图像。然而打开文件浏览器,找到保存 Bitmap 所在的文件夹下,还能看到程序生成的 Bitmap 文件。也就是说,文件其实存在,但 Gallery 似乎并没有刷新。之后,又一个新的行为证实了我的观点,即,每次重启设备之后, Gallery 中就会显示新生成的 Bitmap 了。也就是说,重启设备之后,系统对某个保存有图片信息的文件进行了重新读写(当时得出的是这么个结论,具体原理后面会专门写博客讨论)。

好了,原理能猜出个大概,那么就猜一猜方法:也许可以通知系统来对保存有图片信息的那个文件进行刷新,做一次对文件夹的扫描。但到底有没有这个方法呢?带着疑问,我在网上找了找资料,发现原理的确是这么回事,方法也是有的。

本文地址:http://www.cnblogs.com/rossoneri/p/4239152.html

  现在有两个方法可以解决这个问题:

 

Solution 1: 发送广播,通知系统刷新。

sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"+ Environment.getExternalStorageDirectory())));

不过用这个方法需要给程序添加权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RESTART_PACKAGES" />

这个方法我没有测试,但看介绍应该是可以的。

 

Solution 2: 使用 MediaScannerConnection :

首先对要使用该方法的类继承 MediaScannerConnectionClient :

public class YourView  implements MediaScannerConnectionClient {

}

继承该类需要重写两个方法:

复制代码
@Override
public void onMediaScannerConnected() {
    // TODO Auto-generated method stub
    try {
        msc.scanFile(bitmapPath, "image/jpg");
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Override
public void onScanCompleted(String path, Uri uri) {
    // TODO Auto-generated method stub
    msc.disconnect();
}
复制代码

方法看其名称就能猜到,前者是连接上 MediaScanner 后,进行查找文件。 bitmapPath 是文件的绝对路径,”image/jpg” 是文件格式, jpg 可改为 png 甚至 * 。后者方法是查找结束后断开连接。

最后,创建 MediaScannerConnection 对象,在生成图片后调用其方法:

复制代码
MediaScannerConnection msc;

.....

if (msc != null) {
    msc.disconnect();
}
msc = new MediaScannerConnection(mActivity, thisClass.this);
msc.connect();
复制代码

这样,下次再生成新的图片后,就可以在 Gallery 中看到新生成的图片了!

 

恩,问题解决了。然后我又发现了一个问题:如果我在程序中删除了某个图片,结果发现 Gallery 中还有这个图片的路径,能看到有这个图片,但打开图片是一片灰色。说简单点,就是我用代码删除图片文件了,系统的保存信息还是有这个文件的路径存在,只有 重启才会删掉这些无效路径。好了,问题来了,怎么改?

能不能像之前的步骤再来一次?前面的步骤好像是刷新了系统的某个配置文件,再做一次也许会行。尝试之后发现(第一种方法没试。。),然而并不行。

既然这样不行,那也许是我对系统的某些原理还没有搞懂,应该还有其他的方法。经过一番探索,我还是找到了解决方法,并且对 Android 系统扫描存储这些信息的原理有了进一步的了解。下一篇文章,就简单讨论讨论这个原理和解决办法。今天先到这里。

 

参考资料:

[Android实例] 把bitmap保存后无法在图库看到,但文件是存在的

Image, saved to sdcard, doesn’t appear in Android’s Gallery app

 

本文版权归Wossoneri博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则追究法律责任。

[转载]Android Metro风格的Launcher开发系列第二篇 - wuhao_blog - 博客园

mikel阅读(965)

[转载]Android Metro风格的Launcher开发系列第二篇 – wuhao_blog – 博客园.

前言:

各位小伙伴们请原谅我隔了这么久才开始写这一系列的第二篇博客,没办法忙新产品发布,好了废话不说了,先回顾一下:在我的上一篇博客Android Metro风格的Launcher开发系列第一篇写了如何配置Android开发环境,只是用文字和图片展示了开发Metro风格Launcher的初步设计和产品要求,这一篇文章将会从代码上讲解如何实现对应的UI效果,好了,评书开讲!

Launcher主体框架实现:

Launcher主体框架我选用的是大家所熟悉的ViewPager控件,因为ViewPager可以很容易做pager之间的切换动画,动画是可以自定义的,这样就轻松搞定滑出屏幕的各种效果,也可以控制切换速度,这样就很容易实现如下的效果:

 

  1. 滑动速度控制:
    这个是通过获取ViewPager的Scroller,对Scroller重新设置一些参数就可以了,具体实现的代码如下:

    1
    2
    3
    4
    5
    Field Scroller = ViewPager.class.getDeclaredField("mScroller");
    Scroller.setAccessible(true);
    Interpolator interpolator = new LinearInterpolator();//设置加速器
    ViewPagerScroller scroller = new ViewPagerScroller(context,interpolator);//重新设置ViewPager的Scroller
    Scroller.set(this, scroller);

    在上面的代码中用到了ViewPagerScroller.java,在ViewPagerScroller中的设置如下,其中的mDuration变量是自定义的动画时间,这个你可以根据自己的动画效果来设置时间长度,我这定义的是500毫秒。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Override
    public void startScroll(int startX, int startY, int dx, int dy) {
        // Ignore received duration, use fixed one instead
        super.startScroll(startX, startY, dx, dy, mDuration);
    }
    @Override
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        // Ignore received duration, use fixed one instead
        super.startScroll(startX, startY, dx, dy, mDuration);
    }
  2. Page之间切换效果实现:
    每一屏page之间的切换是通过实现ViewPager.PageTransformer接口来实现的,具体这个接口的讲解我这里就不介绍了,大家可以参考google官方文档:Using ViewPager for Screen Slides。我的实现如下所示:

    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
    class LauncherPageTransformer implements ViewPager.PageTransformer {
        private static float DEFAULT_SCALE = 1.0f;
        private static float SCALE_FACTOR = 0.30f;// 缩放因子 0.50f
        private static float ROTATION_FACTOR = 20f;// 旋转因子
        private static float ALPHA_FACTOR = 0.8f;
        @Override
        public void transformPage(View view, float position) {
            if (position <= 1) { // [-1,1]
                // Modify the default slide transition to shrink the page as well
                if (position < 0) {
                    // view.setRotationY(position * ROTATION_FACTOR);
                    view.setScaleX(SCALE_FACTOR * position + DEFAULT_SCALE);
                    view.setScaleY(SCALE_FACTOR * position + DEFAULT_SCALE);
                    // view.setAlpha(ALPHA_FACTOR * position + 1.0f);
                } else {
                    // view.setRotationY(position * ROTATION_FACTOR);
                    view.setScaleX(SCALE_FACTOR * -position + DEFAULT_SCALE);
                    view.setScaleY(SCALE_FACTOR * -position + DEFAULT_SCALE);
                    // view.setAlpha(ALPHA_FACTOR * -position + 1.0f);
                }
            }
        }
    }

    具体效果可以通过修改旋转和缩放实现。

  3. 左右两侧page悬浮实现

    主屏幕page两侧page部分显示可以通过设置ViewPager的setPageMargin(int margin)方法实现,具体代码如下:

    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
    public class LauncherViewPager extends ViewPager {
        public static final int PAGE_LIMIT = 3;
        public LauncherViewPager(Context context) {
            this(context, null);
        }
        public LauncherViewPager(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
        private void init(Context context) {
            this.setPageMargin(-getResources().getInteger(R.integer.portal_viewpager_margin));
            this.setOffscreenPageLimit(PAGE_LIMIT);
            this.setPageTransformer(true, new LauncherPageTransformer());
            try {
                Field Scroller = ViewPager.class.getDeclaredField("mScroller");
                Scroller.setAccessible(true);
                Interpolator interpolator = new LinearInterpolator();
                ViewPagerScroller scroller = new ViewPagerScroller(context,
                        interpolator);
                Scroller.set(this, scroller);
            } catch (NoSuchFieldException e) {
            } catch (IllegalArgumentException e) {
            } catch (IllegalAccessException e) {
            }
        }
    }
  4. 总结:
    以 上就是Launcher主体框架使用ViewPager实现左右滑动和缩放效果讲解,写的不好的地方还请大家指出并批评指正,“三人行必有我师”,任何给 我的批评和建议我一定会有回复和交流,可以加我的微信号,更快的交流。下一篇博客我将会讲解屏幕中每一格CellView实现,有焦点放大效果,实现每一 个Focus的CellView悬浮效果和在xml文件中灵活配置关联链接等。


     第一时间获得博客更新提醒,以及更多技术信息分享,欢迎关注个人微信公众平台:程序员互动联盟(coder_online),扫一扫下方二维码或搜索微信号coder_online即可关注,我们可以在线交流。