安卓成长记(一)

安卓成长记(一)

搭配的教材是李刚的疯狂安卓讲义

我们先来看看安卓的几层结构吧。

安卓的结构一共有四层,应用程序层,应用程序框架层,下面的两层是函数库和linux内核。下面的两层应该是c语言写的。

应用程序框架层其实就是那些一堆一堆的API。比如contentProvider,TelephonyManager就是这层上面的。

其实第二层,就是应用程序框架层,可以理解为是一个接口,开发者和系统之间的接口。第三层才是真正干活的程序,包含各种的驱动程序,比如SQLite。第二层,编程的时候都是调用了这些东西提供的服务。所以第二层其实是接口层。

service是通常用于为其他组件提供后台服务或者监控其他组件的运行状态。(但是他会不会监听?和boardcast一样的功能?)

BoardcastReceiver,可以理解为是一个系统级的“监听器”。

注意这个东西:context。上下文。

activity是完全继承了这个东西,activity中的很多方法也是这个参数中的。比如一般没有对象调用的方法就是从context继承过来的。比如:startactivity就是context中的一个方法。

第二章

安卓里面的所有组件都继承了View。也就是说View是所有组件的父类。

View中的XML属性其实是最多的。

android:alpha就是View的XML属性。其实这个组件就是被继承的。大部分组件的大部分属性都是从这里继承的。

View有一个子类是ViewGroup,一般被当成是容器使用,就是布局管理器那种。

ViewGroup组件控制其 子组件 依靠的是ViewGroup.LayoutParams和ViewGroup.MarginLayotParams两个内部类。

于是这个android:layout_height就是控制子组件 在容器中的高度。但是实际高度还和android:height这个组件本身的属性有关。但是依照众多实例来看,绝大部分用的都是android:layout_height这个属性。

android:height这个叫做实际高度,android:layout_height这个叫做布局高度。
一般android:height都是android:height=“20px”,而android:layout_height为android:layout_height=“match_parent”这样。布局宽度和高度有三个属性值,match_parent,fill_parent,wrap_content

这样想来,那页边距也好弄的多了。既然是ViewGroup.MarginLayotParams这个内部类,那一定少不了layout这个字眼。
android:layout_marginBottom是距底部的边距。这个总放不到View自己的属性中吧?

View中镇还有个比较像的,就是android:paddingRight。这个是组件里面的属性应该是。在组件里面填充。这不是布局属性。

image.setImageResource(images[++currentImg % images.length]);

其实最可圈可点的就是这一个了。image的方法是image.setImageResource()方法。给image设定资源文件。image的显示的图片。

其实总共也没集中布局。绝对相对线性表格网格帧布局就这六种布局方式。

布局的gravity属性:是控制布局里面的组件的对齐方式。这个属性一般是容器组件才有的。

另外的相似的属性有:layout_gravity。这个属性是容器中的子元素的属性。

我觉得还是自己写写比较好:

<Button
        android:id="@+id/bn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/bn1"/>

一般按钮也就四个属性。id,是用的android:id=”@+id/bn1”

另外一个是text。是按钮的文本。android:text=”@string/bn1”

表格布局是继承了线性布局,表格就是个tabelrow,很简单。

表格布局中的tableRow就是一个标签,连属性也没有:

<TableRow>
            <!-- 为该表格行添加三个按钮 -->
            <Button android:id="@+id/ok2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="普通按钮"/>
            <Button android:id="@+id/ok3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="收缩的按钮"/>
            <Button android:id="@+id/ok4"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="拉伸的按钮"/>
        </TableRow>

就添加一个标签就行了,添加几个按钮代表这一行有多少列。< TableRow >这就是表格布局的一行< /TableRow>

再说一遍,子元素指定layout_gravity属性而容器指定gravity属性。


        // 定义一个线程周期性地改变currentColor变量值
        new Timer().schedule(new TimerTask()
        {
            @Override
            public void run()
            {
                // 发送一条空消息通知系统改变6个TextView组件的背景色
                handler.sendEmptyMessage(0x123);
            }
        }, 0, 200);

这个模板就能建立一个循环的任务吗?
是的,比如下面的:

new Timer().schedule(new TimerTask()
        {
            @Override
            public void run()
            {   
                System.out.println("我知道了");
            }
        }, 0, 5000);

就会循环的进行一个动作。
不过这个退出了avtivity之后还有,这不就尴尬了么。但是完全退出,就是调用onDestory()之后就没有了。

再看下面的这一段:

new Timer().schedule(new TimerTask()
        {
            @Override
            public void run()
            {   
                Toast.makeText(MainActivity.this, "hahah", 1).show();
            }
        }, 0, 5000);

会出现下面的错误:
01-09 22:29:16.327: E/AndroidRuntime(29147): Process: com.example.testxunhuan, PID: 29147
01-09 22:29:16.327: E/AndroidRuntime(29147): java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()
01-09 22:29:16.327: E/AndroidRuntime(29147): at android.os.Handler.< init>(Handler.java:200)
01-09 22:29:16.327: E/AndroidRuntime(29147): at android.os.Handler.< init>(Handler.java:114)
01-09 22:29:16.327: E/AndroidRuntime(29147): at android.widget.Toast TN.<init>(Toast.java:353)010922:29:16.327:E/AndroidRuntime(29147):atandroid.widget.Toast.<init>(Toast.java:100)010922:29:16.327:E/AndroidRuntime(29147):atandroid.widget.Toast.makeText(Toast.java:264)010922:29:16.327:E/AndroidRuntime(29147):atcom.example.testxunhuan.MainActivity 1.run(MainActivity.java:37)
01-09 22:29:16.327: E/AndroidRuntime(29147): at java.util.Timer$TimerImpl.run(Timer.java:284)

虽然我不知道这一堆logcat代表了啥,但是我知道错误是因为不能在子线程中修改UI组件。一般的是用handle去处理的。

下面讲解一下handle的机制:

来来来我们先来感性的看一下这个代码:

public class MainActivity extends Activity
{
    private int currentColor = 0;
    // 定义一个颜色数组
    final int[] colors = new int[] {
        R.color.color1,
        R.color.color2,
        R.color.color3,
        R.color.color4,
        R.color.color5,
        R.color.color6
    };
    final int[] names = new int[] {
            R.id.view01,
            R.id.view02,
            R.id.view03,
            R.id.view04,
            R.id.view05,
            R.id.view06 };
    TextView[] views = new TextView[names.length];
    //首先看handle的位置,是在onCreate()函数之前,那说明这里把Handler看成是一个组件了。
    //只不过这个组件大了点儿……
    Handler handler = new Handler()
    {
        @Override
        public void handleMessage(Message msg)
        {
            // 表明消息来自本程序所发送
            if (msg.what == 0x123)
            {
                for (int i = 0; i < names.length; i++)
                {
                    views[i].setBackgroundResource(colors[(i
                            + currentColor) % names.length]);
                }
                currentColor++;
            }
            super.handleMessage(msg);
        }
    };
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        for (int i = 0; i < names.length; i++)
        {
            views[i] = (TextView) findViewById(names[i]);
        }
        // 定义一个线程周期性地改变currentColor变量值
        //注意下面的是新建了一个线程。
        new Timer().schedule(new TimerTask()
        {
            @Override
            public void run()
            {
                // 发送一条空消息通知系统改变6个TextView组件的背景色
                handler.sendEmptyMessage(0x123);
            }
        }, 0, 200);
    }
}

再来一段:

public class MainActivity extends Activity
{
    // 该程序模拟填充长度为100的数组
    private int[] data = new int[100];
    int hasData = 0;
    // 记录ProgressBar的完成进度
    int status = 0;
    ProgressBar bar , bar2;
    // 创建一个负责更新的进度的Handler
    //还是当成组件的一个孤零零的handle
    //地址:Q:\安卓绿书的代码\02\2.6\ProgressBarTest\app\src\main\java\org\crazyit\ui
    Handler mHandler = new Handler()
    {
        //handler里面最重要的就是这段代码了,处理消息,handleMessage(Message msg)
        @Override
        public void handleMessage(Message msg)
        {
            // 表明消息是由该程序发送的,一般都会有这么一个判断的。
            if (msg.what == 0x111)
            {
                bar.setProgress(status);
                bar2.setProgress(status);
            }
        }
    };
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        bar = (ProgressBar) findViewById(R.id.bar);
        bar2 = (ProgressBar) findViewById(R.id.bar2);
        // 启动线程来执行任务
        //这里启动线程用的是new Thread()去新建一个线程,看这个模板:
//new Thread()
//{
//  public void run()
//  {
//      
//  }
//}.start();
//这样看来也是so easy的一个东西。
//在这个模板里面写东西,就是新建了一个线程。最直观的用法就是在新的线程中去处理UI组件的信息。
//但是安卓怎么去管理线程是个值得考虑的问题。

        new Thread()
        {
            public void run()
            {
                while (status < 100)
                {
                    // 获取耗时操作的完成百分比
                    status = doWork();
                    // 发送消息
                    mHandler.sendEmptyMessage(0x111);
                }
            }
        }.start();
    }
    // 模拟一个耗时的操作
    public int doWork()
    {
        里面的函数就不写了
    }
}

其实大的框架就是这样的:

//在这里新建一个变量一样的Handle,重写里面的handleMessage()函数
Handler mHandler = new Handler()
    {
        //handler里面最重要的就是这段代码了,处理消息,handleMessage(Message msg)
        @Override
        public void handleMessage(Message msg)
        {
            // 表明消息是由该程序发送的,一般都会有这么一个判断的。
            if (msg.what == 0x111)
            {
                bar.setProgress(status);
                bar2.setProgress(status);
            }
        }
    };
    //在程序里面新建一个线程,将要干的事情利用handle.sendEmptyMessage去发送给handle,handle在主线程中。
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        new Thread()
        {
            public void run()
            {
                while (status < 100)
                {
                    // 获取耗时操作的完成百分比
                    status = doWork();
                    // 发送消息
                    mHandler.sendEmptyMessage(0x111);
                }
            }
        }.start();

下面就是教科书式的讲解了。

安卓的UI操作不是线程安全的。于是安卓定了个龟腚,一定要在主线程中进行UI操作。这个主线程通常又被成为UI线程。(其实主线程就是一进入该activity时运行的线程)你要是不明白为什么handler是在主线程中,你这样写也行:

public class MainActivity extends Activity
{
    // 定义周期性显示的图片的ID
    int[] imageIds = new int[]
        {
            R.drawable.java,
            R.drawable.javaee,
            R.drawable.ajax,
            R.drawable.android,
            R.drawable.swift
        };
    int currentImageId = 0;
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        final ImageView show = (ImageView) findViewById(R.id.show);
        //这里无非就是写在下面然后加了一个final。
        //你看后边有个分号,那就说明这其实是一个组件而已,和button什么的没什么区别。
        //只不过这个叫handler的组件有神奇的能力,可以处理message消息。
        final Handler myHandler = new Handler()
        {
            @Override
            public void handleMessage(Message msg)
            {
                // 如果该消息是本程序所发送的
                if (msg.what == 0x1233)
                {
                    // 动态地修改所显示的图片
                    show.setImageResource(imageIds[currentImageId++
                            % imageIds.length]);
                }
            }
        };
        // 定义一个计时器,让该计时器周期性地执行指定任务
        //下面的解释:timerTask()的本质就是启动一条新线程,于是这条线程里面有run()方法。
        //有没有注意到new Thread()里面就是有一个run()方法?
        //来看schedule这个函数:
        new Timer().schedule(new TimerTask()
        {
            @Override
            public void run()
            {
                // 发送空消息
                myHandler.sendEmptyMessage(0x1233);
            }
        }, 0, 1200);
        //函数的原型就是:void schedule(TimerTask task, long delay, long period) 
        //也就是说外边儿的Timer其实跟循环的任务没关系,schedule()才是为循环任务起作用的。
        //不过这个函数在Timer类里面。
    }
}

timer是个啥东西?

Timers are used to schedule jobs for execution in a background process.
上面那句话是文档里面的,是说timer就是去调度任务的。
其中的schedule()方法呢?
void schedule(TimerTask task, long delay, long period)
Schedule a task for repeated fixed-delay execution after a specific delay.
注意repeated这个字儿。

这个梗讲完了,看TimerTask对象:

The TimerTask class represents a task to run at a specified time. The task may be run once or repeatedly.

就是一个线程的意思。这个线程要重写run()函数。

和handler相关的几个东西:
handler,loop,MessageQueue
handler接受并处理message对象。Looper,每个线程都会维护一个Looper对象,这个looper对象中的loop方法负责读取messagqueue里面的消息,之后交给handle处理。

looper和messagequeue是看不见的,所以感觉是handler一调用sendempty()方法的时候那边儿就收到了。其实是先发送到messagequeue里面,looper读到之后交给handler处理的。
messagequeue也不用自己去建立,looper里面自动就有了messagequeue。

但是重要的一点是,Looper不是线程自动就有的。
UI线程是自动有的,UI线程自动初始化一个Looper对象。
但是自己的子线程是必须自己创建Looper对象:

// 定义一个线程类
    class CalThread extends Thread
    {
        public Handler mHandler;
        public void run()
        {
            //这个是在新的线程中,非UI线程是不自动维护looper的,所以自己调用下面的函数创建Looper。
            //不过looper中的messagequeue是自动获取的。
            Looper.prepare();
            mHandler = new Handler()
            {
                // 定义处理消息的方法
                @Override
                public void handleMessage(Message msg)
                {

                }
            };
            Looper.loop();
        }
    }

总结起来就是:
- 每个线程只有一个looper,一个looper自己维护一个messagequeue,looper把messagequeue里面的message拿出来给handler处理。
不过在线程中使用handle是什么意思?在线程中使用handler那这个handler还能修改UI组件么?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值