已经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 条
26 / 08 / 2012 buling

      看了一下上一次写日志的时间,已经2个多月没有动指头写点什么了,刚打完德州月末锦标赛,26名,不理想的成绩,两把断送了,发现如果有失败,很容易破罐子破摔把事情给毁了。博客迁移到美服后发现很多时候响应很慢,而且访问量很少很少,所以买了个aliyun的vps,速度到还是不错,只是要备案,前前后后花了不少钱,折腾,一审核一周过去了,再审核一月过去了,到现在还没有通过。主要是审核的时候不能给域有解析,再民生问题,饮食问题上审核咋就没这么严肃过呢?
      6月底的时候找了个女朋友,四川的,比较喜欢四川的,生活习惯差别不大,而且四川女孩不大化妆,就喜欢素颜,粉底后的脸更真实。此女很有抱负,一直想折腾点事情,我都惭愧。一直觉得我这说话不太可能聊个妹,真正试试的时候发现还行,以后依靠姐给我找个老婆的几率比较小了,不过她也鄙视我会沦落到那个地步。7夕前夕,一直说给她买个抱抱玩具,刚好趁此机会(也是没钱了,真不知怎么花的,下个月真得几个帐),买了个蛮喜欢的,结果,前7夕前几天突然辞职,然后就回家,然后就跟贵爸去了四川,直到明天,突然好不习惯。

      这段时间在工作上也没有做很多事情,也无知的生气了几回,这个毛病,给我点时间。还是花了很多时间在android上,本来有很多总结,但因为博客备案等都推迟了,等改天整理一下吧。马上公司要搬家了,也不知是喜是悲,喜欢那边住宿,便宜干净,讨厌那边的一片寂静。而且习惯了,也很难轻易就动。
      前不久公司体检,发现了一些个小问题,周末抽时间去医院修补修补,跟打补丁似的,治疗了一个早该检查的,消化科,以前食欲不好,口气也不好,现在基本上没有了。发现小腿有血管突出,之前去按脚的时候,技师说应该到医院检查一下,下来搜索发现是静脉曲张,到医院拍了个彩超没有什么血管问题,但毕竟是静脉曲张,要治疗还得手术,医生建议10.1前,准备准备吧。
      前几天跟cd去了趟香港,给表哥买了个手机,回来路上cd说去厦门鼓浪屿很不错,很浪漫,重要的是带个女朋友,今天又看见一个团购,260+,太便宜了点吧,3天包了吃住还车费呢,明天研究研究再决定。

无标签信息 0 条