zoukankan      html  css  js  c++  java
  • 安卓微博客户端 第二天 系统主框架的搭建

      从上次更博到今天过了三天了,并不是因为偷懒了,而是这一课的内容对于基础较差的我来说信息量有点过于大了,隔了这么久才勉勉强强把它吃掉。那么废话不多说,直接进入今天的内容吧。

      首先先看一下到目前为止的UI效果图:

       

      除了下面多了一个“Welcome to Sina”的TextView,也没什么变化呀。哈哈,那你就错了,上次我们这两个按钮是点不动的,这次都有各自的功能了,先输入用户名和密码点下登录试试。

      嗯,各位没有看错,就是将下面的TextView内容改变了。哈哈,可能有人要骂娘了,这不是小学生都会改的吗。嗯,的确,单纯的想改掉它很容易,不过我们这次的主题是主框架的搭建,这只是往框架里面填充了一点内容的结果,整个过程还是费了一点周折的,所以还请大家稍安勿喷。我们继续看UI

      这是点旁边Register的按钮后的结果

      这个Activity中,点击取消按钮会返回到上一个Activity中,还没有给注册按钮绑定监听器,不过我们的重点是搭建框架搭建框架搭建框架,框架搭建好了,往里面添加功能很容易的。好了,UI展示就到这里,下面开始讲解丑陋的UI背后的框架是怎么搭建的。

    ====================分割线====================

     

      这就是整个主框架的工作流程了,多了几个名词,service、handle、线程。这些对于老鸟来说应该都听出老茧了吧,不过对于像我一样刚接触安卓的小白来说还是很陌生的,嗯,下面放两个链接来给小白涨涨姿势。service、handle这两个东西很重要,不懂他们我们的项目就没有办法继续进行下去了。嗯,先把这张图看上三分钟

      http://www.360doc.com/content/14/0415/18/2793098_369238276.shtml

      http://blog.sina.com.cn/s/blog_77c6324101016jp8.html

      首先是我们的主Activit,由于在后期要对我们的UI进行刷新,所以我们Activity的定义方式也高大上了许多

    public class LoginActivity extends AppCompatActivity implements IWeiboActivity

      IWeiboActivity接口中定义了操作UI的方法  

    public interface IWeiboActivity {
    
    //    初始化数据
        void init();
    
    //    刷新UI
        void refresh(Object...params);
    }

      LoginActivity的入口还是onCreate方法

    protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.login);
    
            loginButton = (Button)findViewById(R.id.LoginButton);
            registerButton = (Button)findViewById(R.id.RegisrerButton);
    
            Intent intent = new Intent(this, MainService.class);
    
            startService(intent);
    
            ButtonClickListener buttonClickListener = new ButtonClickListener();
            loginButton.setOnClickListener(buttonClickListener);
            registerButton.setOnClickListener(buttonClickListener);
    
            MainService.addActivity(this);
        }

      嗯,开始定义了几个Button,这些不是重点,后面再讲。重点是我红色高亮的代码。我们在这里使用Intent启动了一个Service,也就是流程图中的MainService。安卓中Activity只负责跟用户卖萌耍怪,背后的脏活累活都是Service在干。同样先看看MainService的定义方式吧

    public class MainService extends Service implements Runnable 

      这里我们的MainService还继承了Runnable接口,但是并不能说Service是线程,它和线程半毛钱关系都没有。我上面分享的文章里面强调过了,这里在强调一遍,Service不是线程!Service不是线程!Service不是线程!好,我们继续。

      同样的,Service的主入口还是onCreate方法

    public void onCreate() {
            super.onCreate();
            isRun = true;
            Thread thread = new Thread(this);
            thread.start();
        }

      由于我们的MainService继承了Runnable接口,所以这里可以作为参数传递给Thread构造函数。这里新开了一个线程,我们再看看run方法

    public void run() {
            Task task = null;
            while (isRun)
            {
                if(!tasks.isEmpty())
                {
    //                队列不为空则赋给task并从队列中移除
                    task = tasks.poll();
                    if(task != null)
                    {
    //                task不为空就执行task,以后会实现
                        doTask(task);
                    }
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
        }

      这个线程的任务就是时刻检测当前有没有任务,有则去执行,没有就继续循环。其中tasks是一个任务队列,定义方式如下

    private static Queue<Task> tasks = new LinkedList<Task>();

      队列嘛,先进先出,数据结构都学过的,不懂的小伙伴们上网搜一搜就好了。<>这个尖括号中的东西叫做泛型,也就是这个队列中每一个元素的类型,下面是Task类的内容

      

    public class Task {
    //  任务Id
        private int taskId;
    
    //    微博登录
        public static final int WEIBO_LOGIN = 1;
    //    微博注册
        public static final int WEIBO_REGISTER = 2;
    
    //    参数
        private Map<String, Object>taskParams;
    
        public void setTaskId(int taskId) {
            this.taskId = taskId;
        }
    
        public void setTaskParams(Map<String, Object> taskParams) {
            this.taskParams = taskParams;
        }
    
    
        public int getTaskId() {
            return taskId;
        }
    
        public Map<String, Object> getTaskParams() {
            return taskParams;
        }
        public Task(int taskId, Map<String, Object>taskParams)
        {
            this.taskId = taskId;
            this.taskParams = taskParams;
        }
    
    }
    View Code

      别看这个Task内容多,其实就定义了一个任务ID"TaskId"和参数键值对taskParams还有一些静态常量,剩下的都是构造函数和set、get方法。

      那我们检测任务的工作到这里也做完了,那么谁来给我们的Service分配任务呢?其实想想就能得出答案:app是为用户服务的,所以任务肯定也是人来下达,而app中和人交互的又是UI,所以给Service下达任务的肯定是UI啦。看我们的流程图也能得出任务是UI给出的这个结论。现在又返回到LoginActivity看看他是如何下达任务的。

      在LoginActivity中定义了几个按钮,绑定了监听器,监听器内容如下

     private class ButtonClickListener implements View.OnClickListener
        {
            @Override
            public void onClick(View v) {
                Task task = null;
    
                switch (v.getId())
                {
                    case R.id.LoginButton:
                        task = new Task(Task.WEIBO_LOGIN, null);
                        MainService.newTask(task);
                        break;
    
                    case R.id.RegisrerButton:
                        task = new Task(Task.WEIBO_REGISTER, null);
                        MainService.newTask(task);
                        break;
    
                    default:
                        break;
                }
            }
        }
    View Code

      可以看到,在case中生成了一个Task对象,并使用MainService中的静态方法将它添加到了任务队列中

    //    添加任务到任务队列中
        public static void newTask(Task t)
        {
            tasks.add(t);
        }

      嗯,这里因为我们的tasks成员也是静态的,否则是不能这么用的(静态方法中用到的成员变量必须都是静态的)

      这边将任务添加到消息队列,还记得之前在MainService中开的那个线程吗?它检测到任务队列中有了新的任务,就会调用doTask方法去执行。 

    private void doTask(Task t)
        {
            Message msg = handler.obtainMessage();
            msg.what = t.getTaskId();
    
            switch (t.getTaskId())
            {
                case Task.WEIBO_LOGIN:
                    System.out.println("doTask >>>>>>  用户登录任务");
                    msg.obj = "正在登录....";
                break;
    
                case Task.WEIBO_REGISTER:
                    System.out.println("doTask >>>>>>  用户注册任务");
                    break;
    
                default:
                    break;
            }
            handler.sendMessage(msg);
        }
    View Code

      在doTask中先用handler生成了一个消息实例,将Task的Id放入msg中,又根据TaskId对任务进行进一步细分,最后将消息发送给handler。注意,这个doTask是在thread线程中的,handler

    则是在MainService中定义的

    class Myhandler extends Handler
        {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                IWeiboActivity activity = null;
                switch (msg.what)
                {
                    case Task.WEIBO_LOGIN:
    //                    更新UI
    
                        activity = (IWeiboActivity)getActivityByName("LoginActivity");
                        activity.refresh(msg.what, msg.obj);
                        break;
    //                    跳转到注册页面
                    case Task.WEIBO_REGISTER:
                        activity = (IWeiboActivity)getActivityByName("LoginActivity");
                        activity.refresh(msg.what);
                        break;
    
    
                    default:
                        break;
                }
            }
        }
    
        Myhandler handler = new Myhandler();
    View Code

      那么问题来了,为什么明明可以在doTask中对UI进行改变,为什么又要费一番周折把它写到handleMessage函数中?答案就是:安卓不允许除了Activity中的主线程以外的线程来改变UI!还想表达的一个意思就是Service中的代码其实也是启动它的Activity的主线程中的。所以,我们不能够在doTask中刷新UI。

      后面的工作就简单了,获取要改变的activity,并调用对应的refresh。这里得到的activity必须继承过IWeiboActivity接口,因为refresh等方法是在IWeiboActivity这个接口中定义的。里面用到了一个getActivityByName的方法

    private Activity getActivityByName(String name)
        {
            if(!appActivities.isEmpty())
            {
                for (Activity activity : appActivities)
                {
                    if(activity != null)
                    {
                        if(activity.getClass().getName().indexOf(name) > 0)
                        {
                            System.out.println(activity.getClass().getName());
                            return activity;
                        }
                    }
                }
            }
            return null;
        }
    View Code

      appActivities是一个ArrayList类型的变量,也是一种组织数据的类型,定义如下

    private static <Activity> appActivities = new ArrayList<Activity>();

       和Queue同理,appActivities中的元素都是Activity类型的。直到这里,我们在MainService中要做的工作也就完成了。

      最后还有点小尾巴,就是Activity中refresh函数的实现,这部分就很简单了,由于不同的任务传递的参数不同,所以这里使用了变参函数  

    public void refresh(Object... paramas) {
    
            int choose = Integer.parseInt(paramas[0].toString());
    
    
            switch (choose)
            {
                case Task.WEIBO_LOGIN:
                    Log.d("MainService", paramas[1].toString());
                    textView = (TextView)findViewById(R.id.textId);
                    textView.setText(paramas[1].toString());
                    break;
                case Task.WEIBO_REGISTER:
                    Intent intent = new Intent(this, RegisterActivity.class);
                    startActivity(intent);
                default:
                    break;
            }
    
        }
    View Code

       这就是整个主框架的设计。没错,想要单纯的改掉UI很容易,但是那只是我们模拟登录,如果真正实现登录就不会这么简单。并且有了框架我们再想加入别的功能就会特别容易,定义一个task实例添加到任务队列中,并实现具体的功能就好了,这样整个项目就很具有逻辑性,便于管理和优化。

      这次的笔记就做到这里,总结一下get到的新技能:service、handler的使用,java中的foreach语句(轻喷,Java基础不扎实),还有返回上一级activity的finish方法。由于信息量对我来说是有一点大,这次讲的逻辑也挺混乱的,欢迎各位拍砖。

      项目源码下载链接:https://files.cnblogs.com/files/51qianrushi/Iweibo.zip

      这次的内容就这么多,下次再见。转载请通知本人或注明出处

  • 相关阅读:
    PHP格式化时间戳函数分享
    The Mac App Store isn't working. How to fix?
    sqlite+ef+powertools
    部署node api的二三事
    node 写api几个简单的问题
    基于项目的简单的代码生成器
    h5跳转到app的实现
    几种常用的git命令
    发送post请求几种常见content-type类型
    cors(Cross-origin resource sharing)跨域资源共享
  • 原文地址:https://www.cnblogs.com/51qianrushi/p/4878284.html
Copyright © 2011-2022 走看看