今天一同事突然有个想法找到我来实现,我就先不介绍这个想法了,大意是需要自动生成android应用,中午就随便看了看,确定可行性,因为实在是太忙,只能挪到晚上来研究,终于搞定个大概了,下面做些记录
      首先需要安装ant,这个是必须的,如果已经有了就略过,
wget http://apache.dataguru.cn//ant/binaries/apache-ant-1.9.1-bin.tar.gz
export PATH=$PATH:$HOME/apache-ant/bin

      下面就直接到sdk了,我在http://developer.android.com/sdk/index.html上找了好久,结果藏的也太深了:“DOWNLOAD FOR OTHER PLATFORMS”下载sdk就行了

wget http://dl.google.com/android/android-sdk_r22.0.1-linux.tgz
tar -xzf android-sdk_r22.0.1-linux.tgz
export PATH=$PATH:$HOME/android/android-sdk/tools
#到此环境配置完了,接下来更新sdk,此动作很慢
android update sdk --no-ui #我更新到2.2就停止了
android list targets #找出自己要编译的版本,比如,我的2.2对应的target是1,那么
android create project -n hello -t 1 -p project/ -k com.mjix -a DefaultActivity #参数的解释就直接 --help吧, -t指定target
cd project
ant release #开始编译

      结果出现了一个超级坑爹的事情就是: libz.so.1: cannot open shared object file: No such file or directory,我再三检查,确信自己安装了,可以通过 1、yum -qa|grep zlib或者2、locate libz.so.1来查看,最后终于找到一位知音博客。下面就直接引用了:
因为现在的系统捏是尼玛64位滴,而该死的Android都是基于32开发的,它依赖的是32位的libz
那位同学的解决办法是export CFLAGS=-m32 后再编译zlib安装,我就偷个懒,直接yum install了一个32位的zlib。
      终于,成功的生成了:BUILD SUCCESSFUL。
接下来可以看到bin下面有一个 hello-release-unsigned.apk,未签名的apk,当然不能发布啦,好了,下面我们就来签名吧。

//生成一个自己的签名,其它自己填就好了
keytool -genkey -v -keystore hello.key -alias hello -keyalg RSA -keysize 2048 -validity 10000
//第一种签名方式
vi ant.properties //添加
key.store=hello.key
key.store.password=123456
key.alias=hello
key.alias.password=123456
//执行 ant release就可以看到 hello-release-unsigned.apk
//第二种签名方法:或者使用如下语句生成:
jarsigner -verbose -keystore hello.key -signedjar bin/hello_signed.apk ./bin/hello-release-unsigned.apk hello -keypass 123456
//最终生成的文件是bin/hello_signed.apk
//使用android sdk的zipalign工具优化已签名的apk文件
zipalign -v 4 bin/hello-release-unaligned.apk bin/hello-release-aligned.apk

下面给一些参考链接:
http://my.oschina.net/u/559701/blog/75333
http://developer.android.com/tools/devices/managing-avds-cmdline.html
http://www.android123.com.cn/androidkaifa/173.html
http://developer.android.com/tools/publishing/app-signing.html

无标签信息 2 条

      发现好久没有写过日志了,突然懒起来拽都拽不动,看着要过年了,又开售,天天成堆的群发短信弄的我焦头烂额,一晃春节马上就过年了。年前的时候接到一个33e9的短信网关开发需求,这个网关不同是,统一了三个网关上下行短信号码。其实说来也没啥,但他们要求用java开发,只有java提供的jar包是用socket开发,其它语言只能使用webservice,这么大用户量,用webservice不太现实,还好以前也写过java。
      之前有一套python开发的短信网关,主要分这么几块:1、http的api进程接受请求,短信入库,并插入日志库;2、3网关进程接收到来自http进程的信号通知,取数据发送,等待状态报告、上行消息等;3、日志进程,整理数据、发送状态、状态报告、上行消息等;4、上行服务负责把上行消息归类并通知各个上行处理插件进程。
      为了快速开发,把以前的python脚本升级了一下(主要是进程代码处理上),把网关进程换成了33e9的java代码,在python中有个threading.Event的模块非常好用,所以在java中就自造了一个

public class ThreadEvent {
    private boolean isset=false;
    private Thread _thread;
    /**
     * 事件是否被设置
     * @return
     */
    public Boolean isSet(){
        return isset;
    }
    /**
     * 设置事件
     * @return
     */
    public synchronized boolean set(){
        isset = true;
        notify();
        return isset;
    }
    /**
     * 事件等待
     * @param timeout wait in milliseconds
     * @throws Exception
     */
    public synchronized void xwait(int timeout) throws Exception{
        wait(timeout);
    }
    /**
     * 清除事件设置
     * @return
     */
    public synchronized boolean clear(){
        isset = false;
        return !isset;
    }
}

接下来是信号通知的代码,感知到数据更新后快速响应,而非sleep轮询

    @SuppressWarnings("sun.misc.Signal")
    private synchronized void init(){
        dataEvent = new ThreadEvent();
        dataEvent.set();
        SignalHandler dataSig = new SignalHandler() {
            @Override
            public void handle(Signal signal) {
                dataEvent.set();
            }
        };
        //捕捉收到短信信号
        Signal sig = new Signal("USR2");
        Signal.handle(sig, dataSig);
    }
    public void run(){
        smsBean = new SmsBean();
        msgSend = new MsgSend();
        msgSend.start();
        int num = 0;
        while(!Service.END){
            try{
                if(!dataEvent.isSet()){
                    dataEvent.xwait(20000);
                }
                //System.out.println("check test");
                num = sendSms();
                if(num<1){
                    dataEvent.clear();
                }
            }catch (Exception ex){
                dataEvent.clear();
                ex.printStackTrace();
                Logging.error("smsMain while error:%s", ex.getMessage());
            }
        }
    }

      io信号通知事件更新的做法除了实现简单,而且可以减少不必要的轮询,而且快速响应,当然缺点是只能在单机部署代码。(或者能多机,但还不知道实现),其余代码都基本可以忽略不计了。
      开发完成后,写了一个小上行处理app,用户上行短信,下发一条对用户的回答,采用的是时下最火的小黄鸡代码啦,很简单,但很好用,代码上

    def get_content(self, cont):
        url = 'http://api.simsimi.com/request.p'
        cont = cont.decode('gbk').encode('utf8')
        params = 'key=your-key&lc=ch&ft=1.00&text='+cont
        f = urllib.urlopen(url, params)
        ret = f.read()
        f.close()
        if ret:
            xret = JsonUtil.read(ret)
            ret = xret.has_key('response') and xret.get('response').decode('utf8').encode('gbk') or '谢谢你发来短信'
        else:
            ret = '谢谢你发来短信'
        return ret

      已经12点过了,但又想起一个功能点,就顺便记录一下吧,自定义listview,listview可以做出各式各样的操作,大致实现方式差不多,今天就实现一个类似微信,新消息数目提示的效果吧。以前也这么搞过,一下子把最近一段时间的积累都写一次,其实这样也有缺陷,少了错误重现和解决方法,其实种种都难免要在错误中不断尝试。下面先看效果图吧。

      实现方式,首先自定义listview的adapter,主要是对不同view的处理方式,少说话,多理会,来,代码,上。

public class ExtendAdapter extends BaseAdapter{
    private ArrayList> arrlist;
    private LayoutInflater inflater;
    private ExtendViewHolder eHolder;
    private Context context;
    private String[] from;
    private int[] to;
    private int resouseId;
    private class ExtendViewHolder {
        TextView msgNumText;
        TextView msgTitle;
        TextView msgDesc;
    }
    public ExtendAdapter(Context c, ArrayList> AlList, int resouse, String[] from, int[] to){
        this.context = c;
        this.resouseId = resouse;
        this.arrlist = AlList;
        this.from = from;
        this.to = to;
        inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }
    @Override
    public int getCount() {
        return arrlist.size();
    }
    @Override
    public Object getItem(int position) {
        return arrlist.get(position);
    }
    @Override
    public long getItemId(int position) {
        return position;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView != null) {
            eHolder = (ExtendViewHolder) convertView.getTag();
        } else {
            convertView = inflater.inflate(resouseId, null);
            eHolder = new ExtendViewHolder();
            eHolder.msgTitle = (TextView)convertView.findViewById(to[0]);
            eHolder.msgDesc = (TextView)convertView.findViewById(to[1]);
            eHolder.msgNumText = (TextView)convertView.findViewById(to[2]);
            convertView.setTag(eHolder);
        }
        HashMap appInfo = arrlist.get(position);
        if (appInfo != null) {
            String title = appInfo.get(from[0]);
            String desc = appInfo.get(from[1]);
            String msgNum = appInfo.get(from[2]);
            eHolder.msgTitle.setText(title);
            eHolder.msgDesc.setText(desc);
            if(!msgNum.equals("") && Integer.valueOf(msgNum)>0){ // 消息大于0
                eHolder.msgNumText.setVisibility(View.VISIBLE);
                eHolder.msgNumText.setText(msgNum);
            }else{
                eHolder.msgNumText.setText("0");
                eHolder.msgNumText.setVisibility(View.GONE);
            }
        }
        return convertView;
    }
}

下面来看一下activity中的代码吧,其实跟别的listview实现一样

    public void initListView(){
        msgClassList = (ListView)findViewById(R.id.msg_class_list);
        alist = new ArrayList>();
        HashMap hmap = null;
        String str;
        DbMsgList dbMsg = new DbMsgList(this);
        for(int i=0, n= StatusConfig.className.length; i();
            hmap.put("title", StatusConfig.className[i]);
            hmap.put("desc", StatusConfig.classDesc[i]);
            hmap.put("msg_num", dbMsg.getNewMsgNum(StatusConfig.classType[i]).toString());
            alist.add(hmap);
        }
        msgAdap = new ExtendAdapter(this, alist, R.layout.msg_class_list,
                new String[]{"title", "desc", "msg_num"},
                new int[]{R.id.msg_class_title, R.id.msg_class_desc, R.id.msg_class_new_num});
        msgClassList.setAdapter(msgAdap);
    }

接下来发一下layout.msg_class_list的代码吧



    
        
        
        
    
    

      在android中一般数据存储估计也就这两种方法最常用了,之前写的所有应用几乎都用了SharedPreferences来存储消息,这种方法存储消息简单,缺陷是只能存储key:value形式的数据,此方法存储的数据放入xml中的,至于路径有点忘了,有兴趣的goo一下。要注意点的是,在做数据存数的时候,选在的key加入uid之类的信息,防止多用户登陆的时候串用了。这次在写这个消息app的时候使用了sqlite来存储消息,sqlite,超小型文件数据库,不介绍了,直接来代码。
      SharedPreferences比较适合存储小型数据,如用户的账号、密码、昵称这类key-value数据

SharedPreferences spre = getSharedPreferences("user_account", MODE_PRIVATE);
uname.setText(spre.getString("uname", ""));
upwd.setText(spre.getString("upwd", ""));
SharedPreferences.Editor edit = spre.edit();
edit.putString("uname", uname.getText().toString());
edit.putString("upwd", upwd.getText().toString());
edit.commit();

代码很直白,就不解释了。下面看这次遇到的一个sqlite的应用,我就原模原样的贴代码了,也懒得处理了。

public class DbMsgList extends SQLiteOpenHelper {
    private static final String DATABASE_NAME = "db_esun_msg_list.db";
    private static final int DATABASE_VERSION = 1;
    private SQLiteDatabase _db;
    private Cursor cur;
    public DbMsgList(Context context) {
        //CursorFactory设置为null,使用默认值
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    //数据库第一次被创建时onCreate会被调用
    @Override
    public void onCreate(SQLiteDatabase db) {
        String sql = "CREATE TABLE IF NOT EXISTS esun_msg_list" +
                "(f_id INTEGER PRIMARY KEY AUTOINCREMENT, f_uname VARCHAR, f_mtid VARCHAR, " +
                "f_content TEXT, f_type VARCHAR, f_instime DATETIME, f_isread INTEGER)";
        db.execSQL(sql);
    }
    //如果DATABASE_VERSION值被改为2,系统发现现有数据库版本不同,即会调用onUpgrade
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("ALTER TABLE esun_msg_list ADD COLUMN other STRING");
    }
    public boolean addMsg(String mtid, String content, String type, String instime, int isread){
        _db = getWritableDatabase();
        String sql = "INSERT INTO esun_msg_list(f_id, f_uname, f_mtid, f_content, f_type, f_instime, f_isread)" +
                " VALUES (null, ?, ?, ?, ?, ?, ?)";
        Object[] args = {Mine.userName, mtid, content, type, instime, isread};
        _db.execSQL(sql, args);
        _db.close();
        return true;
    }
    public boolean setRead(String mtid){
        _db = getWritableDatabase();
        String sql = "UPDATE esun_msg_list SET f_isread = 1 WHERE f_uname = ? and f_mtid = ?";
        Object[] args = {Mine.userName, mtid};
        _db.execSQL(sql, args);
        _db.close();
        return true;
    }
    public boolean setReadAll(String type){
        _db = getWritableDatabase();
        String sql = "UPDATE esun_msg_list SET f_isread = 1 WHERE f_uname = ? and f_type = ?";
        Object[] args = {Mine.userName, type};
        _db.execSQL(sql, args);
        _db.close();
        return true;
    }
    public Cursor getMsg(String type, Integer offset, Integer num){
        _db = getWritableDatabase();
        String sql = "SELECT * FROM (SELECT * FROM esun_msg_list WHERE f_uname = ? and f_type = ? ORDER BY f_id DESC LIMIT ?, ?) ORDER BY f_id";
        cur = _db.rawQuery(sql, new String[]{Mine.userName, type, offset.toString(), num.toString()});
        return cur;
    }
    public boolean clearMsg(String type){
        _db = getWritableDatabase();
        String sql = "DELETE FROM esun_msg_list WHERE f_uname = ? and f_type = ?";
        Object[] args = {Mine.userName, type};
        _db.execSQL(sql, args);
        _db.close();
        return true;
    }
    public Integer getNewMsgNum(String type){
        _db = getWritableDatabase();
        String sql = "SELECT count(*) num FROM esun_msg_list WHERE f_uname = ? and f_type = ? and f_isread = 0";
        String[] args = new String[]{Mine.userName, type};
        cur = _db.rawQuery(sql, args);
        cur.moveToFirst();
        Integer num = cur.getInt(cur.getColumnIndex("num"));
        cur.close();
        _db.close();
        return num;
    }
    public void dropTable(){
        _db = getWritableDatabase();
        String dsql = "DROP TABLE IF EXISTS esun_msg_list";
        _db.execSQL(dsql);
        _db.close();
    }
    public void close(){
        Common.log("close db!");
        try{
            if(_db != null) _db.close();
            if(cur != null) cur.close();
        }catch (Exception ex){
            Common.errorReport(ex);
        }
    }
}

      第四篇了,这段时间一直在折腾域名备案,本来一切以后ok把资料寄过去的时候说资料填写不对,打个电话给客服,真tmd想大骂一顿,因为这个资料填写完成后扫描上传,通过审核了才寄过去的,好吧,我认了你们,再寄一次,正在管局等指示,原则上要1个月,猜想管局其实很闲,但碍于面子,大小也是个政府级别,你搞个申请,我至少也得缓两天给你处理吧,这跟明星耍大牌是一样的,算了,不说了,说多了怀孕咋办。

      一鼓作气吧,那就再写一篇吧,先闲扯一下,晚上看了一下《北京故事》,关于旅行的事情,突然感觉活的好没品,把一年中80%以上的空余时间给了工作,不应该这样生活,还没有结婚,还没有小孩,还感觉不到压力的时候应该出去走走,看看世界的五彩缤纷,正在计划着去趟厦门呢。
      正传,android中消息提示,无非震动、声音、呼吸灯提示,但需要判断好提示场景下的提示方式,比如,应用进入后台,应该声音提示+呼吸灯提示;应用打开,但不在当前活动,则震动提示,如果应用在当前活动界面,则不用提示。例如

    protected void handleMsgData(MsgObject msg){
        DbMsgList dbMsg = new DbMsgList(service);
        String mtid = msg.getStringItem("mtid");
        String content = msg.getStringItem("content");
        String type = msg.getStringItem("type");
        String instime = msg.getStringItem("instime");
        //添加消息到sqlite数据库
        dbMsg.addMsg(mtid, content, type, instime, 0);
        dbMsg.close();
        String title = "未知主题";
        if(StatusConfig.classTypeConf.containsKey(type)){
            title = StatusConfig.className[StatusConfig.classTypeConf.get(type)];
        }
        判断应用是否在后台
        if(!Utils.isAppOnForeground(service)){
            Utils.showMsgNotification(service, title, content);
        }
    }
    public static boolean isAppOnForeground(Context ctx){
        String packName = ctx.getPackageName();
        List appProcess = getManager(ctx).getRunningAppProcesses();
        if(appProcess == null) return false;
        //遍历所有的应用,找出自己
        for(RunningAppProcessInfo info:appProcess){
            if(info.processName.equals(packName) && info.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND){
                return true;
            }
        }
        return false;
    }
    private static NotificationManager _notificationManager;
    public static void showMsgNotification(Context ctx, String title, String content){
        try{
            if(_notificationManager == null){
                _notificationManager = (NotificationManager)ctx.getSystemService(Context.NOTIFICATION_SERVICE);
            }
            String appName = ctx.getResources().getString(R.string.app_name);
            Notification notification = new Notification(R.drawable.icon_chat, appName, System.currentTimeMillis());
            Intent nint = new Intent(ctx, MainActivity.class);
            PendingIntent pint = PendingIntent.getActivity(ctx, 0, nint, 0);
            notification.setLatestEventInfo(ctx, title, content, pint);
            notification.defaults |= Notification.DEFAULT_SOUND; //消息提示
            notification.flags |= Notification.FLAG_AUTO_CANCEL; //点击消息后,自动取消消息
            notification.flags |= Notification.FLAG_SHOW_LIGHTS; //呼吸灯提示
            _notificationManager.notify(0x001, notification); //第一个消息id保证同时只有一个提示消息
        }catch (Exception ex){
            Common.errorReport(ex);
        }
    }

      写了快大半年android了,发现写的博客数量太少了。很多东西其实都可以总结记录一下的,哎,只怪太懒,难得今天这么休闲,就一口气了。其实这篇文章介绍的主题也是今天才搞定的,初衷是,服务端发送富文本标签到客户端可以显示,TextView本来是可以显示图片的,比如微信中插入图片类似,问题是什么呢,图片异步加载的实现方式,客户端想做到用户体验非常爽需要花费大量功夫处理。废话多了就啰嗦了,写了一个自己比较好用的类,先演示使用方法吧,想用的人是不关心你的实现。

TextView htmlTextView = (TextView)findViewById(R.id.text_view);
Spanned sp = Html.fromHtml(content,
                    new HtmlImageGetter(htmlTextView, "/esun_msg", _defaultLoading),
                    null);
htmlTextView.setText(sp);

      介绍一下HtmlImageGetter析构中的三个参数,第一个是Textview对象,主要是为了加载完成后刷新才能显示图片用,第二个参数是,加载出来的图片保存到sd卡的目录,第三个参数是图片未加载时候先显示的加载中Drawable对象。怎么样,用起来还算ok吧,因为图快速实现,所以几乎都忽略了注释,下面就直接来类了

public class HtmlImageGetter implements Html.ImageGetter{
    private TextView _htmlText;
    private String _imgPath;
    private Drawable _defaultDrawable;
    public HtmlImageGetter(TextView htmlText, String imgPath, Drawable defaultDrawable){
        _htmlText = htmlText;
        _imgPath = imgPath;
        _defaultDrawable = defaultDrawable;
    }
    @Override
    public Drawable getDrawable(String imgUrl) {
        String imgKey = Common.md5(imgUrl);
        String path = Environment.getExternalStorageDirectory() + _imgPath;
        FileUtil.createPath(path);
        String[] ss = imgUrl.split("\\.");
        String imgX = ss[ss.length-1];
        imgKey = path+"/" + imgKey+"."+imgX;
        if(FileUtil.exists(imgKey)){
            Drawable drawable = FileUtil.getImageDrawable(imgKey);
            if(drawable != null){
                drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
                return drawable;
            }else{
                Common.log("load img:"+imgKey+":null");
            }
        }
        URLDrawable urlDrawable = new URLDrawable(_defaultDrawable);
        new AsyncThread(urlDrawable).execute(imgKey, imgUrl);
        return urlDrawable;
    }
    private class AsyncThread extends AsyncTask {
        private String imgKey;
        private URLDrawable _drawable;
        public AsyncThread(URLDrawable drawable){
            _drawable = drawable;
        }
        @Override
        protected Drawable doInBackground(String... strings) {
            imgKey = strings[0];
            InputStream inps = NetWork.getInputStream(strings[1]);
            if(inps == null) return _drawable;
            FileUtil.saveFile(imgKey, inps);
            Drawable drawable = Drawable.createFromPath(imgKey);
            return drawable;
        }
        public void onProgressUpdate(Integer... value) {
        }
        @Override
        protected void onPostExecute(Drawable result) {
            _drawable.setDrawable(result);
            _htmlText.setText(_htmlText.getText());
        }
    }
    public class URLDrawable extends BitmapDrawable {
        private Drawable drawable;
        public URLDrawable(Drawable defaultDraw){
            setDrawable(defaultDraw);
        }
        private void setDrawable(Drawable ndrawable){
            drawable = ndrawable;
            drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable
                    .getIntrinsicHeight());
            setBounds(0, 0, drawable.getIntrinsicWidth(), drawable
                    .getIntrinsicHeight());
        }
        @Override
        public void draw(Canvas canvas) {
            drawable.draw(canvas);
        }
    }
}

在代码中有两个封装的方法,一个是网络请求图片和保存图片到sd卡中,就简单贴贴代码了,没甚么技术含量

public class NetWork {
    public static String getHttpData(String baseUrl){
        return getHttpData(baseUrl, "GET", "", null);
    }
    public static String postHttpData(String baseUrl, String reqData){
        return getHttpData(baseUrl, "POST", reqData, null);
    }
    public static String postHttpData(String baseUrl, String reqData, HashMap propertys){
        return getHttpData(baseUrl, "POST", reqData, propertys);
    }
    /**
     * 获取赛事信息
     * @return
     */
    public static String getHttpData(String baseUrl, String method, String reqData, HashMap propertys){
        String data = "", str;
        PrintWriter outWrite = null;
        InputStream inpStream = null;
        BufferedReader reader = null;
        HttpURLConnection urlConn = null;
        try{
            URL url = new URL(baseUrl);
            urlConn = (HttpURLConnection)url.openConnection();
            //启用gzip压缩
            urlConn.addRequestProperty("Accept-Encoding", "gzip, deflate");
            urlConn.setRequestMethod(method);
            urlConn.setDoOutput(true);
            urlConn.setConnectTimeout(Config.TIME_OUT);
            if(propertys != null && !propertys.isEmpty()){
                Iterator> props = propertys.entrySet().iterator();
                Map.Entry entry;
                while (props.hasNext()){
                    entry = props.next();
                    urlConn.setRequestProperty(entry.getKey(), entry.getValue());
                }
            }
            outWrite = new PrintWriter(urlConn.getOutputStream());
            outWrite.print(reqData);
            outWrite.flush();
            urlConn.connect();
            //获取数据流
            inpStream = urlConn.getInputStream();
            String encode = urlConn.getHeaderField("Content-Encoding");
            //如果通过gzip
            if(encode !=null && encode.indexOf("gzip") != -1){
                Log.d(Config.LOG_TAG, "get data :" + encode);
                inpStream = new GZIPInputStream(inpStream);
            }else if(encode != null && encode.indexOf("deflate") != -1){
                inpStream = new InflaterInputStream(inpStream);
            }
            reader = new BufferedReader(new InputStreamReader(inpStream));
            while((str = reader.readLine()) != null){
                data += str;
            }
        }catch (MalformedURLException ex){
            Common.errorReport(ex);
        }catch (IOException ex){
            Common.errorReport(ex);
        }finally{
            if(reader !=null && urlConn!=null){
                try {
                    outWrite.close();
                    inpStream.close();
                    reader.close();
                    urlConn.disconnect();
                } catch (IOException ex) {
                    Common.errorReport(ex);
                }
            }
        }
        Log.d(Config.LOG_TAG, "[Http data]["+baseUrl+"]:" + data);
        return data;
    }
    /**
     * 获取Image信息
     * @return
     */
    public static Bitmap getBitmapData(String imgUrl){
        Bitmap bmp = null;
        Log.d(Config.LOG_TAG, "get imgage:"+imgUrl);
        InputStream inpStream = null;
        try{
            HttpGet http = new HttpGet(imgUrl);
            HttpClient client = new DefaultHttpClient();
            HttpResponse response = (HttpResponse)client.execute(http);
            HttpEntity httpEntity = response.getEntity();
            BufferedHttpEntity bufferedHttpEntity = new BufferedHttpEntity(httpEntity);
            //获取数据流
            inpStream = bufferedHttpEntity.getContent();
            bmp = BitmapFactory.decodeStream(inpStream);
        }catch (Exception ex){
            Common.errorReport(ex);
        }finally{
            if(inpStream !=null){
                try {
                    inpStream.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
        return bmp;
    }
    /**
     * 获取url的InputStream
     * @param urlStr
     * @return
     */
    public static InputStream getInputStream(String urlStr){
        Log.d(Config.LOG_TAG, "get http input:"+urlStr);
        InputStream inpStream = null;
        try{
            HttpGet http = new HttpGet(urlStr);
            HttpClient client = new DefaultHttpClient();
            HttpResponse response = (HttpResponse)client.execute(http);
            HttpEntity httpEntity = response.getEntity();
            BufferedHttpEntity bufferedHttpEntity = new BufferedHttpEntity(httpEntity);
            //获取数据流
            inpStream = bufferedHttpEntity.getContent();
        }catch (Exception ex){
            Common.errorReport(ex);
        }finally{
            if(inpStream !=null){
                try {
                    inpStream.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
        return inpStream;
    }
}
package com.esun.trade.lib;
import java.io.*;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.util.Log;
import com.esun.trade.inc.Config;
public class FileUtil {
    private static int FILE_SIZE = 4*1024;
    public static boolean hasSdcard(){
        String status = Environment.getExternalStorageState();
        if(status.equals(Environment.MEDIA_MOUNTED)){
            return true;
        }
        return false;
    }
    public static boolean createPath(String path){
        File f = new File(path);
        if(!f.exists()){
            Boolean o = f.mkdirs();
            Log.i(Config.LOG_TAG, "create dir:"+path+":"+o.toString());
            return o;
        }
        return true;
    }
    public static boolean exists(String file){
        return new File(file).exists();
    }
    public static File saveFile(String file, InputStream inputStream){
        File f = null;
        OutputStream outSm = null;
        try{
            f = new File(file);
            String path = f.getParent();
            if(!createPath(path)){
                Log.e(Config.LOG_TAG, "can't create dir:"+path);
                return null;
            }
            if(!f.exists()){
                f.createNewFile();
            }
            outSm = new FileOutputStream(f);
            byte[] buffer = new byte[FILE_SIZE];
            while((inputStream.read(buffer)) != -1){
                outSm.write(buffer);
            }
            outSm.flush();
        }catch (IOException ex) {
            Common.errorReport(ex);
            return null;
        }finally{
            try{
                if(outSm != null) outSm.close();
            }catch (IOException ex) {
                Common.errorReport(ex);
            }
        }
        Common.log("[FileUtil]save file:"+file+":"+Boolean.toString(f.exists()));
        return f;
    }
    public static Drawable getImageDrawable(String file){
        if(!exists(file)) return null;
        try{
            InputStream inp = new FileInputStream(new File(file));
            return BitmapDrawable.createFromStream(inp, "img");
        }catch (Exception ex){
            Common.errorReport(ex);
        }
        return null;
    }
}

      这两天一直在搞一个公司app的东东,收获很丰富,真到是经验都是在磨砺中积累而来。想起之前写的德州扑克的app,处理太烂了,接下来把这几天积累的经验总结一下。
      1、关于socket消息处理的问题。场景,一个应用开一个socket服务和服务器通信,但关于事件的处理,如,服务端发送来有聊天信息、游戏信息、更新信息等等,每个活动(Activity)只关心自己的事件。看处理事件走过的三个阶段
      1) 在德州扑克中第一次做app与socket通信,所以当时采用了类似插件思想的消息订阅模式,每个activity(如果需要)都对应一个model(class类)通过注册事件关心列表

//model的基础接口
public interface Model {
    /**
     * 处理消息
     * @param msg
     */
    public void handleMessage(MsgObject msg);
}
//下面这样注册信息给对应的模块
EventCenter.register(EventConfig.CONNECT_RESP, Mconnect.getInstance());
EventCenter.register(EventConfig.PING_RESP, Mconnect.getInstance());
EventCenter.register(EventConfig.TABLE_JOIN_RESP, Mtable.getInstance());

      这样看起来很好很合理,但代码写多了就问题来了,如在游戏大厅activity中收到一条游戏逻辑的消息,本来不应该处理,但处理了,这就导致可能用户退出了当前activity的时候还在响应消息。
      2) 这次写的这个消息应用刚开始也这样,前两天睡觉前仔细琢磨一下,发现个不错的办法:关心消息的每个活动都继承自一个事件父类,事件父类有对消息的基本处理方法,每个活动关心该消息就重新该消息接口。

public class EventActivity extends Activity {
    public static EventActivity self;
    public EventActivity(){
        self = this;
    }
    public void handleMessage(MsgObject msg){
        switch (msg.cmd){
            case Key.X_CONNECT:
                this.handleLogin(msg);
                break;
            case Key.MSG_DATA:
                this.handleMsgData(msg);
                break;
        }
    }
    public void handleLogin(MsgObject msg){
        Common.log("[handle_login]:"+msg.code);
    }
    public void handleMsgData(MsgObject msg){}
}
//这是初始化事件模块
public class InitEvent extends Model{
    public static EventActivity eAct = new EventActivity();
    private InitEvent(){
        EventCenter.register(Key.X_CONNECT, eAct);
        EventCenter.register(Key.MSG_DATA, eAct);
        EventCenter.register(Key.X_ACT_CHAT, eAct);
        EventCenter.register(Key.X_ACT_CHAT_GET, eAct) ;
    }
    private static InitEvent _instance;
    public void init(EventActivity eActivity){
        eAct.self = eActivity;
    }
    public static InitEvent getInstance(){
        if(_instance == null){
            _instance = new InitEvent();
        }
        return _instance;
    }
}
//在每个活动中都初始化这个模块
InitEvent.getInstance().init(this);

      这样每个消息都会发送到当前活动的相应方法来处理,绝不会出现两个activity会处理同一个消息的情况。
      3) 本来写好了应用发现不错,问题来了,如果用户把应用放入后台,应用待机后,socket会自动断开,这样就不能后台消息推送。要把socket放入后台,最简单直接的方法莫过于放置于service中,但问题当然也来了,service和activity通信的问题,如果放入后台,应用其实是不会处理service中的消息。至于service和activity的消息处理机制分两种情况,1、service通知activity,需要通过广播消息事件;2、activit通知service,需要通过Binder来实现,这个google一下就有很多了,我这里就直接代码了

public class SocketService extends Service {
    private final SocketBinder sbinder = new SocketBinder();
    private Communicate comm = Communicate.getInstance();
    @Override
    public IBinder onBind(Intent intent) {
        Common.log("service onbind");
        return sbinder;
    }
    @Override
    public void onCreate(){
        Common.log("service onCreate");
        super.onCreate();
        comm.setOnConnectListen(new Communicate.OnConnectListen() {
            @Override
            public void onConnect(Socket socket) {
                if(Mine.userName != null && !Mine.userName.equals("")){
                    MsgObject msg = Utils.getLoginPackage();
                    comm.send(msg);
                }
                sendMsg(Key.B_CONN_OPEN, "socket on connect!");
            }
            @Override
            public void onClosed(Socket socket) {
                sendMsg(Key.B_CONN_CLOSE, "socket on closed!");
            }
            @Override
            public void onReceiveMsg(byte[] content) {
                MsgObject msg = MsgFormat.getMsgObject(content);
                Common.log("【receive】:"+msg.data);
                new SocketMsg(SocketService.this).handleMessage(msg);
                Intent intent = new Intent(getResources().getString(R.string.broad_msg));
                intent.putExtra("type", Key.B_CONN_MSG);
                intent.putExtra("cmd", msg.cmd);
                intent.putExtra("data", msg.data);
                sendBroadcast(intent);
            }
        });
        comm.runAsync();
    }
    @Override
    public void onStart(Intent intent, int startId){
        super.onStart(intent, startId);
        sendMsg(Key.B_TEST_MSG, "service on start!");
        Common.log("service onstart");
        if(comm.isStop || Communicate.sconn == null || Communicate.sconn.isClosed()){
            comm.runAsync();
        }
    }
    private void sendMsg(int type, String data){
        Intent intent = new Intent(getResources().getString(R.string.broad_msg));
        intent.putExtra("type", type);
        intent.putExtra("data", data);
        sendBroadcast(intent);
    }
    /**
     * 此方法是activity调用service的方法
     **/
    public void sendToSocetMsg(MsgObject msg){
        comm.send(msg);
    }
    public class SocketBinder extends Binder{
        public SocketService getService(){
            return SocketService.this;
        }
    }
}

      在主activity中通过以下方法来初始化和开启service

    public void initService(){
        //过滤广播给Receriver
        IntentFilter filter = new IntentFilter();
        filter.addAction(getResources().getString(R.string.broad_msg));
        registerReceiver(SocketReceiver.getInstance(), filter);
        //启动服务
        Intent it = new Intent(this, SocketService.class);
        startService(it);
        //绑定activity和service
        bindService(it, serviceConn, BIND_AUTO_CREATE);
    }
    private ServiceConnection serviceConn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //保存service实例供activity调用service
            Utils.socketService = ((SocketService.SocketBinder)iBinder).getService();
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Utils.socketService = null;
        }
    };

      如果activity需要发送socket消息到服务端可以使用:Utils.socketService.sendToSocetMsg(msg)方法了,当然自己可以再定义一些别的被调用方法。
      现在对处理逻辑很清楚了吧,而且彻底把消息和处理分离。

无标签信息 0 条

      做手机应用很容易遇到这个需求,网上也有很多人实现了过程,这次刚好也遇到了,只在setThumb中添加了几句缩小按钮的代码以适应应用的需要,下面看一下实现效果吧:

      //在这里写点记事吧,就不新开了。这段时间一直忙于写android版的德州游戏,最初花了一周多点时间就出了一个粗略版应用,接下来大刀阔斧的把所有布局重新设计,大约改版了3-4次,主要是之前没有这方面经验,为了适应动画需求而做,终于在上周历时7-8天出了个动画版本。之前他们开会也确认德州android最终会交由北京开发,但我们还是继续保持,主要是为了做技术积累。
      星哥有天早上来了兴奋的给我说他终于想到要做什么了,把500wan小秘书改成app应用:1、节约短信成本;2、消息实时触达;3、内容多样化。恩,确实很好的一个设计,而且我也觉得我们有那个能力去实现。希望这个想法可以早日提上开发历程。
      那天看见有人在公司传阅这个博客,之前也出现过这样的事情,以前写过一些比较上火的文章,提到了很多人和事,最后不得不删除,前端时间还是想写一些,主要是为了记录走过的这些路,经历的这些点滴,所以在首页加了公司访问限制,对不住了。还是很想让看我博客的人知道不要传阅,看看娱乐还是可以的。
      好了,算是插了一段广告吧,时间有点晚了,贴完代码睡觉。

package com.esun.texas.view;
import android.content.Context;
import android.graphics.*;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AbsSeekBar;
import android.widget.SeekBar;
public class VerticalSeekbar extends SeekBar {
    private Drawable mThumb;
    private int height;
    private int width;
    public interface OnSeekBarChangeListener {
        void onProgressChanged(VerticalSeekbar VerticalSeekBar, int progress, boolean fromUser);
        void onStartTrackingTouch(VerticalSeekbar VerticalSeekBar);
        void onStopTrackingTouch(VerticalSeekbar VerticalSeekBar);
    }
    private OnSeekBarChangeListener mOnSeekBarChangeListener;
    public VerticalSeekbar(Context context) {
        this(context, null);
    }
    public VerticalSeekbar(Context context, AttributeSet attrs) {
        this(context, attrs, android.R.attr.seekBarStyle);
    }
    public VerticalSeekbar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    public void setOnSeekBarChangeListener(OnSeekBarChangeListener l) {
        mOnSeekBarChangeListener = l;
    }
    void onStartTrackingTouch() {
        if (mOnSeekBarChangeListener != null) {
            mOnSeekBarChangeListener.onStartTrackingTouch(this);
        }
    }
    void onStopTrackingTouch() {
        if (mOnSeekBarChangeListener != null) {
            mOnSeekBarChangeListener.onStopTrackingTouch(this);
        }
    }
    void onProgressRefresh(float scale, boolean fromUser) {
        Drawable thumb = mThumb;
        if (thumb != null) {
            setThumbPos(getHeight(), thumb, scale, Integer.MIN_VALUE);
            invalidate();
        }
        if (mOnSeekBarChangeListener != null) {
            mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromUser);
        }
    }
    private void setThumbPos(int w, Drawable thumb, float scale, int gap) {
        int available = w+getPaddingLeft()-getPaddingRight();
        int thumbWidth = thumb.getIntrinsicWidth();
        int thumbHeight = thumb.getIntrinsicHeight();
        available -= thumbWidth;
        // The extra space for the thumb to move on the track
        available += getThumbOffset() * 2;
        int thumbPos = (int) (scale * available);
        int topBound, bottomBound;
        if (gap == Integer.MIN_VALUE) {
            Rect oldBounds = thumb.getBounds();
            topBound = oldBounds.top;
            bottomBound = oldBounds.bottom;
        } else {
            topBound = gap;
            bottomBound = gap + thumbHeight;
        }
        thumb.setBounds(thumbPos, topBound, thumbPos + thumbWidth, bottomBound);
    }
    protected void onDraw(Canvas c)
    {
        c.rotate(-90);
        c.translate(-height,0);
        super.onDraw(c);
    }
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
        height = View.MeasureSpec.getSize(heightMeasureSpec);
        width = View.MeasureSpec.getSize(widthMeasureSpec);
        this.setMeasuredDimension(width, height);
    }
    private Bitmap getThumbImage(Bitmap bmp, int width, int height){
        int w = bmp.getWidth(); int h = bmp.getHeight();
        Matrix m = new Matrix();
        m.postScale((float)width/w, (float)height/h);
        Log.d("test", "__"+w+"___"+h+"___"+width+"___"+height);
        return Bitmap.createBitmap(bmp, 0, 0, w, h, m, true);
    }
    @Override
    public void setThumb(Drawable thumb){
        Bitmap bmp = ((BitmapDrawable) thumb).getBitmap();
        int w = 25;
        int h = (int)(w*2.2766);
        bmp = getThumbImage(bmp, w, h);
        mThumb = new BitmapDrawable(bmp);
        super.setThumb(mThumb);
    }
    protected void onSizeChanged(int w, int h, int oldw, int oldh)
    {
        super.onSizeChanged(h, w, oldw, oldh);
    }
    public boolean onTouchEvent(MotionEvent event) {
        if (!isEnabled()) {
            return false;
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                setPressed(true);
                onStartTrackingTouch();
                trackTouchEvent(event);
                break;
            case MotionEvent.ACTION_MOVE:
                trackTouchEvent(event);
                attemptClaimDrag();
                break;
            case MotionEvent.ACTION_UP:
                trackTouchEvent(event);
                onStopTrackingTouch();
                setPressed(false);
                break;
            case MotionEvent.ACTION_CANCEL:
                onStopTrackingTouch();
                setPressed(false);
                break;
        }
        return true;
    }
    private void trackTouchEvent(MotionEvent event) {
        final int Height = getHeight();
        final int available = Height - getPaddingBottom() - getPaddingTop();
        int Y = (int)event.getY();
        float scale;
        float progress = 0;
        if (Y > Height - getPaddingBottom()) {
            scale = 0.0f;
        } else if (Y  < getPaddingTop()) {
            scale = 1.0f;
        } else {
            scale = (float)(Height - getPaddingBottom()-Y) / (float)available;
        }
        final int max = getMax();
        progress = scale * max;
        setProgress((int) progress);
    }
    private void attemptClaimDrag() {
        if (getParent() != null) {
            getParent().requestDisallowInterceptTouchEvent(true);
        }
    }
    public boolean dispatchKeyEvent(KeyEvent event) {
        if(event.getAction()==KeyEvent.ACTION_DOWN)
        {
            KeyEvent newEvent = null;
            switch(event.getKeyCode())
            {
                case KeyEvent.KEYCODE_DPAD_UP:
                    newEvent = new KeyEvent(KeyEvent.ACTION_DOWN,KeyEvent.KEYCODE_DPAD_RIGHT);
                    break;
                case KeyEvent.KEYCODE_DPAD_DOWN:
                    newEvent = new KeyEvent(KeyEvent.ACTION_DOWN,KeyEvent.KEYCODE_DPAD_LEFT);
                    break;
                case KeyEvent.KEYCODE_DPAD_LEFT:
                    newEvent = new KeyEvent(KeyEvent.ACTION_DOWN,KeyEvent.KEYCODE_DPAD_DOWN);
                    break;
                case KeyEvent.KEYCODE_DPAD_RIGHT:
                    newEvent = new KeyEvent(KeyEvent.ACTION_DOWN,KeyEvent.KEYCODE_DPAD_UP);
                    break;
                default:
                    newEvent = new KeyEvent(KeyEvent.ACTION_DOWN,event.getKeyCode());
                    break;
            }
            return newEvent.dispatch(this);
        }
        return false;
    }
}
无标签信息 0 条

      项目需要加入声音,之前也写过一个小app,用了MediaPlayer,但是这次这个声音极其短,播放的时候没有反应只有一条日志,忘了内容了,大意就是内容太短,最后发现播放这类提示应应该是用SoundPool来播放,应用出来后,玩了一段时间发现没声音了,奇怪,什么意思,赶紧搜搜,下面这文章写的很不错:http://blog.sina.com.cn/s/blog_71d26ff00100uzci.html,把内容copy过来存档:
    SoundPool的用法就不再提了,网上资料多,就说说在实际应用中可能会遇到的一些奇葩的问题,这些问题应该是和底层实现上有关系。
1、AudioFlinger could not create track, status: -12
    SoundPool即音效池,在创建的时候 maxStream这个参数代表能够同时播放的最大音效数,这里切忌合理使用,写的太大后会报AudioFlinger could not create track, status: -12 。。。。一旦报了这个错,你就听不到声音了,呵呵。
2、256个音效
    当调用load方法的时候实际就是把音效加载到了 SoundPool中,此时返回的streamId其实就是该音效在SoundPool中的Id,这个ID从0还是1来着(有点记不清了)递增,不过要注意的是,不要超过 256 这个临界点。也就是说第257个声音加载进去后,调用play方法其实是播不出来的,说不定还会挤掉一些前面加载好的声音。这个256的限制通过查看SDK源码基本就能了解清楚,它底层就那么实现的,用一个类似堆栈来存。
3、unload方法和release方法
    如果你音效多,也不要指望unload方法来清除掉一些音效后再load新的进去,虽然unload后音效卸载了,但是前面分给它在SoundPool里面的Id可没有释放掉,也就是说这个时候你load新的进去只会在后面继续累加,然后累加多了就超过256了,然后就就听不到声音,然后就没有然后了。要想彻底清掉前面的音效请使用release方法,它会连内存中占用的资源一起释放掉。
其他还有点什么呢,load需要一点点时间,load后不要马上unload,load ---play--unload的做法并不可取,不要load太大的音效,它只会申请1M的内存空间。SoundPool出错后通常会看到retuen的值是0。

      最后我亲测了一次,发现果然是load值超过了256后就没有声音了,所以就用了一个HashMap把这个声音值存储了,结果发现还是解决不了问题,只能在值快到达200时主动release掉,release掉后要重新测试化播放器。下面把我的代码贴一下:

package com.esun.texas.controller;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.SoundPool;
import android.util.Log;
import com.esun.texas.R;
import com.esun.texas.config.Config;
import com.esun.texas.lib.Common;
import com.esun.texas.model.Mactivity;
import java.util.HashMap;
/**
 * Created by IntelliJ IDEA.
 * User: tuyl
 * Date: 12-4-1
 * Time: 下午4:15
 * To change this template use File | Settings | File Templates.
 */
public class Csound {
    public static MediaPlayer player = new MediaPlayer();
    public static SoundPool spool = new SoundPool(10, AudioManager.STREAM_SYSTEM, 5);
    public static AudioManager mgr = (AudioManager)Mactivity.NEW_ACTIVITY.getSystemService(Context.AUDIO_SERVICE);
    public static int CARD_SOUND = R.raw.card;
    public static int CHECK_SOUND = R.raw.check;
    public static int CHIPS_SOUND = R.raw.chips;
    public static int GATHER_CHIPS_SOUND = R.raw.chips_gathering;
    public static int FOLD_SOUND = R.raw.fold;
    public static int MY_TURN_SOUND = R.raw.myturn;
    public static int ONTURN_SOUND = R.raw.onturn;
    public static boolean isMute = false;
    public static HashMap sounds = new HashMap();
    private static int getSound(int resId){
        if(!sounds.containsKey(resId)){
            int spload = spool.load(Mactivity.NEW_ACTIVITY, resId, 1);
            sounds.put(resId, spload);
            Log.d(Config.LOG_TAG, "init sound:"+resId);
        }
        return sounds.get(resId);
    }
    /**
     * 播放短音效
     * @param resId
     */
    public static void play(int resId){
        try{
            if(isMute) return; //静音
            final int spload = spool.load(Mactivity.NEW_ACTIVITY, resId, 1);
            Log.d(Config.LOG_TAG, "play sound:"+resId+"___"+spload);
            spool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener(){
                public void onLoadComplete(android.media.SoundPool soundPool, int i, int i1){
                    try{
                        float streamVolumeCurrent=mgr.getStreamVolume(AudioManager.STREAM_MUSIC);
                        float streamVolumeMax=mgr.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
                        float volume=streamVolumeCurrent/streamVolumeMax;
                        spool.play(spload, volume, volume, 0, 0, 1);
                    }catch (Exception ex){
                        Common.errorReport("Csound.play.onLoadComplete", ex);
                    }
                }
            });
            if(spload > 200){
                spool.release();
                spool = new SoundPool(10, AudioManager.STREAM_SYSTEM, 5);
            }
        }catch (Exception ex){
            Common.errorReport("Csound.play", ex);
        }
    }
    public static void playMusic(int resSound){
        try{
            if(isMute) return; //静音
            Activity act = Mactivity.NEW_ACTIVITY;
            AssetFileDescriptor afd = act.getResources().openRawResourceFd(resSound);
            player.reset();
            player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength());
            player.prepare();
            player.start();
            afd.close();
            player.setOnCompletionListener(new MediaPlayer.OnCompletionListener(){
                public void onCompletion(MediaPlayer arg0){
                    player.release(); //释放资源
                }
            });
        }catch (Exception ex){
            Common.errorReport("Csound.playMusic", ex);
        }
    }
}
无标签信息 0 条
20 / 04 / 2012 buling

      刚开始写的时候就是直接从网上copy,因为像快速出原型,结果花费了好多时间做调试,代码离想象中总是差很远,最后我实在受不了了,只能自己一个字一个字的码出了一个类库,贴出来,分享给有用的人。使用方法:

RC4 rc4 = new RC4(Info.RC4_KEY);
String data = rc4..make("body");

这段代码我想解释都解释不了。

package com.esun.texas.lib;
public class RC4{
    public int[] box = new int[256];
    public RC4(String key){
        byte[] k = key.getBytes();
        int i = 0, x = 0, t = 0, l = k.length;
        for(i=0; i<256; i++){
            box[i] = i;
        }
        for(i=0; i<256; i++){
            x = (x+box[i]+k[i%l]) % 256;
            t = box[x];
            box[x] = box[i];
            box[i] = t;
        }
    }
    public byte[] make(byte[] data){
        int t, o, i=0, j = 0, l = data.length;
        byte[] out = new byte[l];
        int[] ibox = new int[256];
        System.arraycopy(box, 0, ibox, 0, 256);
        for(int c=0; c
            
无标签信息 0 条