安卓微博客户端 第二天 系统主框架的搭建

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

  首先先看一下到目前为止的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

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

原文地址:https://www.cnblogs.com/51qianrushi/p/4878284.html