[转载]Android系统广播处理机制

mikel阅读(925)

[转载]Android系统广播处理机制 – 宁 静 致 远 – 博客园.

ndroid系统中的广播是广泛用于应用程序之间通信的一种手段,它类似于事件处理机制,不同的地方就是广播的处理是系统级别的事件处理过程(一般事件处理是控件级别的)。在此过程中仍然是离不开Intent对象,理解广播事件的

处理过程,灵活运用广播处理机制,在关键之处往往能实现特别的效果,举一个比较经典的例子,哪黑名单功能,当打进一个电话时,即产生了一个来电广播,则接收这种来电广播的BroadcastReceiver就会拿这个来电号码与黑名单中

号码进行比较,若匹配,则对此来电做相应处理,如挂电话或静音。在这个例子中就涉及到系统广播的发送、接收及对广播事件处理过程。

对比Android系统广播的相关知识,列出下面一张思维导图:

Android广播事件处理 (2)

实例代码:

待续……

[转载]超炫的3D特效程序管理功能android

mikel阅读(1059)

[转载]超炫的3D特效程序管理功能android – tankaixiong – 博客园.

tank我昨天我花了点时间重新整理了一下,加了很多注释希望让大家能够看的很明白

整理后在原来的基础上,实现一个超炫的3D特效程序管理功能,所以更有用途了,不仅仅只是显示图片了。

实现的效果:
用3D效果显示所有已安装的程序列表,点击某张图片时动态显示到最前一张,长按可以打开该程序。(如上篇博客展示的样子,这里不再贴出)

主要思路流程如下:

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 实例化launcher列表,得到应用程序的信息列表(包括图片)
getLauncher();
final CoverFlow cf = new CoverFlow(this);//重写Garry的getChildStaticTransformation ,产生层叠和放大效果
//填充我的要的图片 http://www.cnblogs.com/tankaixiong/(原)
cf.setAdapter(new ImageAdapter(this));
//自定义图片的填充方式
ImageAdapter imageAdapter = new ImageAdapter(this);
cf.setAdapter(imageAdapter);
cf.setAnimationDuration(1500);
cf.setOnItemClickListener(this);
cf.setOnItemLongClickListener(lonClick);
setContentView(cf);
}

第一步:
创建一个实体类来保存程序信息:

LauncherItem

package com.Android.tank;
import Android.content.ComponentName;
import android.graphics.drawable.Drawable;
public class LauncherItem {
Drawable icon;
String name;
ComponentName component;
LauncherItem(Drawable d, String s, ComponentName cn) {
icon = d;
name = s;
component = cn;
}
public Drawable getIcon() {
return icon;
}
public void setIcon(Drawable icon) {
this.icon = icon;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ComponentName getComponent() {
return component;
}
public void setComponent(ComponentName component) {
this.component = component;
}
};
第二步:存入
//这里保存从应用程序中获取到的信息LIST(包括图片的信息),你也可以自己定一个图片集合
List<LauncherItem> lvalue;
// 获得app 列表信息
public void getLauncher() {
lvalue = new ArrayList<LauncherItem>();
PackageManager pkgMgt = this.getPackageManager();//这个方法是关键
// to query all launcher & load into List<>
Intent it = new Intent(Intent.ACTION_MAIN);
it.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> ra = pkgMgt.queryIntentActivities(it, 0);//查询
//存入集合中 http://www.cnblogs.com/tankaixiong/(原)
for (int i = 0; i < ra.size(); i++) {
ActivityInfo ai = ra.get(i).activityInfo;
// String ainfo = ai.toString();
Drawable icon = ai.loadIcon(pkgMgt);
String label = ai.loadLabel(pkgMgt).toString();
ComponentName c = new ComponentName(ai.applicationInfo.packageName,
ai.name);
LauncherItem item = new LauncherItem(icon, label, c);
lvalue.add(item);
}
}
第三步:重写baseadapter
public class ImageAdapter extends BaseAdapter {
int mGalleryItemBackground;
private Context mContext;
public ImageAdapter(Context context) {
mContext = context;
TypedArray typedArray = obtainStyledAttributes(R.styleable.Gallery);
mGalleryItemBackground = typedArray.getResourceId(
R.styleable.Gallery_android_galleryItemBackground, 0);
}
// 第1点改进,返回一个很大的值,例如,Integer.MAX_VALUE
public int getCount() {
return resIds.length;
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageView iv = new ImageView(mContext);
iv.setImageDrawable(lvalue.get(position).icon);
iv.setImageBitmap(MyImgView.createReflectedImage(MyImgView
.drawableToBitmap(lvalue.get(position).icon)));//加入处理过的图片
iv.setLayoutParams(new Gallery.LayoutParams(80, 60));
return iv;
}
第四步:处理图片,产生特效
package com.android.tank;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuffXfermode;
import android.graphics.Bitmap.Config;
import android.graphics.PorterDuff.Mode;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.Drawable;
public class MyImgView {
/**
* 添加倒影,原理,先翻转图片,由上到下放大透明度
* @param originalImage
* @return
*/
public static Bitmap createReflectedImage(Bitmap originalImage) {
// The gap we want between the reflection and the original image
final int reflectionGap = 4;
int width = originalImage.getWidth();
int height = originalImage.getHeight();
// This will not scale but will flip on the Y axis
Matrix matrix = new Matrix();
matrix.preScale(1, -1);
// Create a Bitmap with the flip matrix applied to it.
// We only want the bottom half of the image
Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0,
height / 2, width, height / 2, matrix, false);
// Create a new bitmap with same width but taller to fit reflection
Bitmap bitmapWithReflection = Bitmap.createBitmap(width,
(height + height / 2), Config.ARGB_8888);
// Create a new Canvas with the bitmap that's big enough for
// the image plus gap plus reflection
Canvas canvas = new Canvas(bitmapWithReflection);
// Draw in the original image
canvas.drawBitmap(originalImage, 0, 0, null);
// Draw in the gap http://www.cnblogs.com/tankaixiong/(原)
Paint defaultPaint = new Paint();
canvas.drawRect(0, height, width, height + reflectionGap, defaultPaint);
// Draw in the reflection
canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, null);
// Create a shader that is a linear gradient that covers the reflection
Paint paint = new Paint();
LinearGradient shader = new LinearGradient(0,
originalImage.getHeight(), 0, bitmapWithReflection.getHeight()
+ reflectionGap, 0x70ffffff, 0x00ffffff, TileMode.CLAMP);
// Set the paint to use this shader (linear gradient)
paint.setShader(shader);
// Set the Transfer mode to be porter duff and destination in
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
// Draw a rectangle using the paint with our linear gradient
canvas.drawRect(0, height, width, bitmapWithReflection.getHeight()
+ reflectionGap, paint);
return bitmapWithReflection;
}
//drawable 类型转化为bitmap
public static Bitmap drawableToBitmap(Drawable drawable) {
Bitmap bitmap = Bitmap
.createBitmap(
drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(),
drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
: Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
// canvas.setBitmap(bitmap);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable
.getIntrinsicHeight());
drawable.draw(canvas);
return bitmap;
}
}
第五步:
重写Garry 来达到层叠效果。
package com.android.tank;
import android.content.Context;
import android.graphics.Camera;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.Transformation;
import android.widget.Gallery;
import android.widget.ImageView;
//自己定义的Gallery
public class CoverFlow extends Gallery {
private Camera mCamera = new Camera();
private int mMaxRotationAngle = 50;
private int mMaxZoom = -500;
private int mCoveflowCenter;
private boolean mAlphaMode = true;
private boolean mCircleMode = false;
public CoverFlow(Context context) {
super(context);
this.setStaticTransformationsEnabled(true);
}
public CoverFlow(Context context, AttributeSet attrs) {
super(context, attrs);
this.setStaticTransformationsEnabled(true);
}
public CoverFlow(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.setStaticTransformationsEnabled(true);
}
public int getMaxRotationAngle() {
return mMaxRotationAngle;
}
public void setMaxRotationAngle(int maxRotationAngle) {
mMaxRotationAngle = maxRotationAngle;
}
public boolean getCircleMode() {
return mCircleMode;
}
public void setCircleMode(boolean isCircle) {
mCircleMode = isCircle;
}
public boolean getAlphaMode() {
return mAlphaMode;
}
public void setAlphaMode(boolean isAlpha) {
mAlphaMode = isAlpha;
}
public int getMaxZoom() {
return mMaxZoom;
}
public void setMaxZoom(int maxZoom) {
mMaxZoom = maxZoom;
}
private int getCenterOfCoverflow() {
return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2
+ getPaddingLeft();
}
private static int getCenterOfView(View view) {
return view.getLeft() + view.getWidth() / 2;
}
//重写Garray方法 ,产生层叠和放大效果
@Override
protected boolean getChildStaticTransformation(View child, Transformation t) {
final int childCenter = getCenterOfView(child);
final int childWidth = child.getWidth();
int rotationAngle = 0;
t.clear();
t.setTransformationType(Transformation.TYPE_MATRIX);
if (childCenter == mCoveflowCenter) {
transformImageBitmap((ImageView) child, t, 0, 0);
} else {
rotationAngle = (int) (((float) (mCoveflowCenter - childCenter) / childWidth) * mMaxRotationAngle);
// Log.d("test", "recanglenum:"+Math.floor ((mCoveflowCenter -
// childCenter) / childWidth));
if (Math.abs(rotationAngle) > mMaxRotationAngle) {
rotationAngle = (rotationAngle < 0) ? -mMaxRotationAngle
: mMaxRotationAngle;
}
transformImageBitmap((ImageView) child, t, rotationAngle,
(int) Math.floor((mCoveflowCenter - childCenter)/ (childWidth==0?1:childWidth)));
}
return true;
}
/**
* This is called during layout when the size of this view has changed. If
* you were just added to the view hierarchy, you're called with the old
* values of 0.
* @param w
*            Current width of this view.
* @param h
*            Current height of this view.
* @param oldw
*            Old width of this view.
* @param oldh
*            Old height of this view.
*/
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mCoveflowCenter = getCenterOfCoverflow();
super.onSizeChanged(w, h, oldw, oldh);
}
/**
* Transform the Image Bitmap by the Angle passed
* @param imageView
*            ImageView the ImageView whose bitmap we want to rotate
* @param t
*            transformation
* @param rotationAngle
*            the Angle by which to rotate the Bitmap
*/
private void transformImageBitmap(ImageView child, Transformation t,
int rotationAngle, int d) {
mCamera.save();
final Matrix imageMatrix = t.getMatrix();
final int imageHeight = child.getLayoutParams().height;
final int imageWidth = child.getLayoutParams().width;
final int rotation = Math.abs(rotationAngle);
mCamera.translate(0.0f, 0.0f, 100.0f);
// As the angle of the view gets less, zoom in
if (rotation <= mMaxRotationAngle) {
float zoomAmount = (float) (mMaxZoom + (rotation * 1.5));
mCamera.translate(0.0f, 0.0f, zoomAmount);
if (mCircleMode) {
if (rotation < 40)
mCamera.translate(0.0f, 155, 0.0f);
else
mCamera.translate(0.0f, (255 - rotation * 2.5f), 0.0f);
}
if (mAlphaMode) {
((ImageView) (child)).setAlpha((int) (255 - rotation * 2.5));
}
}
mCamera.rotateY(rotationAngle);
mCamera.getMatrix(imageMatrix);
imageMatrix.preTranslate(-(imageWidth / 2), -(imageHeight / 2));
imageMatrix.postTranslate((imageWidth / 2), (imageHeight / 2));
mCamera.restore();
}
}

最后是表示层:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Gallery android:id="@+id/gallery" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_marginTop="30dp" />
<ImageSwitcher android:id="@+id/imageswitcher"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:layout_marginTop="30dp" />
</LinearLayout>

好了,大概就这些了,整理这么多东东贴出可花了我不少时间呢,希望对大家有帮助!

这里提供源码下载的地址(请尊重tank的心血成果):

http://files.cnblogs.com/tankaixiong/MyApplicationMenu.rar

[转载]Android用GSon处理Json数据

mikel阅读(860)

[转载]Android用GSon处理Json数据 – Vincent.C – 博客园.

此篇接上篇 Android访问WCF(下篇)-客户端开发 将服务器获取的JSON数据通过GSON这个类库, 进行反序列化, 并通过UI显示出来.

如何在Android平台上用GSON反序列化JSON数据, 参考了这篇文章 http://benjii.me/2010/04/deserializing-json-in-android-using-gson/

一. 建立我们包装好的Http请求类文件 WebDataGetApi.java

package com.demo;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.protocol.HTTP;

import android.util.Log;

public class WebDataGetApi {

    private static final String TAG = "WebDataGetAPI";
    private static final String USER_AGENT = "Mozilla/4.5";

    protected String getRequest(String url) throws Exception {
        return getRequest(url, new DefaultHttpClient(new BasicHttpParams()));
    }

    protected String getRequest(String url, DefaultHttpClient client)
            throws Exception {
        String result = null;
        int statusCode = 0;
        HttpGet getMethod = new HttpGet(url);
        Log.d(TAG, "do the getRequest,url=" + url + "");
        try {
            getMethod.setHeader("User-Agent", USER_AGENT);
            // HttpParams params = new HttpParams();

            // 添加用户密码验证信息
            // client.getCredentialsProvider().setCredentials(
            // new AuthScope(null, -1),
            // new UsernamePasswordCredentials(mUsername, mPassword));

            HttpResponse httpResponse = client.execute(getMethod);
            // statusCode == 200 正常
            statusCode = httpResponse.getStatusLine().getStatusCode();
            Log.d(TAG, "statuscode = " + statusCode);
            // 处理返回的httpResponse信息
            result = retrieveInputStream(httpResponse.getEntity());
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
            throw new Exception(e);
        } finally {
            getMethod.abort();
        }
        return result;
    }

    /**
     * 处理httpResponse信息,返回String
     * 
     * @param httpEntity
     * @return String
     */
    protected String retrieveInputStream(HttpEntity httpEntity) {
        int length = (int) httpEntity.getContentLength();
        if (length < 0)
            length = 10000;
        StringBuffer stringBuffer = new StringBuffer(length);
        try {
            InputStreamReader inputStreamReader = new InputStreamReader(
                    httpEntity.getContent(), HTTP.UTF_8);
            char buffer[] = new char[length];
            int count;
            while ((count = inputStreamReader.read(buffer, 0, length - 1)) > 0) {
                stringBuffer.append(buffer, 0, count);
            }
        } catch (UnsupportedEncodingException e) {
            Log.e(TAG, e.getMessage());
        } catch (IllegalStateException e) {
            Log.e(TAG, e.getMessage());
        } catch (IOException e) {
            Log.e(TAG, e.getMessage());
        }
        return stringBuffer.toString();
    }
}

二. 建立JsonDataGetApi.java

package com.demo;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class JsonDataGetApi extends WebDataGetApi {
    private static final String BASE_URL = "http://10.0.2.2:82/AccountService/";
    private static final String EXTENSION = "Json/";;

    public JSONObject getObject(String sbj) throws JSONException, Exception {
        return new JSONObject(getRequest(BASE_URL + EXTENSION + sbj));
    }

    public JSONArray getArray(String sbj) throws JSONException, Exception {
        return new JSONArray(getRequest(BASE_URL + EXTENSION + sbj));
    }
}

三. 建立Android端Account模型Account.java

package com.demo;

import java.util.Date;

public class Account {

    public String Name;

    public int Age;

    public String Address;

    public Date Birthday;
}

四. 在我们的主Activity中调用刚才的方法, 在这一步中我们需要引入Google的gson 库gson-1.6.jar至我们的工程(下载地址)

package com.demo;

import java.util.Date;

import org.json.JSONArray;
import org.json.JSONObject;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

public class WebData extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        getJsonData();
    }

    public void getJsonData() {
        JsonDataGetApi api = new JsonDataGetApi();
        JSONArray jArr;
        JSONObject jobj;
        try {
            //调用GetAccountData方法
            jArr = api.getArray("GetAccountData");
            //从返回的Account Array中取出第一个数据
            jobj = jArr.getJSONObject(0);
            
          GsonBuilder gsonb = new GsonBuilder();
            //Json中的日期表达方式没有办法直接转换成我们的Date类型, 因此需要单独注册一个Date的反序列化类.
            //DateDeserializer ds = new DateDeserializer();
            //给GsonBuilder方法单独指定Date类型的反序列化方法
              //gsonb.registerTypeAdapter(Date.class, ds);
            
            Gson gson = gsonb.create();

            Account account = gson.fromJson(jobj.toString(), Account.class);

            Log.d("LOG_CAT", jobj.toString());
            ((TextView) findViewById(R.id.Name)).setText(account.Name);
            ((TextView) findViewById(R.id.Age)).setText(account.Age);
            ((TextView) findViewById(R.id.Birthday)).setText(account.Birthday
                    .toGMTString());
            ((TextView) findViewById(R.id.Address)).setText(account.Address);

        } catch (Exception e) {
            Toast.makeText(getApplicationContext(), e.getMessage(),
                    Toast.LENGTH_LONG).show();
            e.printStackTrace();
            TextView movie_Address = (TextView) findViewById(R.id.Address);
            movie_Address.setText(e.getMessage());
        }
    }
}

五.我们开始构建UI

打开layout下的main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView android:id="@+id/Name" android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    <TextView android:id="@+id/Age" android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    <TextView android:id="@+id/Birthday" android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    <TextView android:id="@+id/Address" android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

在配置好RunConfiguration之后,我们开始运行程序,  查看Log发现有以下错误,

image

意思是说访问被禁止,也就是未授权访问,  其意思并不是我们的服务未授权, 因为Andriod具有很好的很好很好的安全机制, 我们要访问网络必须要经过授权才可以;

我们打开res目录下AndroidManifest.xml, 注意字体加粗放大的那句, 就是给我们的程序加入Internet的访问授权.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.demo"
      android:versionCode="1"
      android:versionName="1.0">
      
      <uses-permission android:name="android.permission.INTERNET"></uses-permission>

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".WebData"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
</manifest>

再次运行程序, 会发现显示如下:

image

image

从上图中的statuscode = 200来看,说明我们的请求已经成功, 问题出现在Json Parse(Json数据转换/反序列化/格式化)的过程中, 我们现在把从服务器传过来的数据拿出来看看, 在浏览器输入我们的服务地址: http://localhost:82/AccountService/Json/GetAccountData

[
 {
     "Address": "YouYi East Road",
     "Age": 56,
     "Birthday": "/Date(1298605481453+0800)/",
     "Name": "Bill Gates"
 },
 {
     "Address": "YouYi West Road",
     "Age": 57,
     "Birthday": "/Date(1298605481453+0800)/",
     "Name": "Steve Paul Jobs"
 },
 {
     "Address": "YouYi North Road",
     "Age": 65,
     "Birthday": "/Date(1298605481453+0800)/",
     "Name": "John D. Rockefeller"
 }
]

我们发现其中的Birthday的结果并非我们想象中yyyy-mm-dd HH:mm:ss类型, 究其原因可以查看MSDN文章《JavaScript 和 .NET 中的 JavaScript Object Notation (JSON) 简介

现在我们给我们的GsonBuilder指定Date的序列化方法, 先增加一个Date反序列化的类DateDeserializer.java

package com.demo;

import java.lang.reflect.Type;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;

public class DateDeserializer implements JsonDeserializer<Date> {
    public Date deserialize(JsonElement json, Type typeOfT,
            JsonDeserializationContext context) throws JsonParseException {
        String JSONDateToMilliseconds = "\\/(Date\\((.*?)(\\+.*)?\\))\\/";
        Pattern pattern = Pattern.compile(JSONDateToMilliseconds);
        Matcher matcher = pattern.matcher(json.getAsJsonPrimitive()
                .getAsString());
        String result = matcher.replaceAll("$2");
        return new Date(new Long(result));
    }
}

其次修改Activity类中的GetDate方法如下, 注意其中加粗的部分.

    public void getJsonData() {
        JsonDataGetApi api = new JsonDataGetApi();
        JSONArray jArr;
        JSONObject jobj;
        try {
            //调用GetAccountData方法
            jArr = api.getArray("GetAccountData");
            //从返回的Account Array中取出第一个数据
            jobj = jArr.getJSONObject(0);
            
            GsonBuilder gsonb = new GsonBuilder();
            //Json中的日期表达方式没有办法直接转换成我们的Date类型, 因此需要单独注册一个Date的反序列化类.
            DateDeserializer ds = new DateDeserializer();
            //给GsonBuilder方法单独指定Date类型的反序列化方法
              gsonb.registerTypeAdapter(Date.class, ds);
            
            Gson gson = gsonb.create();

            Account account = gson.fromJson(jobj.toString(), Account.class);

            Log.d("LOG_CAT", jobj.toString());
            ((TextView) findViewById(R.id.Name)).setText(account.Name);
            ((TextView) findViewById(R.id.Age)).setText(String.valueOf(account.Age));
            ((TextView) findViewById(R.id.Birthday)).setText(account.Birthday
                    .toGMTString());
            ((TextView) findViewById(R.id.Address)).setText(account.Address);

        } catch (Exception e) {
            Toast.makeText(getApplicationContext(), e.getMessage(),
                    Toast.LENGTH_LONG).show();
            e.printStackTrace();
        }
    }
}

我们现在再运行程序 :

image

执行成功.

示例下载

[转载]ASP.NET中UrlEncode应该用Uri.EscapeDataString()

mikel阅读(1128)

[转载]ASP.NET中UrlEncode应该用Uri.EscapeDataString() – dudu – 博客园.

今天,茄子_2008反馈他博客中的“C++” 标签失效。检查了一下代码,生成链接时用的是HttpUtility.UrlEncode(url),从链接地址获取标签时用的是 HttpUtility.UrlDecode(url),从Encode到Decode,“C++”变成了“C  ”(加号变成空格)。这是大家熟知的问题,这里我们分析一下这个问题,并给出解决方法。

先看一下问题发生的过程:

1. 原始链接:

http://www.cnblogs.com/xd502djj/tag/C++/

2. HttpUtility.UrlEncode之后,得到:

http://www.cnblogs.com/xd502djj/tag/C%2b%2b/

3. Request.RawUrl,得到:

http://www.cnblogs.com/xd502djj/tag/C++/

4. HttpUtility.UrlDecode,得到:

http://www.cnblogs.com/xd502djj/tag/C /

上面第3步已经得到正确的结果,第4步的UrlDecode反而将加号变为了空格。

看来解决方法很简单,取消多此一举的UrlDecode,开始我们也是这么干的。过了一段时间,有用户反映“Windows Phone”的标签失效了,变成了“Windows+Phone”。我们一查,原来是在HttpUtility.UrlEncode时,空格被转换为加 号,需要调用UrlDecode将加号还原为空格,于是又把HttpUtility.UrlDecode加上(忘了之前的“C++”标签问题)。然 后,“C++”标签又失效…这样反反复复,看似Bug很多,工作很忙,实际上就是一个Bug…

终于有一天,我们说“再也不能这样过”,开始寻找解决方案:

既然HttpUtility.UrlEncode()不能用,那在.NET中找找有没有替代品。

先找到了HttpUtility.UrlPathEncode()。嘿,有用,轻松搞定“C++”与空格问题,但是…后来发现搞不定“C#”,它没有对“#”进行编码。

继续寻找…找到了Uri.EscapeUriString(),与HttpUtility.UrlPathEncode()同样的问题。

继续寻找…终于找到了…Uri.EscapeDataString(),搞定!请看下面的测试代码:

public void UrlEncodeTest() { string url = "C++ C#"; Console.WriteLine(HttpUtility.UrlEncode(url));//C%2b%2b+C%23 Console.WriteLine(HttpUtility.UrlPathEncode(url));//C++%20C# Console.WriteLine(Uri.EscapeUriString(url));//C++%20C# Console.WriteLine(Uri.EscapeDataString(url));//C%2B%2B%20C%23 }

注:运行环境.NET4。

[转载]Android---文本中缩略图点击弹出大图效果实现

mikel阅读(981)

[转载]Android—文本中缩略图点击弹出大图效果实现 – 莴笋炒肉 – 博客园.

很久没有写博客了,这两天一直忙于一个关于考试的项目,将其中的一些效果实现的经验写下来,希望给看到的人或者给有这方面需求的人帮助。

首先来张效果图,没有经过美工处理的 实现基本功能

其实做这个项目复习了很多内容,将之前单个项目中用到的某些功能综合到一起了,例如1、自定义标题栏2、Java和JavaScript的互调3、 Activity实现仿Dialog样式4、多线程实现考试倒计时5、退出Activity时保存配置信息(考试剩余时间)6、熟悉UI布局

上面这些效果中,讲讲通过Java和JavaScript互调实现点击文本中缩略图弹出一个大图。

最开始想实现这种效果的时候就想到了TextView控件,因为TextView通过Html这个类可以在文本中插入图片。但是有个问题困扰着我, 一个文本中的图片个数是不确定的,我怎样在一个TextView中添加多个图片(这个好解,通过TextView的append方法可以拼接任意张,因为 这里的缩略图都是相同的,但这却无法定位我点击了那个缩略图,应该弹出那个大图)。最后实在没办法,我想到了WebView这个控件,通过它加载一个本地 html页面,在其中通过给<image />对象添加onclick事件,传递一个imgSrc参数给Java方法,透过Java方法打开一个Activity来显示图片,ok,整个要实 现的效果就完成了。So Easy,虽然最后实现这个效果没多少代码,但是从TextView这个控件一路走来到WebView,也耗费了将近一天的功夫,但是这个过程却非常值得 享受,喔,我又懂得了更多。

下面通过代码一步一步来解析:首先是准备asset中的本地html文件。

<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body onload="window.JsUseJave.onLoad()"> Write a recount for your newsletter using the notes given _____ <a onClick='setIv("/sdcard/IMG001.png")' > <img id="ig"/></a> <script language="javascript"> function load(src){ document.getElementById('ig').src=src; } function setIv(src){ window.JsUseJave.setImgSrc(src) } </script> </body> </html>

1、最开始的时候是想在进入Activity的时候就自动加载图片

wv.loadUrl("javascript:load('"+imgSrc+"'");

,但是发现一个问题在body的onload()中直接调用js:load(src)方法并没有执行。没办法只能用通过点击按钮给图片加载资源。后 来突然想到可以JavaScript和Java互调,就把这一步添加到JavaScriptInterface类中,这个类要自己定义

final class JsUseJaveInterface{ public void setImgSrc(String imgSrc){ Intent intent =new Intent(E6.this,ImageShow.class); Bundle bundle=new Bundle(); bundle.putString("imgSrc", imgSrc); intent.putExtras(bundle); startActivity(intent); } //登录加载图片 public void onLoad(){ wv.loadUrl("javascript:load('"+src+"')"); } }

设置JavaScript可调用Java

wv.addJavascriptInterface(new JsUseJaveInterface(), "JsUseJave");

在html文件body的onload事件中通过java转一步调用javascript方法中的load(src)事件。这样就能在加载Activity的时候将缩略图同时显示出来了。

2、有了前面的经验,点击缩略图弹出大图就好实现了。给<image/>添加点击事件间接的去调用Java中的一个方法重新打开一个Activity显示大图,就是上面的自定义的JsUseJavaInteface中的setImgSrc()方法。

**js调用Java中方法:window.JsUseJave.onLoad(),JsUseJave是wv.addJavascriptInterface(new JsUseJaveInterface(), “JsUseJave”);中的别名,onLoad()则是JsUseJaveInterface这个类中定义的一个方法。

[转载]理解Android系统的进程间通信原理(二)----RPC机制

mikel阅读(867)

[转载]理解Android系统的进程间通信原理(二)—-RPC机制 – 宁 静 致 远 – 博客园.

理解Android系统中的轻量级解决方案RPC的原理,需要先回顾一下JAVA中的RMI(Remote Method Invocation)这个易于使用的纯JAVA方案(用来实现分布式应用)。有关RMI的相关知识,可以通过下图来归纳:

RMI原理 (2)

Android中的RPC也是参考了JAVA中的RMI方案,这里我们再详细了解一下RPC的实现过程。

Android中的RPC机制是为了实现一个进程使用另一个进程中的远程对象,它使用了Android自己的AIDL(接口定义语言),使用户很方 便地定义出一个接口作为规范,通过一个远程Service为代理 ,客户端在绑定该远程Service过程中获取远程对象,进而使用该对象。可参考下图所示:

Android的RPC原理及应用 (2)

补充:RPC的另一个目的是对客户端只声明接口及方法,隐藏掉具体实现类,供客户端直接获取此接口实例。

实例代码:

待续

[转载]巧用WebBrowser实现代码操作网页

mikel阅读(1124)

[转载]巧用WebBrowser实现代码操作网页 – 老翁的开发随记 – 博客园.

回顾一下上一个随笔,给出了两种方法自动化操作网页

  • 篡改HTML代码,加载到WebBrowser运行
  • 使用Fiddle截获POST给服务器的URL和数据,使用HttpRequest类代码POST到服务器

今天给出另外一种方法,就是利用WinForm控件中的WebBrowser控件来操作页面。

这里我们会模拟一个搜索过程,打开百度网站,输入搜索关键字,在搜索结果中打开连接。

image

image

首先,我们建立一个Windows Form Application Project,并在界面上拉一个WebBrowser控件。

WebBrowser1的URL我们写成百度的地址

image

并设置WebBrowser加载网页完成后的事件

image

需要注意在这个项目中由于会用到一些操作HTML结点的类,需要引用一下的两个DLL。

image

然后,在后台编写WebBrowser1切换地址后的完成事件

public Form1()
{
InitializeComponent();
(webBrowser1.ActiveXInstance as SHDocVw.WebBrowser).NavigateComplete2 +=
new SHDocVw.DWebBrowserEvents2_NavigateComplete2EventHandler(Form1_NavigateComplete2);
}
void Form1_NavigateComplete2(object pDisp, ref object URL)
{
IHTMLDocument2 doc = (webBrowser1.ActiveXInstance as SHDocVw.WebBrowser).Document as IHTMLDocument2;
doc.parentWindow.execScript("window.alert=null", "JavaScript");
doc.parentWindow.execScript("window.confirm=null", "JavaScript");
doc.parentWindow.execScript("window.open=null", "JavaScript");
doc.parentWindow.execScript("window.showModalDialog=null", "javascript");
doc.parentWindow.execScript("window.close=null", "javascript");
}

当然,还有文档加载完的事件,这里需要知道百度首页搜索框的ID和搜索按钮的ID,以方便填写关键字和执行搜索。我们查看了百度首页的源代码发现了一个用于提交搜索的Form

<a href="http://images.cnblogs.com/cnblogs_com/wengyuli/201102/201102260141496080.png"><img style="background-image: none; border: 0px none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px;" title="image" alt="image" src="http://images.cnblogs.com/cnblogs_com/wengyuli/201102/201102260141499635.png" width="297" border="0" height="109"></a>
于是在代码中我们获取到kw并输入关键字”翁玉礼”,然后点击按钮su
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
HtmlDocument doc = this.webBrowser1.Document;
doc.GetElementById("kw").InnerText = "翁玉礼";
doc.GetElementById("su").InvokeMember("click");
}
代码写到这里已经提交搜索了,WebBrowser也会接收这次提交后的记过,下面我们需要在结果中打开一个连接做实验。
先查看一下搜索结果的HTML源代码

image

private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
HtmlDocument doc = this.webBrowser1.Document;
if(doc.Url.AbsoluteUri=="http://www.baidu.com/")
{
doc.GetElementById("kw").InnerText = "翁玉礼";
doc.GetElementById("su").InvokeMember("click");
}
if(doc.Url.AbsoluteUri.Contains("wd"))//根据URL中的
{
HtmlElement table = doc.GetElementById("1");//根据搜索结果的HTML表,找到第一个搜索结果的URL,这里模拟
var html = table.Children[0].Children[0].InnerHtml;
var url = html.Substring(html.IndexOf("href") + 6, html.IndexOf(" target"));
this.webBrowser1.Navigate(url);
}
}

这样您即可自动的定时运行这个APP来操作页面了。比起使用HttpRequest来POST数据到服务端是不是更好一些呢,没有cookie的烦恼,没有脚本加密算法的烦恼。

[转载]C# 谈Dictionary,SortedDictionary排序

mikel阅读(1445)

[转载]C# 谈Dictionary,SortedDictionary排序 – VvxT [Varvery] – 博客园.

使用过Dictionary的人都知道,当每一个Add里面的值都不会改变其顺序,所以 需要需要对其排序的时候就用到SortedDictionary,但SortedDictionary并不是那么理想,其默认的方式只支持正序排序,想要 反序排序时必须得靠自己重新编写代码,下面来看一个简单的例子:

测试环境为Web,如在WinForm下,调试则只需改一下输出语句即可。

如以下代码在调试时不能使用则需要引用:

using System.Linq;

using System.Collections.Generic;

1 private void TestDictionarySort() 2 { 3 SortedDictionary<string, string> sd = new SortedDictionary<string, string>(); 4 sd.Add("321", "fdsgsags"); 5 sd.Add("acb", "test test"); 6 sd.Add("1123", "lslgsgl"); 7 sd.Add("2bcd13", "value"); 9   10 foreach (KeyValuePair<string, string> item in sd) 11 { 12 Response.Write("键名:" + item.Key + " 键值:" + item.Value); 13 } 14 15 }

上面代码输出效果:

键名:1123 键值:lslgsgl
键名:2bcd13 键值:value
键名:321 键值:fdsgsags
键名:acb 键值:test test

好了,现在我们来看一下反序排序的效果,请看下面的代码:

private void TestDictionarySort() { SortedDictionary<string, string> sd = new SortedDictionary<string, string>(); sd.Add("321", "fdsgsags"); sd.Add("acb", "test test"); sd.Add("1123", "lslgsgl"); sd.Add("2bcd13", "value");   Response.Write("<br />正序排序数据:<br />"); foreach (KeyValuePair<string, string> item in sd) { Response.Write("键名:" + item.Key + " 键值:" + item.Value + "<br />"); } //重新封装到Dictionary里(PS:因为排序后我们将不在使用排序了,所以就使用Dictionary) Dictionary<string, string> dc = new Dictionary<string, string>(); foreach (KeyValuePair<string, string> item in sd.Reverse()) { dc.Add(item.Key, item.Value); } sd = null; //再看其输出结果: Response.Write("<br />反序排序数据:<br />"); foreach (KeyValuePair<string, string> item in dc) { Response.Write("键名:" + item.Key + " 键值:" + item.Value + "<br />"); } }

上面代码输出效果:

正序排序数据:
键名:1123 键值:lslgsgl
键名:2bcd13 键值:value
键名:321 键值:fdsgsags
键名:acb 键值:test test

反序排序数据:
键名:acb 键值:test test
键名:321 键值:fdsgsags
键名:2bcd13 键值:value
键名:1123 键值:lslgsgl

好了,效果实现了,欢迎大家一起讨论出一个更好的方法来,欢迎拍砖!

[转载]我的架构经验小结(五)-- 日志记录

mikel阅读(1013)

[转载]我的架构经验小结(五)-- 日志记录 – zhuweisky – 博客园.

以前写的关于架构经验方面的文章(如上一篇实战中演化的三层架构)都是从整体的角度出发的,采用全局的视角,本文我们将拉近镜头,聚焦于日志记录这一块。随着做软件的时间越长、经验积累得越来越多,就越觉得日志记录的重要。

日志记录的主要作用可以从正反两个方面来说:

(1)如果程序运行出现问题,可以通过相关日志快速定位到出问题的地方,找到问题的根源,从而快速解决问题。对于已上线的系统来说,如果出现一个 bug,通常,解决这个bug的时间主要花在定位bug上,一旦找到bug根源,修复它是非常快的。而恰当的日志记录可以帮我们迅速定位bug。

(2)确保我们的系统运行在正常状态。当我们看到日志中类似“***成功完成!”字眼时,我们就确信我们的程序很好的执行了任务,心里就会很踏实。

1.记录应用程序异常

应用程序出现异常,要么是程序bug,要么是出现了意外事件(如磁盘坏掉、网络断开等),这些都是非常严重的事情,否则怎么叫“异”常 了。我的习惯是,所有重要的应用程序异常都是必须记录的。当然,如果某异常是属于正常业务逻辑处理的一部分,则也许没必要记录。

我使用ESBasic.Loggers.IExceptionLogger来记录异常:

public interface IExceptionLogger
{
/// <summary>
/// 记录异常。
/// </summary>
/// <param name=”ee”>异常</param>
/// <param name=”methodPath”>抛出异常的目标方法。</param>
/// <param name=”genericTypes”>目标方法的类型参数。如果为非泛型方法,则传入null</param>
/// <param name=”argumentNames”>调用方法的各Parameters的名称。如果方法没有参数,则传入null</param>
/// <param name=”argumentValues”>调用方法的各Parameters的值。如果方法没有参数,则传入null</param>
void Log(Exception ee, string methodPath, Type[] genericTypes, string[] argumentNames, object[] argumentValues);
}

Log方法记录的信息非常详细,包括异常的详细内容、异常发声的方法的路径(格式通常为:命名空间.类名.方法名),调用该方法的参数名称和对应的参数值,如果为泛型方法,还要记录泛型参数的类型。

你可能有疑问说,使用起来太麻烦了!因为构造调用这个方法所需的参数比较麻烦,特别是后三个参数。是的,确实是这样,如果你是手动来使用它的话,就需要亲 手去打造每一个参数。而我,很少手动去使用它,而是让动态代理来自动使用它。动态代理会截获所有的方法抛出的异常,并构造好上述Log方法所需的所有参 数,再调用IExceptionLogger接口来记录这些信息。

ESBasic.Emit.DynamicProxy.DynamicProxyFactory的CreateEfficientAopProxy方法可以创建截获异常的代理,如其重载的某个签名如下:

public static TInterface CreateEfficientAopProxy<TInterface>(TInterface origin, IAopInterceptor aopInterceptor)

方法的第二个参数可以传入“截获子”ExceptionInterceptor对象,而ExceptionInterceptor使用IExceptionLogger接口来记录截获到的异常:

/// <summary>
/// ExceptionInterceptor 的构造函数。
/// </summary>
public ExceptionInterceptor(IExceptionLogger logger)

至于将截获的异常记录到哪里,取决于IExceptionLogger接口的实现,我的框架提供了两个实现:

(1)ESBasic提供了ESBasic.Loggers.ExceptionFileLogger 用于将异常记录到文本文件。

(2)DataRabbit提供了DataRabbit.Application.Log.AppExceptionTableLogger 用于将异常记录到数据表,数据表可以是物理数据库中的表(如SQLServer中的表),也可以是内存数据库中的表。

如果记录到物理数据库,则可以使用下列的SQL语句创建对应的数据表:

CREATE TABLE [AppException]
([AutoID] [
int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL, [ApplicationID] [varchar](10) NOT NULL,
[ExceptionType] [varchar](
20) NOT NULL,[ErrorMessage] [varchar](200) NOT NULL,[ErrorLevel] [nvarchar](10) NOT NULL,
[Location] [varchar](
100) NOT NULL,[ArgumentInfo] [varchar](200) NOT NULL,[OccureTime] [datetime] NOT NULL
CONSTRAINT [PK_AppException] PRIMARY KEY CLUSTERED ([AutoID] ASC))

如果是记录到内存数据库,可以使用对应的Entity定义:

代码

[Serializable]
public class AppException
{
public const string TableName = AppException;
public const string _AutoID = AutoID;
public const string _ApplicationID = ApplicationID;
public const string _ExceptionType = ExceptionType;
public const string _ErrorMessage = ErrorMessage;
public const string _ErrorLevel = ErrorLevel;
public const string _Location = Location;
public const string _ArgumentInfo = ArgumentInfo;
public const string _OccureTime = OccureTime;

public AppException() { }
public AppException(string _appID, string _errorType, string _errorMessage, string _location, string _errorLevel, string _argumentInfo)
{
this.applicationID = _appID ?? “”;
this.errorType = _errorType ?? “”;
this.errorMessage = _errorMessage ?? “”;
this.location = _location ?? “”;
this.errorLevel = _errorLevel ?? “”;
this.argumentInfo = _argumentInfo ?? “”;
}

#region AutoID
private System.Int32 m_AutoID = 0;
public System.Int32 AutoID
{
get
{
return this.m_AutoID;
}
set
{
this.m_AutoID = value;
}
}
#endregion

#region ApplicationID
private string applicationID = “”;
/// <summary>
/// 出现异常的应用程序的标志。
/// </summary>
public string ApplicationID
{
get { return applicationID; }
set { applicationID = value; }
}
#endregion

#region ExceptionType
private string errorType = “”;
/// <summary>
/// 异常的类型。如NullObjectReference。
/// </summary>
public string ExceptionType
{
get { return errorType; }
set { errorType = value; }
}
#endregion

#region ErrorMessage
private string errorMessage = “”;
/// <summary>
/// 异常的详细信息。
/// </summary>
public string ErrorMessage
{
get { return errorMessage; }
set { errorMessage = value; }
}
#endregion

#region ErrorLevel
private string errorLevel = “”;
/// <summary>
/// 异常的级别 — 高、中、低。
/// </summary>
public string ErrorLevel
{
get { return errorLevel; }
set { errorLevel = value; }
}
#endregion

#region Location
private string location = “”;
/// <summary>
/// 出现异常的具体位置,精确到方法名称。
/// </summary>
public string Location
{
get { return location; }
set { location = value; }
}
#endregion

#region ArgumentInfo
private string argumentInfo = “”;
/// <summary>
/// 抛出异常时方法的调用参数信息。
/// </summary>
public string ArgumentInfo
{
get { return argumentInfo; }
set { argumentInfo = value; }
}
#endregion

#region OccureTime
private DateTime occureTime = DateTime.Now;
/// <summary>
/// 异常发生的时间。
/// </summary>
public DateTime OccureTime
{
get { return occureTime; }
set { occureTime = value; }
}
#endregion
}

2.记录数据库访问异常

当程序执行SQL语句时抛出的数据访问异常,也是属于异常的一种,当然也可以通过上面的IExceptionLogger来将其记录到文本文件或数据表。DataRabbit框架内部会自动截获所有的数据库访问异常,并调用IExceptionLogger来记录它。

void Log(Exception ee, string methodPath, Type[] genericTypes, string[] argumentNames, object[] argumentValues);

对Log方法所需的调用参数是如此构造的:

(1)第一个参数直接传入截获到的Exception。

(2)第二个参数由发生异常的方法加上所执行的SQL语句构成。格式通常为:命名空间.类名.方法名@<commandText>……</commandText>。

(3)第三个参数为泛型类型,由于没有泛型,所以此处传入null即可。

(4)第四个参数由执行SQL语句所需的各个参数的名称构成。

(5)第五个参数由执行SQL语句所需的各个参数的值构成。

所以,当数据库访问抛出异常的时候,通过日志我们可以知道是哪个方法抛出的异常,并且执行的sql语句的文本是什么样的,sql参数是如何的。

3.记录方法执行的时间

在大型系统中,我们经常需要监视我们系统执行的性能状况,当出现性能问题时,我们要能够迅速地找到瓶颈在什么地方。在程序的层面上来说,就是看哪个方法执行所消耗的时间很长(当然,内存也是一个重要方面,这里我们暂且不谈)。

我使用ESBasic.Loggers.IMethodTimeLogger来记录异常:

public interface IMethodTimeLogger
{
/// <summary>
/// 记录方法执行的时间。
/// </summary>
/// <param name=”methodPath”>抛出异常的目标方法。</param>
/// <param name=”genericTypes”>目标方法的类型参数。如果为非泛型方法,则传入null</param>
/// <param name=”argumentNames”>调用方法的各Parameters的名称。如果方法没有参数,则传入null</param>
/// <param name=”argumentValues”>调用方法的各Parameters的值。如果方法没有参数,则传入null</param>
/// <param name=”millisecondsConsumed”>方法执行的时间,毫秒</param>
void Log(string methodPath, Type[] genericTypes, string[] argumentNames, object[] argumentValues, double millisecondsConsumed);
}

同样的,我们不需要手动构造方法所需的参数来调用它,而仍然可以通过上面介绍的动态代理来自动完成这些事情,只需要使用对应的截获子MethodTimeInterceptor就可以了,而MethodTimeInterceptor使用IMethodTimeLogger接口来记录方法的执行时间:

/// <summary>
/// MethodTimeInterceptor 的构造函数。
/// </summary>
public MethodTimeInterceptor(IMethodTimeLoggerlogger)

我的框架提供了ESBasic.Loggers.MethodTimeFileLogger 用于将方法执行时间记录到文本文件,并且可以设定只记录那些执行时间大于指定值(如100ms)的方法调用。

4.记录安全日志

安全日志主要用于记录与安全相关的一些操作,比如用户的登陆/退出、修改密码、上线/掉线等。通过记录这些信息我们可以跟踪用户使用系统的状况,并且可以 做一些有意义的统计。在我的框架中,我使用ESBasic.Loggers.ISecurityLogger来记录相关安全事件和安全操作。

/// <summary>
/// 用于记录安全日志。比如用户的登陆/退出、进入/注销等日志。
/// </summary>
public interface ISecurityLogger
{
/// <summary>
/// 记录安全日志。
/// </summary>
/// <param name=”userID”>进行安全操作的用户编号</param>
/// <param name=”source”>来源(比如用户的IP)</param>
/// <param name=”taskType”>安全操作的类型</param>
/// <param name=”comment”>备注</param>
void Log(string userID, string source, string taskType ,string comment);
}

比如,在ESFramework的使用中,我们可以使用ISecurityLogger来记录用户的上线/下线/掉线等信息:

/// <summary>
/// 用于将用户的上下线事件记录到用户安全日志(可以是文件、数据库、内存数据库等)。
/// </summary>
public class UserSecurityLogBridge
{
#region UserManager
private IUserManager userManager;
public IUserManager UserManager
{
set { userManager = value; }
}
#endregion

#region SecurityLogger
private ISecurityLogger securityLogger;
public ISecurityLogger SecurityLogger
{
set { securityLogger = value; }
}
#endregion

public void Initialize()
{
this.userManager.SomeOneConnected += new ESBasic.CbGeneric<UserData>(userManager_SomeOneConnected);
this.userManager.SomeOneDisconnected += new ESBasic.CbGeneric<UserData>(userManager_SomeOneDisconnected);
this.userManager.SomeOneTimeOuted += new ESBasic.CbGeneric<UserData>(userManager_SomeOneTimeOuted);
this.userManager.SomeOneBeingPushedOut += new ESBasic.CbGeneric<UserData>(userManager_SomeOneBeingPushedOut);
}

void userManager_SomeOneBeingPushedOut(UserData userData)
{
this.securityLogger.Log(userData.UserID, userData.UserAddress.ToString(), BeingPushedOut, “”);
}

void userManager_SomeOneTimeOuted(UserData userData)
{
this.securityLogger.Log(userData.UserID, userData.UserAddress.ToString(), TimeOuted, “”);
}

void userManager_SomeOneDisconnected(UserData userData)
{
this.securityLogger.Log(userData.UserID, userData.UserAddress.ToString(), Disconnected, string.Format(LogonTime:{0},DownloadBytes:{1}, userData.TimeLogon, userData.TotalDownloadBytes));
}

void userManager_SomeOneConnected(UserData userData)
{
this.securityLogger.Log(userData.UserID, userData.UserAddress.ToString(), Connected, “”);
}
}

只要实现ISecurityLogger接口我们就可以执行真正的记录动作,我们也提供了两种实现:

(1)ESBasic提供了ESBasic.Loggers.SecurityFileLogger 用于将安全日志记录到文本文件。

(2)DataRabbit提供了DataRabbit.Application.Log.SecurityOperationTableLogger用于将安全日志记录到物理数据库或内存数据库。

如果记录到物理数据库,则可以使用下列的SQL语句创建对应的数据表:

CREATE TABLE [SecurityOperation]
([AutoID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL, [ApplicationID] [varchar](10) NOT NULL, [UserID] [nvarchar](20) NOT NULL,
[Source] [nvarchar](20) NOT NULL,[TaskType] [nvarchar](20) NOT NULL,[Comment] [nvarchar](100) NOT NULL,
[OccureTime] [datetime] NOT NULL
CONSTRAINT [PK_SecurityOperation] PRIMARY KEY CLUSTERED ([AutoID] ASC))

如果是记录到内存数据库,可以使用对应的Entity定义:

View Code

[Serializable]
public class SecurityOperation
{
public SecurityOperation() { }
public SecurityOperation(string _appID, string _userID, string _source, string _taskType, string _comment)
{
this.applicationID = _appID ?? “”;
this.userID = _userID ?? “”;
this.source = _source ?? “”;
this.taskType = _taskType ?? “”;
this.comment = _comment ?? “”;
}

#region AutoID
private System.Int32 m_AutoID = 0;
public System.Int32 AutoID
{
get
{
return this.m_AutoID;
}
set
{
this.m_AutoID = value;
}
}
#endregion

#region ApplicationID
private string applicationID = “”;
/// <summary>
/// 出现异常的应用程序的标志。
/// </summary>
public string ApplicationID
{
get { return applicationID; }
set { applicationID = value; }
}
#endregion

#region UserID
private string userID = “”;
/// <summary>
/// 进行安全操作的用户编号
/// </summary>
public string UserID
{
get { return userID; }
set { userID = value; }
}
#endregion

#region Source
private string source = “”;
/// <summary>
/// 来源(比如用户的IP)
/// </summary>
public string Source
{
get { return source; }
set { source = value; }
}
#endregion

#region TaskType
private string taskType = “”;
/// <summary>
/// 安全操作的类型
/// </summary>
public string TaskType
{
get { return taskType; }
set { taskType = value; }
}
#endregion

#region Comment
private string comment = “”;
/// <summary>
/// 备注
/// </summary>
public string Comment
{
get { return comment; }
set { comment = value; }
}
#endregion

#region OccureTime
private DateTime occureTime = DateTime.Now;
/// <summary>
/// 操作发生的时间。
/// </summary>
public DateTime OccureTime
{
get { return occureTime; }
set { occureTime = value; }
}
#endregion
}

5.记录重要的业务操作

记录重要的业务操作日志也是非常重要的,比如类似某操作员修改了某个用户的权限这样关键性的操作,通过业务操作日志,当发现操作失误时,我们可以地查找到是哪个操作员在什么时间犯错而导致误操作的,这就为我们的系统安全又提供了一层监控机制。

由于业务操作类型千变万化,所以我们没有提供一个标准统一的接口来规范它,在不同的系统中,根据不同的需求,我们可能会采用不同的记录方式。当然,有些业务操作日志也许可以作为安全日志记录下来,这样就可以使用上面的ISecurityLogger接口来进行。

[转载]读《game engine architecture》有感

mikel阅读(1031)

[转载]读《game engine architecture》有感 – ustc_msra_ase – 博客园.

最近在看一本叫做《game engine architecture》的书,这本书从很细,很具体的讲解现在游戏引擎的体系结构。本书的亮点:1.讲解现代游戏引擎架构,拥有非常新的实例。包括作 者自己公司的引擎和商业引擎例如Unreal的实例。2.清楚的讲解实现细节。代码少而思想多,往往一段话就可以让你了解某个部分的实现–(来自豆瓣上 的点评)。下面来讲讲我的看法。

两星期前我没有涉足过game的任何东西,对于编程也是一知半解。凭着自己的热情和对图形编程等的兴趣开始了游戏引擎体系结构的学习。它给我的第一感觉是这个一个很庞大的软件,涉及到的内容绝对可以用“相当丰富”来形容。下面是我从很多资料中得到的这个引擎大概的容貌:

从这幅图就可以看出来这个软件是多么的给力啊。继续看每一个部分简介的时候,发现里面涉及的知识包括有图形学,人工智能,数学,物理,美术等的知识。这本书就是从下面开始往上讲各部分的内容。

今天来稍微讲一下书中的第五章关于内存管理方面的知识,内存主要从两方面来影响系统的性能,1.dynamic memory allocation,主要体现在malloc()和c++中的new关键字上,由于它是heap形式的分配,由于1它要适应各种大小的分配方式,势必会 影响系统的性能;2某些操作系统调用malloc()等的时候,会进入内核态,然后再回来,这样就增加了相应的消耗。但是这些操作又是不可避免的,那么怎 么优化呢?方法很多,想具体了解请看书的第五章第二节。2.memory access patterns,主要体现在内存的碎片上。于是就要在第一个因素解决的前提下,进行消除碎片的工作。具体的方法见书。

从这个小点就可以看到这本书是何等的详细,细致,想看这本书的话,可以到:http://books.google.com/books?id=LJ20tsePKk4C&printsec=frontcover&dq=Game+Engine+Architecture&hl=zh-CN&ei=RT9mTZrnKsrirAeNh9naCg&sa=X&oi=book_result&ct=result&resnum=1&ved=0CCsQ6AEwAA#v=onepage&q&f=false。但是从这几天学习的情况来看的话,只是一味的看书还是不行的,需要找一个开源的引擎来学习一下,推荐irrlicht,可以到:http://irrlicht.sourceforge.net/ 下载,可以到http://baike.baidu.com/view/623466.htm看一下简介。里面有源代码,大家可以学习一下。

–by xiaoming