[转载]Scrum与高效能人士的执行4原则 - 王洪刚 - 博客园

mikel阅读(1018)

[转载]Scrum与高效能人士的执行4原则 – 王洪刚 – 博客园.

分享了高效能人士的执行4原则,发现它和Scrum非常相近,可以形成互补。

Scrum框架:

高效能人士的执行4原则框架:

Scrum与4原则

Sprint Backlog VS. 聚焦最重要目标

Sprint Backlog:Sprint代办事项列表是一组为当前Sprint选出的产品代办事项列表条目,外加交付产品增量和实现Sprint目标的计划。

聚焦最重要目标:要事第一,全神贯注。“如果其他各个方面都保持现有状况的话,改进哪一个方面才能给我们带来最大收益?”

Product owner在plan meeting中要给出Sprint Backlog的唯一排序,整个团队按照优先级别来完成这些backlog,也就是要事第一,全神贯注。

User Story/Tasks VS. 关注引领性指标

用户故事(user story)是从用户的角度来描述用户渴望得到的功能。用户故事会分解为小的task,这个样整团队就可以去完成一个个task,进而完成用户故事。

关注引领性指标:分解目标,落实行动

Scrum Board VS. 坚持激励性记分表

在Scrum Board上,列出Sprint Blacklog以及对应的tasks。然后根据这些信息绘制出燃尽图。燃尽图(burn down chart)是在项目完成之前,对需要完成的工作的一种可视化表示。燃尽图有一个Y轴(工作)和X轴(时间)。理想情况下,该图表是一个向下的曲线,随着 剩余工作的完成,“烧尽”至零。燃尽图向项目组成员和企业主提供工作进展的一个公共视图。

坚持激励性记分表:计分衡量,一目了然

Sprint plan, Stand up meeting, Sprint Review&Reptrospective VS. 建立规律问责制

Sprint计划会议, 每日例会, sprint评审会议,sprint回顾会议帮助整个team建立一种日常工作的节奏。每日例会实时跟进,评审会议关注着产品,回顾会议关注着流程,这些都是定期的实时跟进贯彻始终。

建立规律问责制:实时跟进,贯彻始终

通过上面简单的映射关系,大家很容易可以看出二者之间的相通点。但是,高效执行4原则并非是为了帮你处理日常事务而设计的,相反,它将教会你如何在日常事务的纠缠中去执行最重要的战略规划。Scrum基本是用在开发团队中的解决日常开发事务的。

就我个人而言,高效执行4原则和Scrum是相通互补的。没有人规定你在日常事务中不可以使用高效执行4原则,也没有人规定执行最重要的战略规划不能用Scrum框架。我们可以从两者中吸取好的使用到我们团队中。

[转载]Android 4.0 WebView 使用loadData加载网页乱码之解决办法_寒灵_新浪博客

mikel阅读(937)

[转载]Android 4.0 WebView 使用loadData加载网页乱码之解决办法_寒灵_新浪博客.

Android 4.0以上的版本,Webview若使用loadData会出现中文乱码

 

 

 

解决办法:

 

 

 

使用loadDataWithBaseURL方法

 

 

 

原因:

 

 

 

请看loadData和loadDataWithBaseURL区别

 

 

 

首先,从方法的定义来看:

 

 

 

public void  loadData(String data, String mimeType, String encoding) 

 

public void  loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)

 

 

 

loadDataWithBaseURL()比loadData()多两个参数,可以指定HTML代码片段中相关资源的相对根路径,也可以指定历史Url,两个方法的其余三个参数相同。

 

 

 

其次,两个方法加载的HTML代码片段有些不同

 

 

 

loadData()中的html 代码中不能包含’#’, ‘%’, ‘\’, ‘?’四中特殊字符,这就为我们内嵌css样式等制造了麻烦,因为css中经常用’#’, ‘%’等字符,需要如何处理呢?我们需要用UrlEncoder编码为#, %, ‘, ? ,所以loadData需要对这些特殊字符进行转换,而loadDataWithBaseURL不需要转换。

 

 

 

loadData() 中特殊字符的转换方法:

 

 

 

StringBuilder buf = new StringBuilder(html.length());

 

   for (char c : html.toCharArray()) {

 

       switch (c) {

 

         case ‘#’:  buf.append(“#”); break;

 

         case ‘%’:  buf.append(“%”); break;

 

         case ‘\”: buf.append(“‘”); break;

 

         case ‘?’:  buf.append(“?”); break;               

 

         default:

 

           buf.append(c);

 

           break;

 

       }

 

   }

 

 

 

loadDataWithBaseURL 使用方法:

 

 

 

StringBuilder data = new StringBuilder(“<html><body bgcolor=\”#F2F6F8\”>”);  

 

            int size = paperList.size();  

 

            for(int i = 0;i < size;i++){  

 

                if(!StringUtils.isEmpty(paperList.get(i).getFilePath())){  

 

                    data.append(“<center>”).append(“<img src = \”file://”+paperList.get(i).getFilePath()+”\”>”).append(“</center><br>”);  

 

                }  

 

                data.append(“<center>”).append(“<font color=\”#000000\”>”+paperList.get(i).getContent()+”</font>”).append(“</center><br><br>”);  

 

            }  

 

            data.append(“</body></html>”);  

webView.loadDataWithBaseURL(“”, data, “text/html”, “UTF-8″,””);

[转载]Android中使用webservice验证用户登录的示例 - Kevin Gao - 博客园

mikel阅读(979)

[转载]Android中使用webservice验证用户登录的示例 – Kevin Gao – 博客园.

前段时间做了2个Android方面的项目,现在想抽空对其中的有些知识点进行下总结,也算进一步的学习了。

由于开发手机客户端一般都要和服务器打交道,因此用户的登录验证在一般的应用中都少不了。因而我将以前做的项目中的使用webservice验证的这块

单独写出来了。我们的手机应用的服务器端采用的是ASP.NET开发的,因而webservice的开发也是用C#开发的,发布在IIS上的。

Android SDK中并没有提供调用WebService的库,因此,需要使用第三方的SDK来调用WebService。PC版本的WebService库非常丰 富,但这些对Android来说过于庞大。适合手机的WebService客户端的SDK有一些,比较常用的是KSOAP2。

KSOAP2 地址:http://code.google.com/p/ksoap2-android/

我们在项目中使用的是: ksoap2-android-assembly-2.4-jar-with-dependencies.jar。

在项目中引用KSOAP2后,我们就要引入下面几个包了:

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

然后我们就要编写调用验证用户登录的方法,并在其中调用webservice方法了,代码如下:

public String GetUserWS(String methodName, String[] parameterList) {
// 创建SoapObject对象,并指定WebService的命名空间和调用的方法名
SoapObject request = new SoapObject(Config.NAMESPACE, methodName);
// 调用的函数如果有参数,这里可以设置需要传递的参数 注意:第一个参数使用arg0 多个参数依次类推 arg1,arg2…
if (parameterList != null) {
// for (int i = 0; i < parameterList.length; i++) {
request.addProperty(“key”, parameterList[0]);
request.addProperty(“userName”, parameterList[1]);
request.addProperty(“passWord”, parameterList[2]);

// }
}

// 生成调用WebService方法的SOAP请求信息,并指定SOAP的版本
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
SoapEnvelope.VER11);
// envelope.setOutputSoapObject(request);
// 上边的一句等价于下边的这句 将SoapObject对象赋给envelope对象
envelope.bodyOut = request;
// 当前开发的是.net WS 这里需要不调用Java WS
envelope.dotNet = true;

/*
* 这里不要使用 AndroidHttpTransport ht = new AndroidHttpTransport(URL);
* 这是一个要过期的类
* 创建HttpTransportSE对象。通过HttpTransportSE类的构造方法可以指定WebService的WSDL文档的URL
*/

     //这里的SOAP_GETUSERINFOACTION = “http://172.16.xx.xxx:3366/Service/EWineService.asmx?op=Mobile_GetUserInfo”;
HttpTransportSE ht = new HttpTransportSE(Config.SOAP_GETUSERINFOACTION);

try {
// 请求WS
ht.call(Config.SOAP_ACTION, envelope);
if (envelope.getResponse() != null) {
// 获得WS函数返回值信息
// System.out.println(envelope.getResponse().toString());
Log.d(“wine”, “GetUserWS Result:”
+ envelope.getResponse().toString());
return envelope.getResponse().toString();
}

} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
Log.d(“wine”, “GetUserWS Error:” + e.getMessage());
}
return null;
}

 

具体的调用代码如下:

// 点确定按钮所执行的东东
String[] parameterList = new String[3];
parameterList[0] = LOGINKEY;
parameterList[1] = TxtUser.getText().toString();
parameterList[2] = TxtPassword.getText().toString();

   //注意Config.METHOD_GETUSERINFO是具体调用的webservice中方法名,例如:METHOD_GETUSERINFO = “Mobile_GetUserInfo”;
// 调用webService
String strRemoteInfo = GetUserWS(Config.METHOD_GETUSERINFO,
parameterList);

 

[转载]Android 利用Hierarchy Viewer工具学习别人的UI设计 - Terry_龙 - 博客园

mikel阅读(719)

[转载]Android 利用【Hierarchy Viewer 】 工具学习别人的UI设计 – Terry_龙 – 博客园.

Android 工具栏里面,地址:D:\Program Files\Android-sdk-windows\tools  目录下打开此工具

此工具名为:层级观察器

本篇文章将教大家如何利用层级观察器查看和优化自己的UI层次关系或者看别人应用程序UI的布局结构,双击该处理文件,显示如下 图:

左边为设备列表名称,如果你开启多个设备将会一一列出你当前运行的设备,包括真机。

右边是当前设备列表运行的Acitivity ,即你当前运行的Activity 对应的UI布局,本篇将使用Android 自身的音乐播放器,来操作得到UI布局结构,这里的我们要点击

com.android.music/com.android.music.MediaPlaybackActivity 项,选中它,到上面点击Load View Hierarchy 项,右下角将会出现加载进度条,然后将加载的View 结构显示出来,如下图:

左边为UI 的关系图

右边为每个View 的属性(必须选中任意View 对象)和结构草图底下是关系图的缩略图

底下进度条可以控制关系的显示大小

左下角两个按钮为缩略显示和列表显示方式,先把列表显示方式为如下图:

左边为布局中使用的ViewGroup和View 的关系结构图,点中任意的一个View 对象右边都会帮我们获得焦点

右边为显示当前Activity 在真机运行的效果图,我记得有个朋友在QQ群问过,有没有什么工具可以得到坐标的,其实这个工具就可以坐到,看最右下角,分别为RGB 和XY 轴,我们点击屏幕的任意为置,它都会帮我们算出来当前显示的值,怎么样是不是很实用的一个工具。快去试试吧

Tip:最上边的导航还可以帮们把VIEW结构生成PSD图或者做快速的切换设备动作。

[转载]Android用户登录数据存储的三种方式 - bibei1234 - 博客园

mikel阅读(833)

[转载]Android用户登录数据存储的三种方式 – bibei1234 – 博客园.

 登录的页面:

     


 

   布局代码:

 

    <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:orientation=“vertical” >

 

 

 

<!– 用户名的布局 –>

 

 

 

<LinearLayout

 

android:layout_width=“match_parent”

 

android:layout_height=“wrap_content” >

 

 

 

<TextView

 

android:id=“@+id/view_name”

 

android:layout_width=“wrap_content”

 

android:layout_height=“wrap_content”

 

android:text=“@string/text_name” />

 

 

 

<EditText

 

android:id=“@+id/edit_name”

 

android:layout_width=“0dp”

 

android:layout_height=“wrap_content”

 

android:layout_weight=“1”

 

android:ems=“10”

 

android:inputType=“textPersonName” >

 

 

 

<requestFocus />

 

</EditText>

 

</LinearLayout>

 

<!– 密码布局 –>

 

 

 

<LinearLayout

 

android:layout_width=“match_parent”

 

android:layout_height=“wrap_content” >

 

 

 

<TextView

 

android:id=“@+id/view_pass”

 

android:layout_width=“wrap_content”

 

android:layout_height=“wrap_content”

 

android:text=“@string/text_pass” />

 

 

 

<EditText

 

android:id=“@+id/edit_pass”

 

android:layout_width=“0dp”

 

android:layout_height=“wrap_content”

 

android:layout_weight=“1”

 

android:ems=“10”

 

android:inputType=“textPassword” >

 

 

 

<requestFocus />

 

</EditText>

 

</LinearLayout>

 

 

 

<LinearLayout

 

android:layout_width=“match_parent”

 

android:layout_height=“wrap_content” >

 

 

 

<Button

 

android:id=“@+id/btn_login”

 

android:layout_width=“wrap_content”

 

android:layout_height=“wrap_content”

 

android:layout_marginLeft=“0dp”

 

android:text=“@string/text_login” />

 

 

 

<CheckBox

 

android:id=“@+id/cbx_rember”

 

android:layout_width=“wrap_content”

 

android:layout_height=“wrap_content”

 

android:layout_marginLeft=“100dp”

 

android:text=“@string/text_rember” />

 

</LinearLayout>

 

 

 

<LinearLayout

 

android:layout_width=“match_parent”

 

android:layout_height=“wrap_content” >

 

 

 

<RadioGroup

 

android:id=“@+id/rg”

 

android:layout_width=“wrap_content”

 

android:layout_height=“wrap_content”

 

android:orientation=“horizontal”

 

tools:ignore=“UselessParent” >

 

 

 

<RadioButton

 

android:id=“@+id/radio_rom”

 

android:layout_width=“wrap_content”

 

android:layout_height=“wrap_content”

 

android:checked=“true”

 

android:text=“@string/rom” />

 

 

 

<RadioButton

 

android:id=“@+id/radio_sp”

 

android:layout_width=“wrap_content”

 

android:layout_height=“wrap_content”

 

android:text=“@string/sp” />

 

 

 

<RadioButton

 

android:id=“@+id/radio_sd”

 

android:layout_width=“wrap_content”

 

android:layout_height=“wrap_content”

 

android:text=“@string/sd” />

 

</RadioGroup>

 

 

 

</LinearLayout>

 

 

 

</LinearLayout>

 

String。Xml的值里面的代码:

 

<?xml version=“1.0” encoding=“utf-8”?>

 

<resources>

 

 

 

<string name=“app_name”>用户登录三种数据存储方式</string>

 

<string name=“action_settings”>Settings</string>

 

<string name=“hello_world”>Hello world!</string>

 

<string name=“text_name”>用户名:</string>

 

<string name=“text_pass”>密码:</string>

 

<string name=“text_login”>登陆</string>

 

<string name=“text_rember”>记住密码</string>

 

<string name=“rom”>rom存储</string>

 

<string name=“sp”>sp存储</string>

 

<string name=“sd”>sd存储</string>

 

</resources>

 

FileService 服务层的代码:

 

 

 

package www.csdn.net.service;

 

 

 

import java.io.File;

 

import java.io.FileInputStream;

 

import java.io.FileNotFoundException;

 

import java.io.FileOutputStream;

 

import java.io.IOException;

 

import java.util.HashMap;

 

import java.util.Map;

 

 

 

import www.csdn.net.tools.StreamTools;

 

 

 

import android.content.Context;

 

import android.content.SharedPreferences;

 

import android.content.SharedPreferences.Editor;

 

import android.os.Environment;

 

import android.widget.Toast;

 

 

 

public class FileService {

 

// 上下文的对象

 

public Context context;

 

 

 

public FileService(Context context) {

 

 

 

this.context = context;

 

}

 

 

 

/**

 

往手机内存上存储用户名与密码的操作

 

*

 

* @param name

 

* @param pass

 

* @param fileName

 

* @return

 

* @throws IOException

 

*/

 

public boolean saveToRom(String name, String pass, String fileName)

 

throws IOException {

 

try {

 

// 通过openFileOutput()方法获取一个文件的输出流对象

 

FileOutputStream fos = context.openFileOutput(fileName,

 

Context.MODE_PRIVATE);

 

// 拼接用户名与密码

 

String result = name + “:” + pass;

 

fos.write(result.getBytes());

 

fos.flush();

 

fos.close();

 

 

 

} catch (FileNotFoundException e) {

 

 

 

e.printStackTrace();

 

return false;

 

}

 

return true;

 

}

 

 

 

/**

 

追加模式

 

*

 

* @param name

 

* @param pass

 

* @param fileName

 

* @return

 

*/

 

public boolean saveToRomAppend(String name, String pass, String fileName) {

 

// MODE_PRIVATE :私有的模式

 

// 为默认操作模式

 

try {

 

// 通过openFileOutput()方法获取一个文件的输出流对象

 

FileOutputStream fos = context.openFileOutput(fileName,

 

Context.MODE_APPEND);

 

// 拼接用户名与密码

 

String result = name + “:” + pass;

 

// 写入

 

fos.write(result.getBytes());

 

fos.flush();

 

fos.close();

 

} catch (Exception e) {

 

e.printStackTrace();

 

return false;

 

}

 

 

 

return true;

 

 

 

}

 

 

 

/**

 

可读模式

 

*

 

* @param name

 

* @param pass

 

* @param fileName

 

* @return

 

*/

 

public boolean saveToRomReadable(String name, String pass, String fileName) {

 

// MODE_PRIVATE :私有的模式

 

// 为默认操作模式

 

try {

 

// 通过openFileOutput()方法获取一个文件的输出流对象

 

FileOutputStream fos = context.openFileOutput(fileName,

 

Context.MODE_WORLD_READABLE);

 

// 拼接用户名与密码

 

String result = name + “:” + pass;

 

// 写入

 

fos.write(result.getBytes());

 

fos.flush();

 

fos.close();

 

} catch (Exception e) {

 

e.printStackTrace();

 

return false;

 

}

 

 

 

return true;

 

 

 

}

 

 

 

/**

 

可以写的模式

 

*

 

* @param name

 

* @param pass

 

* @param fileName

 

* @return

 

*/

 

public boolean saveToRomWritable(String name, String pass, String fileName) {

 

// MODE_PRIVATE :私有的模式

 

// 为默认操作模式

 

try {

 

// 通过openFileOutput()方法获取一个文件的输出流对象

 

FileOutputStream fos = context.openFileOutput(fileName,

 

Context.MODE_WORLD_WRITEABLE);

 

// 拼接用户名与密码

 

String result = name + “:” + pass;

 

// 写入

 

fos.write(result.getBytes());

 

fos.flush();

 

fos.close();

 

} catch (Exception e) {

 

e.printStackTrace();

 

return false;

 

}

 

 

 

return true;

 

 

 

}

 

 

 

// 读取数据

 

public Map<String, String> readFile(String fileName) throws IOException {

 

Map<String, String> map = null;// new HashMap<String, String>();

 

try {

 

FileInputStream fis = context.openFileInput(fileName);

 

String value = StreamTools.getValue(fis);

 

String values[] = value.split(“:”);

 

// 判断map集合

 

if (values.length > 0) {

 

map = new HashMap<String, String>();

 

map.put(“name”, values[0]);

 

map.put(“pass”, values[1]);

 

}

 

} catch (FileNotFoundException e) {

 

e.printStackTrace();

 

}

 

return map;

 

}

 

 

 

/**

 

采用SharedPreferences存储数据的操作

 

*

 

* @param name

 

* @param pass

 

* @param fileName

 

* @return

 

*/

 

public boolean saveBytsp(String name, String pass, String fileName) {

 

// 通过上下文获取Sharepreferences对象

 

SharedPreferences preferences = context.getSharedPreferences(fileName,

 

context.MODE_PRIVATE);

 

// 返回Editor对象

 

Editor editor = preferences.edit();

 

editor.putString(“name”, name);

 

editor.putString(“pass”, pass);

 

editor.putInt(“age”, 8);

 

editor.putBoolean(“flag”, true);

 

// 提交

 

 

 

return editor.commit();

 

}

 

 

 

public boolean saveToSDCard(String name, String pass, String fileName) {

 

// 首先判断是否拥有SDcard

 

if (Environment.getExternalStorageDirectory().equals(

 

Environment.MEDIA_MOUNTED)) {

 

// SDCard写入数据

 

// 首先要获取SDCard目录

 

File sdCardDir = Environment.getExternalStorageDirectory();

 

File file = new File(sdCardDir, fileName);// 根据目录及创建文件的名称 创建文件

 

try {

 

FileOutputStream fos = new FileOutputStream(file);

 

String result = name + “:” + pass;

 

fos.write(result.getBytes());

 

fos.flush();

 

fos.close();

 

} catch (Exception e) {

 

// TODO Auto-generated catch block

 

e.printStackTrace();

 

return false;

 

}

 

 

 

} else {

 

Toast.makeText(context, “手机不拥有SDCard”, Toast.LENGTH_LONG).show();

 

}

 

return true;

 

}

 

 

 

// 读取数据

 

 

 

public Map<String, String> readFileSDcard(String fileName) {

 

Map<String, String> map = null;// new HashMap<String, String>();

 

try {

 

// 第一步:获取SDcard目录文件

 

File sdcardDir = Environment.getExternalStorageDirectory();

 

// 第二步:根据sdcard目录及文件名称 创建文件对象

 

File file = new File(sdcardDir, fileName);

 

// 第三步:根据文件对象创建文件的输入流

 

FileInputStream  fis = new FileInputStream(file);

 

// 第四步:利用StreamTools工具 获取文件中的内容

 

String value = StreamTools.getValue(fis);

 

// 根据规则拆分

 

String values[] = value.split(“:”);

 

//

 

if (values.length > 0) {

 

map = new HashMap<String, String>();

 

map.put(“name”, values[0]);

 

map.put(“pass”, values[1]);

 

}

 

} catch (Exception e) {

 

 

 

e.printStackTrace();

 

}

 

return map;

 

}

 

}

 

 

 

这里用到的一个工具:

 

 

package www.csdn.net.tools;

 

 

 

import java.io.ByteArrayOutputStream;

 

import java.io.FileInputStream;

 

import java.io.IOException;

 

 

 

public class StreamTools {

 

public static String getValue(FileInputStream fis) throws IOException{

 

//字节的输出流对象

 

ByteArrayOutputStream stream = new ByteArrayOutputStream();

 

byte [] buffer =  new byte[1024];

 

int length=1;

 

while ((length = fis.read(buffer)) != -1) {

 

stream.write(buffer, 0, length);

 

}

 

stream.flush();

 

stream.close();

 

String value= stream.toString();

 

return value;

 

}

 

}

 

   最主要的代码:  Activity

 

package com.example.file;

 

 

 

import java.io.IOException;

 

import java.util.Map;

 

 

 

import www.csdn.net.service.FileService;

 

 

 

import android.app.Activity;

 

import android.os.Bundle;

 

import android.text.TextUtils;

 

import android.view.Menu;

 

import android.view.View;

 

import android.widget.Button;

 

import android.widget.CheckBox;

 

import android.widget.EditText;

 

import android.widget.RadioButton;

 

import android.widget.Toast;

 

 

 

 

 

 

 

public class LoginActivity extends Activity {

 

 

 

// 声明 获取的用户名与密码的组件

 

public EditText edit_name, edit_pass;

 

// 声明登陆按钮对象

 

public Button btn_login;

 

// 声明CheckBox组件对象

 

public CheckBox box_remember;

 

 

 

//创建业务对象

 

public FileService fileService;

 

// 声明出单选的组件

 

public RadioButton radio_rom, radio_sp, radio_sd;

 

@Override

 

protected void onCreate(Bundle savedInstanceState) {

 

super.onCreate(savedInstanceState);

 

// 设置显示视图

 

setContentView(R.layout.activity_login);

 

 

 

//实例化业务对象

 

fileService = new FileService(this);

 

 

// 根据id名称获取相应组件对象

 

edit_name = (EditText) findViewById(R.id.edit_name);

 

edit_pass = (EditText) findViewById(R.id.edit_pass);

 

btn_login = (Button) findViewById(R.id.btn_login);

 

box_remember = (CheckBox) findViewById(R.id.cbx_rember);

 

 

 

// 给按钮注册事件

 

btn_login.setOnClickListener(new MyOnClickListener());

 

 

 

 

 

 

 

radio_rom = (RadioButton) findViewById(R.id.radio_rom);

 

radio_sp = (RadioButton) findViewById(R.id.radio_sp);

 

radio_sd = (RadioButton) findViewById(R.id.radio_sd);

 

 

 

// 采用SharedPreferences实现数据回显

 

// 根据上下文的api获取SharedPreferences对象

 

/*

 

SharedPreferences preferences =

 

this.getSharedPreferences(“li”,Context.MODE_PRIVATE);

 

edit_name.setText(preferences.getString(“name”, “csdn”));

 

edit_pass.setText(preferences.getString(“pass”, “csdn”));

 

*/

 

// 采用sdcard数据进行实现数据回显

 

 

 

// 获取map集合对象

 

//回显数据

 

Map<String, String> map = fileService.readFileSDcard(“csdnsdcard.txt”);

 

if(map!=null){

 

edit_name.setText(map.get(“name”));

 

edit_pass.setText(map.get(“pass”));

 

}

 

 

 

}

 

 

 

@Override

 

public boolean onCreateOptionsMenu(Menu menu) {

 

// Inflate the menu; this adds items to the action bar if it is present.

 

getMenuInflater().inflate(R.menu.login, menu);

 

return true;

 

}

 

 

 

// 内部类

 

class MyOnClickListener implements View.OnClickListener {

 

@Override

 

public void onClick(View v) {

 

int id = v.getId();

 

 

 

switch (id) {

 

case R.id.btn_login:

 

// 获取用户名与密码

 

String name = edit_name.getText().toString();

 

String pass = edit_pass.getText().toString();

 

 

 

// 判断用户名与密码是否为空

 

if (TextUtils.isEmpty(name) || TextUtils.isEmpty(pass)) {

 

Toast.makeText(LoginActivity.this, “用户名或者密码不能为空“,

 

Toast.LENGTH_LONG).show();

 

return;

 

} else {

 

 

 

// 首先 判断 是否记住密码

 

if (box_remember.isChecked()) {

 

 

 

// 判断采用什么方式保存

 

if (radio_rom.isChecked()) {

 

// 采用rom保存

 

 

 

} else if (radio_sp.isChecked()) {

 

// 采用SharedPreferences

 

boolean flag = fileService.saveBytsp(name, pass,

 

“csdn”);

 

if (flag) {

 

Toast.makeText(LoginActivity.this, “保存成功“,

 

Toast.LENGTH_LONG).show();

 

} else {

 

 

 

Toast.makeText(LoginActivity.this, “保存失败“,

 

Toast.LENGTH_LONG).show();

 

}

 

 

 

} else if (radio_sd.isChecked()) {

 

// sd卡保存

 

boolean flag = fileService.saveToSDCard(name, pass,

 

“csdnsdcard.txt”);

 

if (flag) {

 

Toast.makeText(LoginActivity.this, “保存成功“,

 

Toast.LENGTH_LONG).show();

 

} else {

 

 

 

Toast.makeText(LoginActivity.this, “保存失败“,

 

Toast.LENGTH_LONG).show();

 

}

 

}

 

 

 

}

 

 

 

}

 

 

 

break;

 

default:

 

break;

 

}

 

 

 

}

 

}

 

 

 

}

 

在进行sdcard存储的时候要有权限:

 

<uses-sdk

 

android:minSdkVersion=“8”

 

android:targetSdkVersion=“17” />

 

<!– 创建与删除文件的权限 –>

 

<uses-permission android:name=“android.permission.MOUNT_UNMOUNT_FILESYSTEMS”/>

 

<!– 写入数据的权限 –>

 

<uses-permission android:name=“android.permission.WRITE_EXTERNAL_STORAGE”/>

 

 

 

存放的数据在/data/data/<package name>/files目录应用私有的文件,txt的形式显示。

 


 

最后的效果图:

 


 


 

使用文件进行数据存储

 

Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可以使用Context.MODE_APPEND
Context.MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用来控制其他应用是否有权限读写该文件。
MODE_WORLD_READABLE:表示当前文件可以被其他应用读取;MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。
如果希望文件被其他应用读和写,可以传入:
openFileOutput(“itcast.txt”, Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);

android有一套自己的安全模型,当应用程序(.apk)在安装时系统就会分配给他一个userid,当该应用要去访问其他资源比如文件的时候,就需 要userid匹配。默认情况下,任何应用创建的文件,sharedpreferences,数据库都应该是私有的(位于/data/data /<package name>/files),其他程序无法访问。除非在创建时指定了Context.MODE_WORLD_READABLE或者 Context.MODE_WORLD_WRITEABLE ,只有这样其他程序才能正确访问。

 

读取文件内容

如果要打开存放在/data/data/<package name>/files目录应用私有的文件,可以使用Activity提供openFileInput()方法。
FileInputStream inStream = this.getContext().openFileInput(“itcast.txt”);
Log.i(“FileTest”, readInStream(inStream));
readInStream()的方法请看本页下面备注。

或者直接使用文件的绝对路径:
File file = new File(“/data/data/cn.itcast.action/files/itcast.txt”);
FileInputStream inStream = new FileInputStream(file);
Log.i(“FileTest”, readInStream(inStream));
注意:上面文件路径中的“cn.itcast.action”为应用所在包,当你在编写代码时应替换为你自己应用使用的包。
对于私有文件只能被创建该文件的应用访问,如果希望文件能被其他应用读和写,可以在创建文件时,指定Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE权限。

Activity还提供了getCacheDir()和getFilesDir()方法:
getCacheDir()方法用于获取/data/data/<package name>/cache目录
getFilesDir()方法用于获取/data/data/<package name>/files目录

 

[转载]在.NET中实现OAuth身份认证 - 白袍秀士 - 博客园

mikel阅读(1268)

[转载][转载]在.NET中实现OAuth身份认证 – 白袍秀士 – 博客园.

来源:李会军

 

OAuth为 开放API的授权提供了一个安全且开放的标准,它的第一个版本发布于2007年,OAuth 2.0协议版本还在处于草稿状态。到现在为止,大多数开放API的互联网站都采用OAuth方式来进行授权,如前一段时间Twitter取消HTTP Basic认证方式而采用OAuth,国内的豆瓣应该是使用OAuth的先 行者,而新浪微博开放平台同时支持HTTP Basic和OAuth两种方式。使用OAuth最大的好处在于第三方应用无需知道用户的账号信息(如用户名与密码)就可以申请获得该用户资源的授权。本 文首先我会简单介绍一下HTTP Basic认证方式,而后面详细介绍在.NET中如何使用OAuth方式进行认证。

HTTP Basic认证如果采用HTTP Basic认证方式,一般的做法是在HTTP请求头中添加Authorization标头,把用户名和密码装换为Base64编码放在HTTP请求头中,用.NET非常简单的就可以实现,如下代码所示:

string url = “http://terrylee.me/blog”;
HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;

string username = “myusername”;
string password = “mypassword”;

string up = username + “:” + password;
CredentialCache cache = new CredentialCache();
cache.Add(new Uri(url), “Basic”, new NetworkCredential(username, password));
request.Credentials = cache;
request.Headers.Add(“Authorization”, “Basic ” + Convert.ToBase64String(new ASCIIEncoding().GetBytes(up)));

HttpWebResponse response = request.GetResponse() as HttpWebResponse;
Stream stream = response.GetResponseStream();
StreamReader reader = new StreamReader(stream, Encoding.UTF8);
Console.WriteLine(reader.ReadToEnd());在发起请求后,可以看到HTTP头中的Authorization标头:


关于HTTP Basic认证的详细内容大家可以参考RFC2617。可以看到使用HTTP Basic认证的缺点在于第三方应用需要知道用户账号信息,存在着很多的不安全因素。

OAuth认证在使用OAuth认证时,有3个常用的URL需要首先了解一下:

A. 获取未授权的Request Token B. 请求用户对Request Token授权 C. 使用授权Request Token换取Access Token整个认证过程可以分为3步,我们用下面的流程图来表示:

接下来我们以豆瓣的OAuth认证为例,具体看一下每一步需要做什么。首先可以在http://code.google.com/p/oauth/下载到各种语言实现OAuth类库,因为我们的示例用C#来实现(完整的示例代码在这里能够下载到),所以只用下载到的OAuthBase类就够了,另外在开始之前还需要到豆瓣去创建一个应用,以便能够拿到api_key和api_Secret。

第一步,获取未授权的Request Token,请求的地址为http://www.douban.com/service/auth/request_token,在这一步里的请求参数oauth_consumer_key就是我们在上面创建应用时得到的api_key,而计算签名使用的Secret也就是上面创建应用时得到的api_secret:

//1. 获取Request Token,该步骤使用API Key和API Key Secret签名
public void getRequestToken()
{
Uri uri = requestTokenUri;
string nonce = oAuth.GenerateNonce();
string timeStamp = oAuth.GenerateTimeStamp();
string normalizeUrl, normalizedRequestParameters;

// 签名
string sig = oAuth.GenerateSignature(
uri,
apiKey,
apiKeySecret,
string.Empty,
string.Empty,
“GET”,
timeStamp,
nonce,
OAuthBase.SignatureTypes.HMACSHA1,
out normalizeUrl,
out normalizedRequestParameters);
sig = HttpUtility.UrlEncode(sig);

//构造请求Request Token的url
StringBuilder sb = new StringBuilder(uri.ToString());
sb.AppendFormat(“?oauth_consumer_key={0}&”, apiKey);
sb.AppendFormat(“oauth_nonce={0}&”, nonce);
sb.AppendFormat(“oauth_timestamp={0}&”, timeStamp);
sb.AppendFormat(“oauth_signature_method={0}&”, “HMAC-SHA1”);
sb.AppendFormat(“oauth_version={0}&”, “1.0”);
sb.AppendFormat(“oauth_signature={0}”, sig);

Console.WriteLine(“请求Request Token的url: \n” + sb.ToString());

//请求Request Token
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(sb.ToString());
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader stream = new StreamReader(response.GetResponseStream(), System.Text.Encoding.UTF8);
string responseBody = stream.ReadToEnd();
stream.Close();
response.Close();

Console.WriteLine(“请求Request Token的返回值: \n” + responseBody);

//解析返回的Request Token和Request Token Secret
Dictionary<string, string> responseValues = parseResponse(responseBody);
requestToken = responseValues[“oauth_token”];
requestTokenSecret = responseValues[“oauth_token_secret”];
}通过这一步我们可以得到Request Token和Request Token Secret。

第 二步,请求用户对Request Token授权,跳转到服务提供方授权页面(本例中指豆瓣http://www.douban.com/service/auth /authorize),在跳转到授权页面时需要传递第一步获取的Request Token。另外,在请求授权页面时还有一个可选参数oauth_callback,指用户授权完全后跳转回的页面,对于Web应用来说,这一步不是什么 问题,使用浏览器进行跳转就可以了。但是如果第三方应用是客户端程序,就显的有些麻烦,新浪微博API使用的解决方案是如果第三方应用是客户端应用,则在 用户授权完成后,生成一个PIN码,第三方应用会要求用户输入这个PIN码。

// 2. 用户确认授权
public void authorization()
{
//生成引导用户授权的url
string url =  authorizationUri + requestToken;

Console.WriteLine(“请将下面url粘贴到浏览器中,并同意授权,同意后按任意键继续:”);
Console.WriteLine(url);
}第三步,使用授权的Request Token换取Access Token,请求的地址为http://www.douban.com/service/auth/access_token,这一步的请求过程与第一步大同小异,唯一不同的地方在于计算签名时需要同时使用创建应用时获取的api_secret和第一步获取的Request Token Secret,在请求的结果中将会包含Access Token和Access Token Secret。

// 3. 换取Access Token,该步骤使用API Key、API Key Secret、Request Token和Request Token Secret签名
public void getAccessToken()
{
Uri uri = accessTokenUri;
string nonce = oAuth.GenerateNonce();
string timeStamp = oAuth.GenerateTimeStamp();
string normalizeUrl, normalizedRequestParameters;

// 签名
string sig = oAuth.GenerateSignature(
uri,
apiKey,
apiKeySecret,
requestToken,
requestTokenSecret,
“GET”,
timeStamp,
nonce,
OAuthBase.SignatureTypes.HMACSHA1,
out normalizeUrl,
out normalizedRequestParameters);
sig = HttpUtility.UrlEncode(sig);

//构造请求Access Token的url
StringBuilder sb = new StringBuilder(uri.ToString());
sb.AppendFormat(“?oauth_consumer_key={0}&”, apiKey);
sb.AppendFormat(“oauth_nonce={0}&”, nonce);
sb.AppendFormat(“oauth_timestamp={0}&”, timeStamp);
sb.AppendFormat(“oauth_signature_method={0}&”, “HMAC-SHA1”);
sb.AppendFormat(“oauth_version={0}&”, “1.0”);
sb.AppendFormat(“oauth_signature={0}&”, sig);
sb.AppendFormat(“oauth_token={0}&”, requestToken);

Console.WriteLine(“请求Access Token的url: \n” + sb.ToString());

//请求Access Token
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(sb.ToString());
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader stream = new StreamReader(response.GetResponseStream(), System.Text.Encoding.UTF8);
string responseBody = stream.ReadToEnd();
stream.Close();
response.Close();

Console.WriteLine(“请求Access Token的返回值: \n” + responseBody);

//解析返回的Request Token和Request Token Secret
Dictionary<string, string> responseValues = parseResponse(responseBody);
accessToken = responseValues[“oauth_token”];
accessTokenSecret = responseValues[“oauth_token_secret”];
}到 这一步为止,整个认证过程就已经完成了。现在有了Access Token和Access Token Secret,就可以访问受限资源了,在计算签名时需要使用创建应用时得到的api_secret和这一步得到的Access Token Secret,OAuth认证信息推荐添加在HTTP请求标头中,如使用豆瓣API发送广播:

POST http://api.douban.com/miniblog/saying HTTP/1.1
Authorization: OAuth realm=””,
oauth_consumer_key=0c7c3ca26a68d2ad2574b5e73f3a7807,
oauth_nonce=8559837,
oauth_timestamp=1284995469,
oauth_signature_method=HMAC-SHA1,
oauth_version=1.0,
oauth_signature=HmkcQjhd6B3pZ%2fRWkJ23VAxlHKQ%3d,
oauth_token=136c07ebc88d53ec3a4417c359a7fbc4

Content-Type: application/atom+xml
Host: api.douban.com
Content-Length: 172

<?xml version=’1.0′ encoding=’UTF-8′?>
<entry xmlns:ns0=”http://www.w3.org/2005/Atom” xmlns:db=”http://www.douban.com/xmlns/”>
<content>C# OAuth认证成功</content>
</entry>OAuth的认证过程虽然相比较HTTP Basic稍显有些复杂,但只要弄清楚了整个过程,还是比较简单的,总结起来就是获取Request Token,请求用户授权,换取Access Token三个步骤。

参考资料:

1. OAuth官方网站http://oauth.net/

2. OAuth规范中文描述:http://www.rollingcode.org/blog/f/oauth-core-1.0-final-cn.html

3. 豆瓣OAuth认证示例:http://code.google.com/p/douban-oauth-sample/

4. 豆瓣API OAuth认证:http://www.douban.com/service/apidoc/auth

[转载]Java开发各种资料。 - 浮沉雄鹰 - 博客园

mikel阅读(1269)

[转载]分享一下我珍藏的各种资料。 – 浮沉雄鹰 – 博客园.

mySQL技术内幕(第4版):http://pan.baidu.com/share/link?shareid=1451104019&uk=3255860305

Head First设计模式:http://pan.baidu.com/share/link?shareid=1453342338&uk=3255860305

javaee7tutorial:http://pan.baidu.com/share/link?shareid=1456582808&uk=3255860305

javaswing(第二版):http://pan.baidu.com/share/link?shareid=1458675199&uk=3255860305

java并发编程实战:http://pan.baidu.com/share/link?shareid=1461998217&uk=3255860305

Unix/linux系统管理技术手册(第四版):http://pan.baidu.com/share/link?shareid=1470324303&uk=3255860305

java核心技术卷2:http://pan.baidu.com/share/link?shareid=1473870955&uk=3255860305

java核心技术卷1:http://pan.baidu.com/share/link?shareid=1476406508&uk=3255860305

java编程思想第4版:http://pan.baidu.com/share/link?shareid=1478433139&uk=3255860305

myeclipse2013 64bit for linux part1(解压后的文件是经过split命令分开的):http://pan.baidu.com/share/link?shareid=1481360929&uk=3255860305

myeclipse2013 64bit for linux part2:http://pan.baidu.com/share/link?shareid=1487028314&uk=3255860305

myeclipse2013 64bit for linux 破解补丁:http://pan.baidu.com/share/link?shareid=1490292984&uk=3255860305

mentohust for linux 64bit:http://pan.baidu.com/share/link?shareid=1491760460&uk=3255860305

cdlinux 0.9.7:http://pan.baidu.com/share/link?shareid=1499422272&uk=3255860305

win7 64bit 旗舰版微软mdsn原版:http://pan.baidu.com/share/link?shareid=1502402431&uk=3255860305

win xp sp3 原版:http://pan.baidu.com/share/link?shareid=1505825016&uk=3255860305

继续补充:

鸟哥的linux私房菜基础篇第三版:http://pan.baidu.com/share/link?shareid=1804763158&uk=3255860305

鸟哥的linux私房菜服务器架设篇第三版:http://pan.baidu.com/share/link?shareid=1807285278&uk=3255860305

重构-改善既有代码设计2010版http://vdisk.weibo.com/s/aKJkAyPaLKojq

重构与模式:http://pan.baidu.com/share/link?shareid=1949889649&uk=3255860305

UML和模式应用(第三版):http://vdisk.weibo.com/s/aKJkAyPaLKoxb

设计模式:http://pan.baidu.com/share/link?shareid=2032223468&uk=3255860305

[转载]数据库中各种字符串的截取函数 - 妍珊 - 博客园

mikel阅读(1004)

[转载]数据库中各种字符串的截取函数 – 妍珊 – 博客园.

今天看见有人再问数据库中的数据截取了,我就和大家分享一下我看过的一篇有关这方面的文章吧。

--各种字符串截取函数
 
if exists (select * from dbo.ss where id = object_id(N'[dbo].[f_splitSTR]') and xtype in (N'FN', N'IF', N'TF'))
drop function [dbo].[f_splitSTR]
GO
 
--1 循环截取法
CREATE FUNCTION f_splitSTR(
@s   varchar(8000),   --待分拆的字符串
@split varchar(10)     --数据分隔符
)RETURNS @re TABLE(col varchar(100))
AS
BEGIN
    DECLARE @splitlen int
    SET @splitlen=LEN(@split+'a')-2
    WHILE CHARINDEX(@split,@s)>0
    BEGIN
        INSERT @re VALUES(LEFT(@s,CHARINDEX(@split,@s)-1))
        SET @s=STUFF(@s,1,CHARINDEX(@split,@s)+@splitlen,'')
    END
    INSERT @re VALUES(@s)
    RETURN
END
GO
 
 
/*==============================================*/
 
if exists (select * from dbo.ss where id = object_id(N'[dbo].[f_splitSTR]') and xtype in (N'FN', N'IF', N'TF'))
drop function [dbo].[f_splitSTR]
GO
 
--2 使用临时性分拆辅助表法
CREATE FUNCTION f_splitSTR(
@s   varchar(8000),  --待分拆的字符串
@split varchar(10)     --数据分隔符
)RETURNS @re TABLE(col varchar(100))
AS
BEGIN
    --创建分拆处理的辅助表(用户定义函数中只能操作表变量)
    DECLARE @t TABLE(ID int IDENTITY,b bit)
    INSERT @t(b) SELECT TOP 8000 0 FROM syscolumns a,syscolumns b
 
    INSERT @re SELECT SUBSTRING(@s,ID,CHARINDEX(@split,@s+@split,ID)-ID)
    FROM @t
    WHERE ID<=LEN(@s+'a') 
        AND CHARINDEX(@split,@split+@s,ID)=ID
    RETURN
END
GO
 
/*==============================================*/
 
if exists (select * from dbo.ss where id = object_id(N'[dbo].[f_splitSTR]') and xtype in (N'FN', N'IF', N'TF'))
drop function [dbo].[f_splitSTR]
GO
 
if exists (select * from dbo.ss where id = object_id(N'[dbo].[tb_splitSTR]') and objectproperty(id,N'IsUserTable')=1)
drop table [dbo].[tb_splitSTR]
GO
 
--2.1 使用永久性分拆辅助表法
--字符串分拆辅助表
SELECT TOP 8000 ID=IDENTITY(int,1,1) INTO dbo.tb_splitSTR
FROM syscolumns a,syscolumns b
GO
 
--字符串分拆处理函数
CREATE FUNCTION f_splitSTR(
@s     varchar(8000),  --待分拆的字符串
@split  varchar(10)     --数据分隔符
)RETURNS TABLE
AS
RETURN(
    SELECT col=CAST(SUBSTRING(@s,ID,CHARINDEX(@split,@s+@split,ID)-ID) as varchar(100))
    FROM tb_splitSTR
    WHERE ID<=LEN(@s+'a') 
        AND CHARINDEX(@split,@split+@s,ID)=ID)
GO
 
 
/*==============================================*/
 
if exists (select * from dbo.ss where id = object_id(N'[dbo].[f_splitSTR]') and xtype in (N'FN', N'IF', N'TF'))
drop function [dbo].[f_splitSTR]
GO
 
--3 将数据项按数字与非数字再次拆份
CREATE FUNCTION f_splitSTR(
@s   varchar(8000),    --待分拆的字符串
@split varchar(10)     --数据分隔符
)RETURNS @re TABLE(No varchar(100),Value varchar(20))
AS
BEGIN
    --创建分拆处理的辅助表(用户定义函数中只能操作表变量)
    DECLARE @t TABLE(ID int IDENTITY,b bit)
    INSERT @t(b) SELECT TOP 8000 0 FROM syscolumns a,syscolumns b
 
    INSERT @re 
    SELECT    No=REVERSE(STUFF(col,1,PATINDEX('%[^-^.^0-9]%',col+'a')-1,'')),
        Value=REVERSE(LEFT(col,PATINDEX('%[^-^.^0-9]%',col+'a')-1))
    FROM(
        SELECT col=REVERSE(SUBSTRING(@s,ID,CHARINDEX(@split,@s+@split,ID)-ID))
        FROM @t
        WHERE ID<=LEN(@s+'a') 
            AND CHARINDEX(@split,@split+@s,ID)=ID)a
    RETURN
END
GO
 
 
/*==============================================*/
 
if exists (select * from dbo.ss where id = object_id(N'[dbo].[f_splitSTR]') and xtype in (N'FN', N'IF', N'TF'))
drop function [dbo].[f_splitSTR]
GO
 
--3.1 分拆短信数据
CREATE FUNCTION f_splitSTR(@s varchar(8000))
RETURNS @re TABLE(split varchar(10),value varchar(100))
AS
BEGIN
    DECLARE @splits TABLE(split varchar(10),splitlen as LEN(split))
    INSERT @splits(split)
    SELECT 'AC' UNION ALL
    SELECT 'BC' UNION ALL
    SELECT 'CC' UNION ALL
    SELECT 'DC'   
    DECLARE @pos1 int,@pos2 int,@split varchar(10),@splitlen int
    SELECT TOP 1 
        @pos1=1,@split=split,@splitlen=splitlen
    FROM @splits
    WHERE @s LIKE split+'%'
    WHILE @pos1>0
    BEGIN
        SELECT TOP 1
            @pos2=CHARINDEX(split,@s,@splitlen+1)
        FROM @splits
        WHERE CHARINDEX(split,@s,@splitlen+1)>0
        ORDER BY CHARINDEX(split,@s,@splitlen+1)
        IF @@ROWCOUNT=0
        BEGIN
            INSERT @re VALUES(@split,STUFF(@s,1,@splitlen,''))
            RETURN
        END
        ELSE
        BEGIN
            INSERT @re VALUES(@split,SUBSTRING(@s,@splitlen+1,@pos2-@splitlen-1))
            SELECT TOP 1 
                @pos1=1,@split=split,@splitlen=splitlen,@s=STUFF(@s,1,@pos2-1,'')
            FROM @splits
            WHERE STUFF(@s,1,@pos2-1,'') LIKE split+'%'
        END
    END
    RETURN
END
GO

[转载]Chrome扩展程序的二次开发:把它改得更适合自己使用 - Wayou - 博客园

mikel阅读(1224)

[转载]Chrome扩展程序的二次开发:把它改得更适合自己使用 – Wayou – 博客园.

我当然知道未经作者允许修改别人程序是不道德的了,但作为学习研究之用还是无可厚非,这里仅供交流。

一切都是需求驱动的

话说某天我在网上猎奇的时候无意间发现这么一款神奇的谷歌浏览器插件:Extension Source Locator。翻译成大中华语意思大概是扩展程序源码定位器

它是干什么的呢,根据被翻译过来的不太准确的大中华语可以大概知道这玩意儿可以定位到一个你已经在谷歌浏览器上安装了的扩展程序的源码,或者说源文件 ,在你电脑磁盘的哪个地方。

这当然没什么神奇的了,你或许说我可以通过上网查查就知道谷歌浏览器扩展程序安装后与之相关的文件在磁盘什么地方了。但有了它使我们更加方便地定位到源文件所在文件夹。

更重要的是它让我意识到我可以修改一些我喜欢的扩展程序,让其更加适合自己的使用。

因为很多时候我会碰到这种情况,遇到一个扩展,装上之后非常喜欢,用段时间后觉得有些地方如果能这样,那就更好了。如果能那样,那这个插件就完美了,etc.

这个时候,第一反应还是乖乖的去该插件的评论页面给作者提建议,并且一个插件里的评论大部分确实是这样的建设性意见:如果怎么怎么着我就给你打五分。。。。

大家都知道,作者一般很忙,往往会把用户这些无趣的需求置之不理。

作为程序员的我,当然知道自己动手方能丰衣足食。

 

安装Extension Source Locator插件,不装其实也可以

Extension Source Locator安装页面在Google Web Store,的盆友们可以前往安装。

 

装好之后你再打开一个空的页面时,页面应该呈现的是这样的画面了:

 

如何使用:

使用过程当然是点击”Copy to clipboard” 按扭。这个页面还有任何其他按扭么一_一!!

点击后得到一个插件的文件夹地址,将其粘贴到Windows 资源管理器的地址栏中,轻击回车,你就来到了这个一谷歌浏览器扩展程序的源文件存放的地方了。

比如上面截图的的Google+ 这个扩展程序。

点开该1.2.0.418_0文件夹,里面存放了Google+ 这个扩展程序相关的源文件。

 

案例一:制作一个指向百度首页的谷歌扩展程序

你完全不需要知道一个谷歌扩展程序是如何工作的,也不用学习如何编写一个扩展程序,只要是一个程序员,多少应该是掌握HTML,CSS,JavaScript的。

谷歌扩展程序,包括在最近新版(Chrome 29)中新增的谷歌浏览器打包程序(Chrome packaged app), 其本质都是HTML 与JavaScript

 

像 Google+啦,Gmail啦,这些是最简单的谷歌浏览器插件,点击之后只是指向相关网站而以。

国人每天都会使用百度进行搜索,如果有一个可以打开百度首页的谷歌插件就方便了,就像在谷歌浏览器里创建了一个快捷方式一样。到谷歌应用商店一搜,还真没有这样一个插件。

拿上面的Google+为例,我们需要做的仅仅是去Google+文件所在的文件夹,找到相关文件然后打开,把指向Google+的连接更改为www.baidu.com即可。

上面已经找到该扩展的文件夹了,打开一看里面非常之简陋:

  • 一个跟语言本地化什么的相关的_locales文件夹(直接无视);
  • 一个存放程序图标的icons文件夹(呆会我们做一个带百度logo的图标去替换里面的Google+图标);
  • 一个谷歌扩展程序必备的清单文件manifest.json;(可以做手脚的地方也就只有这么一个文件了)

好家伙,这东西竟然连一个像样的HTML与JS文件都没有(其实也不需要有,因为他的功能灰常之简单,这个插件本身不显示任何页面,所以它没有HTML文件,更多关于谷歌浏览器插件的相关知识请移步谷歌开发者中心的扩展程序开发页面,也可以看看园子里面的文章比如Harvey如何开发Chrome(谷歌)浏览器的插件) 。

将所有文件及文件夹复制到另一个新的文件夹,比如我们在桌面新建一个叫”Baidu”的文件夹。之后我们的操作在复制的文件上进行,目的是不破坏原来的文件,也方便我们修改好之后以这个新文件夹生成一个新的谷歌扩展程序。

 

唯一有用的似乎就一个JSON后缀的文件,打开一看果然看到了指向Google+的连接。

所以我们要做的就非常简单了,将连接改为”http://www.baidu.com” ,同时把程序的显示名改一下,再保存关闭;

使用强大的PS制作新的图标(我不会告诉你作为一名非著名程序员之外,我还是个半职业的平面设计师):

 

128X128

16X16

 

然后分别更名为icon16.png, icon128.png将icons文件夹中原来的图标替换。

最后一步,将我们的山寨程序安装到谷歌浏览器。

页面停留在插件页面的情况下,直接从桌面将那个Baidu文件夹拖到浏览器里,插件就这样被安装好了。

在新标签页或者Chrome app launcher里便可以看到我们新制作的百度首页插件。

 

点击它就可以直达百度啦\(^_______^)/。虽然没什么意义一_一。。。

 

案例二:让谷歌搜索中的英文关键字像中文一样红色高亮

 

这个功能我想了很久,但在谷歌应用商店能找到的高亮搜索关键字的插件不能完全满足我的要求。

比如这个叫word highlight的插件似乎是个不错的选择。

但它会为关键字里的每个单词加上不同的背景色,真心有点难看。

为了实现之前说的那样,跟中文搜索时有一样的体验,我开始了对它的改造。

同之前一样,复制其在磁盘上的地址,在资源浏览器中找到这个插件的源文件。

打开一看,文件还挺多的,似乎有点无从下手。

但其实找到关键点就不那么难了。因为显示相关的无非就是CSS,再者就是看JavaScript里面关于CSS颜色设置相关的代码,可以通过搜索‘color’找到相关代码。

但我首先还是尝试看能不能在CSS文件里突破,不行再去JavaScript代码尝试。

从而页面代码来看,它为需要高亮的关键字加了一些CSS class:

所以打开CSS文件夹,在option_page.css 里搜索相关class,无果。

看来只能去JavaScript 代码里一探究竟了。

一看就知道words_highlight.js 应该就是实现对文字进行高亮相关的代码。所以打开它来研究。

通过在words_highlight.js文件里搜索’color’,发现在代码的173行定义了一个包含颜色的数组,用的还是RGB模式的颜色。

然后在代码的270行发现了对文字进行背景及颜色设置的代码:

似乎已经很明朗了。

在谷歌搜索中随便用中文搜索一下,目的是为了获得原生的红色色值。如下图,我们得到谷歌对中文使用的颜色是#dd4b39

但我们得到的是16进制的色值,为了代码风格的统一我们也转换成代码中需要的RGB色值。随便找一个在线色值转换工具,最后得到我们需要的RGB为rgb(221,75,57)

将原来代码中270行对于文字背景设置的代码删除,再把color的设置由原来的black改为 想要的 rgb(221,75,57)。保存并关闭文件。

 

现在重启一下浏览器再随便搜索一下,效果就出来了。看起来还蛮不错的样子。

[转载]zxing二维码扫描的流程简析(Android版) - nickycookie - 博客园

mikel阅读(1356)

[转载]zxing二维码扫描的流程简析(Android版) – nickycookie – 博客园.

目前市面上二维码的扫描似乎用开源google的zxing比较多,接下去以2.2版本做一个简析吧,勿喷。。。

下载下来后定位两个文件夹,core和Android,core是一些核心的库,Android是针对android的一些代码。

我们先看核心库,在package com.google.zxing中的一些生成二维码的类关系

接口Writer里面有两个encode的重载函数,不同的格式的二维码有各自的类实现了Writer接口,MultiformatWriter类 比较特殊,根据代码的注释可见其其实是个工厂类,根据BarcodeFormat实例化不同的Writer,然后最终调用各自的 Encode.encode()方法

复制代码
 1 public final class MultiFormatWriter implements Writer {
 2 
 3   @Override
 4   public BitMatrix encode(String contents,
 5                           BarcodeFormat format,
 6                           int width,
 7                           int height) throws WriterException {
 8     return encode(contents, format, width, height, null);
 9   }
10 
11   @Override
12   public BitMatrix encode(String contents,
13                           BarcodeFormat format,
14                           int width, int height,
15                           Map<EncodeHintType,?> hints) throws WriterException {
16 
17     Writer writer;
18     switch (format) {
19       case EAN_8:
20         writer = new EAN8Writer();
21         break;
22       case EAN_13:
23         writer = new EAN13Writer();
24         break;
25       case UPC_A:
26         writer = new UPCAWriter();
27         break;
28       case QR_CODE:
29         writer = new QRCodeWriter();
30         break;
31       case CODE_39:
32         writer = new Code39Writer();
33         break;
34       case CODE_128:
35         writer = new Code128Writer();
36         break;
37       case ITF:
38         writer = new ITFWriter();
39         break;
40       case PDF_417:
41         writer = new PDF417Writer();
42         break;
43       case CODABAR:
44         writer = new CodaBarWriter();
45         break;
46       case DATA_MATRIX:
47         writer = new DataMatrixWriter();
48         break;
49       case AZTEC:
50         writer = new AztecWriter();
51         break;
52       default:
53         throw new IllegalArgumentException("No encoder available for format " + format);
54     }
55     return writer.encode(contents, format, width, height, hints);
56   }
57 
58 }
复制代码

然后看解析二维码的类结构

关键就是这个MultiformatReader,里面聚合了多个reader,并且根据客户端设置的DecodeHintType值,确定添加reader以及添加reader的顺序,最后调用reader.decode方法

复制代码
  1 public final class MultiFormatReader implements Reader {
  2 
  3   private Map<DecodeHintType,?> hints;
  4   private Reader[] readers;
  5 
  6   @Override
  7   public Result decode(BinaryBitmap image) throws NotFoundException {
  8     setHints(null);
  9     return decodeInternal(image);
 10   }
 11 
 12   @Override
 13   public Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints) throws NotFoundException {
 14     setHints(hints);
 15     return decodeInternal(image);
 16   }
 17 
 18   public Result decodeWithState(BinaryBitmap image) throws NotFoundException {
 19     // Make sure to set up the default state so we don't crash
 20     if (readers == null) {
 21       setHints(null);
 22     }
 23     return decodeInternal(image);
 24   }
 25 
 26   public void setHints(Map<DecodeHintType,?> hints) {//根据设置的hint来设置reader
 27     this.hints = hints;
 28 
 29     boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
 30     @SuppressWarnings("unchecked")
 31     Collection<BarcodeFormat> formats =
 32         hints == null ? null : (Collection<BarcodeFormat>) hints.get(DecodeHintType.POSSIBLE_FORMATS);
 33     Collection<Reader> readers = new ArrayList<Reader>();
 34     if (formats != null) {
 35       boolean addOneDReader =
 36           formats.contains(BarcodeFormat.UPC_A) ||
 37           formats.contains(BarcodeFormat.UPC_E) ||
 38           formats.contains(BarcodeFormat.EAN_13) ||
 39           formats.contains(BarcodeFormat.EAN_8) ||
 40           formats.contains(BarcodeFormat.CODABAR) ||
 41           formats.contains(BarcodeFormat.CODE_39) ||
 42           formats.contains(BarcodeFormat.CODE_93) ||
 43           formats.contains(BarcodeFormat.CODE_128) ||
 44           formats.contains(BarcodeFormat.ITF) ||
 45           formats.contains(BarcodeFormat.RSS_14) ||
 46           formats.contains(BarcodeFormat.RSS_EXPANDED);
 47       // Put 1D readers upfront in "normal" mode
 48       if (addOneDReader && !tryHarder) {
 49         readers.add(new MultiFormatOneDReader(hints));
 50       }
 51       if (formats.contains(BarcodeFormat.QR_CODE)) {
 52         readers.add(new QRCodeReader());
 53       }
 54       if (formats.contains(BarcodeFormat.DATA_MATRIX)) {
 55         readers.add(new DataMatrixReader());
 56       }
 57       if (formats.contains(BarcodeFormat.AZTEC)) {
 58         readers.add(new AztecReader());
 59       }
 60       if (formats.contains(BarcodeFormat.PDF_417)) {
 61          readers.add(new PDF417Reader());
 62       }
 63       if (formats.contains(BarcodeFormat.MAXICODE)) {
 64          readers.add(new MaxiCodeReader());
 65       }
 66       // At end in "try harder" mode
 67       if (addOneDReader && tryHarder) {
 68         readers.add(new MultiFormatOneDReader(hints));
 69       }
 70     }
 71     if (readers.isEmpty()) {
 72       if (!tryHarder) {
 73         readers.add(new MultiFormatOneDReader(hints));
 74       }
 75 
 76       readers.add(new QRCodeReader());
 77       readers.add(new DataMatrixReader());
 78       readers.add(new AztecReader());
 79       readers.add(new PDF417Reader());
 80       readers.add(new MaxiCodeReader());
 81 
 82       if (tryHarder) {
 83         readers.add(new MultiFormatOneDReader(hints));
 84       }
 85     }
 86     this.readers = readers.toArray(new Reader[readers.size()]);
 87   }
 88 
 89   @Override
 90   public void reset() {
 91     if (readers != null) {
 92       for (Reader reader : readers) {
 93         reader.reset();
 94       }
 95     }
 96   }
 97 
 98   private Result decodeInternal(BinaryBitmap image) throws NotFoundException {//最终都调用这个方法
 99     if (readers != null) {
100       for (Reader reader : readers) {
101         try {
102           return reader.decode(image, hints);
103         } catch (ReaderException re) {
104           // continue
105         }
106       }
107     }
108     throw NotFoundException.getNotFoundInstance();
109   }
110 
111 }
复制代码

DecodeHintType的语法比较有意思,还在理解中

复制代码
 1 public enum DecodeHintType {
 2 
 3   /**
 4    * Unspecified, application-specific hint. Maps to an unspecified {@link Object}.
 5    */
 6  OTHER(Object.class),
 7 
 8   /**
 9    * Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;
10    * use {@link Boolean#TRUE}.
11    */
12   PURE_BARCODE(Void.class),
13 
14   /**
15    * Image is known to be of one of a few possible formats.
16    * Maps to a {@link List} of {@link BarcodeFormat}s.
17    */
18   POSSIBLE_FORMATS(List.class),
19 
20   /**
21    * Spend more time to try to find a barcode; optimize for accuracy, not speed.
22    * Doesn't matter what it maps to; use {@link Boolean#TRUE}.
23    */
24   TRY_HARDER(Void.class),
25 
26   /**
27    * Specifies what character encoding to use when decoding, where applicable (type String)
28    */
29   CHARACTER_SET(String.class),
30 
31   /**
32    * Allowed lengths of encoded data -- reject anything else. Maps to an {@code int[]}.
33    */
34   ALLOWED_LENGTHS(int[].class),
35 
36   /**
37    * Assume Code 39 codes employ a check digit. Doesn't matter what it maps to;
38    * use {@link Boolean#TRUE}.
39    */
40   ASSUME_CODE_39_CHECK_DIGIT(Void.class),
41 
42   /**
43    * Assume the barcode is being processed as a GS1 barcode, and modify behavior as needed.
44    * For example this affects FNC1 handling for Code 128 (aka GS1-128). Doesn't matter what it maps to;
45    * use {@link Boolean#TRUE}.
46    */
47   ASSUME_GS1(Void.class),
48 
49   /**
50    * The caller needs to be notified via callback when a possible {@link ResultPoint}
51    * is found. Maps to a {@link ResultPointCallback}.
52    */
53   NEED_RESULT_POINT_CALLBACK(ResultPointCallback.class),
54 
55   // End of enumeration values.
56   ;
57 
58   /**
59    * Data type the hint is expecting.
60    * Among the possible values the {@link Void} stands out as being used for
61    * hints that do not expect a value to be supplied (flag hints). Such hints
62    * will possibly have their value ignored, or replaced by a
63    * {@link Boolean#TRUE}. Hint suppliers should probably use
64    * {@link Boolean#TRUE} as directed by the actual hint documentation.
65    */
66   private final Class<?> valueType;
67 
68   DecodeHintType(Class<?> valueType) {
69     this.valueType = valueType;
70   }
71   
72   public Class<?> getValueType() {
73     return valueType;
74   }
75 
76 }
复制代码

然后我们看下android里面是如何调用的,入口是CaptureActivity,在com.google.zxing.client.android package中,以下描述一个通用的流程

CaptureAct中的onResume中的initCamera初始化CaptureActHandler,其构造函数中新起了一个 DecodeThread去异步准备一个DecodeHandler,然后调用restartPreviewAndDecode方法,让 DecodeHandler去处理R.id.decode的消息,当然这里需要处理一些线程同步问题,代码里用到了CountDownLatch来控制。 DecodeHanlder处理R.id.decode消息后用传递R.id.decode_succeeded消息给 CaptureActHanlder,最终再调用handleDecode传递给CaptureAct.