序:由于多次在Windows系统下Java开发环境安装不顺利,编写手机应用的尝试一直拖延到最近。
这个寒假我终于在自己的计算机上安装了Ubuntu系统,对我的影响确实很大。
这里长话短说,直切正题。回顾一下从一开始到第一个手机应用的诞生:本文简要总结Ubuntu系统下实现Android应用开发流程,以及最初的环境搭建。
关键字:Ubuntu/Eclipse/JDK/Android SDK/开发环境搭建/Android App/DDMS/USB调试
我的系统环境:
计算机 Lenovo Thinkpad X121e(32位的机型)
操作系统 Ubuntu 12.04 LTS
内存 3.8GiB
CPU Intel Core i3 @ 1.4GHz x4
第一部分 开发环境搭建
由于开发环境是前一段时间装的,现在已经记不清楚了,况且网上参考资料也很多,所以这里简单概括下,并列出我收藏的一些参考文献。为了节省篇幅这里不引用或翻译任何外来文字,只提供了链接以便参考。
1.安装Eclipse
一般开发Android应用使用Eclipse集成开发环境来编程(也可以用.Net+Mono的组合,这个还没有认真玩过)。
可以在终端直接输入eclipse,然后系统会提示这个还没有安装,并告诉你用什么指令可以安装。照着提示很快就能装好。
或者百度一下eclipse官方网站,下载相应的安装包(版本很多找.deb后缀、for Ubuntu,我的eclipse启动时有JUNO的字样,详见本章参考文献[1][2])。
2.安装JDK
同样可以在官方网站下载到。
安装完后在终端输入 java -version 检查一下版本是否是所需要的。
如果不是,cd /usr/bin 用 update-alternatives 命令修改 java jar javap javah 四个文件的链接以及优先级,使之最终指JDK安装位置的对应文件。(需要用sudo命令提升权限,详见本章参考文献[3])
3.安装Android SDK
参照Android官方网站中的Installing the Eclipse Plugin就可以顺利安装ADT(Android Development Tools)
打开Eclipse之后点击工具栏上的 Android SDK Manager 选择需要的API来安装。我只安装了Android 2.2 和 Android 4.0.3,因为我的手机(Meizu MX 4核)系统是从 Android 4.0.3 定制的,Android 2.2 作为系统最低要求。在 Android 4.0.3 下 SystemImage 包中我只选了ARM EABI v7a(同样因为我的手机处理器就属于ARM 7架构)。所以,简单起见,我只安装了那些自己需要的包。
提醒:安装之前的协议记得按Accept All,我一开始就是只按了Accept,以为就是接受了,其实只接受了一个包的协议,导致其他包没有安装。
4.配置AVD(Android Virtual Device)
这个步骤是在建立一个模拟器,由于模拟器启动慢的原因,我不太愿意使用。但是否应该跳过这个步骤,这篇文章中的第一个Note可以帮助你决定:Using Hardware Devices
打开Eclipse之后点击工具栏上的 Android Virtual Device Manager,然后点击 New... 。主要是根据屏幕大小、储存器容量、CPU架构方面进行配置来建立一个模拟器,帮助你一廉价的方式完成在不同配置的设备上调试。
5.为使用DDMS(Dalvik Debug Monitor Server)作准备
这个步骤也是为真机调试作准备。在Ubuntu下配置USB规则(/etc/udev/rules.d/70-android.rules)。这个文件一开始不存在, sudo gedit 70-android.rules 之后输入这样一行文字:SUBSYSTEM=="usb", SYSFS{idVendor}=="18d1", MODE="0666"
关于USB 规则的这个文件名,网络上的文献记载各不相同,这个文件名可以工作。如有异议可以自己具体搜索一下,因为这也可能跟系统平台有关系。其中要修改的就是SYSFS{idVendor}=="18d1"这一项中引号里的数值(代表了具体的设备)。这个数值是通过 lsusb 命令查得的(详见本章参考文献[4][5])。这个步骤的官方说明参考Using Hardware Devices / Setting up a Device for Development
然后重启调试器有关的服务就完成了(详见本章参考文献[4][5])。
在Eclipse中点击 Window > Open Perspective > Other... > DDMS,就可以进入DDMS视图。以后每次进入Eclipse后进入DDMS视图,Eclipse就会自动启动调试器的有关服务,而无需手动启动,这篇文章中第一段提到了这一点:Debugging from Eclipse with ADT
参考文献:
·[1]ubuntu下配置jdk+eclipse+android sdk
·[2]Ubuntu 12.04安装Java开发环境(jdk1.7 + Eclipse)
·[3]update-alternatives命令
·[4]ubuntu10.04 下通过usb在真机调试android程序的设置
·[5]步骤五的命令记录
alex@alex-ThinkPad-X121e:~$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 002 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 001 Device 003: ID 147e:1002 Upek
Bus 001 Device 004: ID 0a5c:217f Broadcom Corp. Bluetooth Controller
Bus 002 Device 003: ID 5986:01a6 Acer, Inc Lenovo Integrated Webcam
Bus 002 Device 013: ID 18d1:4e21 Google Inc. Nexus S
alex@alex-ThinkPad-X121e:~$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 002 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 001 Device 003: ID 147e:1002 Upek
Bus 001 Device 004: ID 0a5c:217f Broadcom Corp. Bluetooth Controller
Bus 002 Device 003: ID 5986:01a6 Acer, Inc Lenovo Integrated Webcam
Bus 002 Device 014: ID 18d1:4e22 Google Inc. Nexus S (debug)
alex@alex-ThinkPad-X121e:~$ cd /etc/udev
alex@alex-ThinkPad-X121e:/etc/udev$ cd rules.d
alex@alex-ThinkPad-X121e:/etc/udev/rules.d$ ls
70-persistent-cd.rules 70-persistent-net.rules README
alex@alex-ThinkPad-X121e:/etc/udev/rules.d$ sudo gedit 70-android.rules
[sudo] password for alex:
alex@alex-ThinkPad-X121e:/etc/udev/rules.d$ ls
70-android.rules 70-persistent-cd.rules 70-persistent-net.rules README
alex@alex-ThinkPad-X121e:/etc/udev/rules.d$ cat 70-android.rules
SUBSYSTEM=="usb", SYSFS{idVendor}=="18d1", MODE="0666"
alex@alex-ThinkPad-X121e:/etc/udev/rules.d$ sudo chmod a+rx /etc/udev/rules.d/70-android.rules
alex@alex-ThinkPad-X121e:/etc/udev/rules.d$ sudo service udev restart
udev stop/waiting
udev start/running, process 6898
alex@alex-ThinkPad-X121e:/etc/udev/rules.d$ cd ~
alex@alex-ThinkPad-X121e:~$ ls
asdk examples.desktop jws Ubuntu One 模板 图片 下载 桌面
code ftpshared tmp 公共的 视频 文档 音乐
alex@alex-ThinkPad-X121e:~$ cd asdk
alex@alex-ThinkPad-X121e:~/asdk$ ls
add-ons platforms samples system-images tools
extras platform-tools sources temp
alex@alex-ThinkPad-X121e:~/asdk$ cd tools
alex@alex-ThinkPad-X121e:~/asdk/tools$ ls
adb_has_moved.txt emulator64-arm jobb sqlite3
android emulator64-mips lib support
ant emulator64-x86 lint systrace
apkbuilder emulator-arm mksdcard templates
apps emulator-mips monitor traceview
ddms emulator-x86 monkeyrunner uiautomatorviewer
dmtracedump etc1tool NOTICE.txt zipalign
draw9patch hierarchyviewer proguard
emulator hprof-conv source.properties
alex@alex-ThinkPad-X121e:~/asdk/tools$ cat adb_has_moved.txt
The adb tool has moved to platform-tools/
If you don't see this directory in your SDK,
launch the SDK and AVD Manager (execute the android tool)
and install "Android SDK Platform-tools"
Please also update your PATH environment variable to
include the platform-tools/ directory, so you can
execute adb from any location.
alex@alex-ThinkPad-X121e:~/asdk/tools$ cd ..
alex@alex-ThinkPad-X121e:~/asdk$ cd platform-tools/
alex@alex-ThinkPad-X121e:~/asdk/platform-tools$ ls
aapt aidl dexdump fastboot llvm-rs-cc renderscript
adb api dx lib NOTICE.txt source.properties
alex@alex-ThinkPad-X121e:~/asdk/platform-tools$ sudo ./adb kill-server
alex@alex-ThinkPad-X121e:~/asdk/platform-tools$ ./adb devices
List of devices attached
MX21CA2ALGPES22334 device
alex@alex-ThinkPad-X121e:~/asdk/platform-tools$ ./adb root
adbd cannot run as root in production builds
alex@alex-ThinkPad-X121e:~/asdk/platform-tools$
第二部分 编写 Android 应用程序
这个部分的没有列举参考文献,因为参考文献比较好找,并且过程也比搭建环境简单。这里介绍一下我开发的第一个手机应用程序的开发过程,来示范一下开发的简要流程。由于个人对C/C++以及C#编程都很熟悉,所以Java上手很快,没有遇到周折。
1.真机运行 HelloWorld
这个步骤是为了避免一些低级错误的发生。先新建一个Project(Android Project / Android Application Project)。如果新建工程的表单中某些项有警告或错误标识(文字旁的一个小图标),说明还有一些项目需要安装(如API、System Image...),检查并完成安装(参考第一部分)。然后把手机设置成USB 调试模式,并通USB数据线连接计算机。在左侧(Java 视图)中右击工程那一项,点击 Run As > Android Application。Eclipse就会在手机上自动部署应用程序并启动。看到了跟设计视图中一样的图形界面,中间写着 Hello World 的字样,就说明一切已经准备就绪可以开始编程了。如果不能下载进去而显示了一些adb的错误,那么先进入DDMS视图来启动调试服务(参考第一部分/步骤5/第4段)。
2.设计界面
Android的UI是通过xml的形式来表示的,就类似于网页一样。在工程中找 res > layout > *.xml 就是布局有关的文件,双击打开后,Eclipse会提供可视化设计视图以及xml代码。自己摆弄一下这些空间,看看xml对用有什么变化,如果之前有过WPF、网页或其他类似的开发经验的话很快就知道怎么设计界面了,为了节省篇幅这里就不列举了。Android应用界面中静态的文本可以放在 res > values > strings.xml 当中,可以通过可视化方式设计、更改。
截图:
界面代码:
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".MainActivity" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
android:text="@string/ruletext" />
<TextView
android:id="@+id/textViewTotal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView1"
android:layout_below="@+id/textView1"
android:layout_marginTop="16dp"
android:text="@string/total"
android:textAppearance="?android:attr/textAppearanceLarge" />
<SeekBar
android:id="@+id/seekBarTotal"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textViewTotal"
android:layout_alignRight="@+id/textView1"
android:layout_below="@+id/textViewTotal"
android:max="60" />
<TextView
android:id="@+id/TextViewK"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/seekBarTotal"
android:layout_below="@+id/seekBarTotal"
android:layout_marginTop="20dp"
android:text="@string/lottery"
android:textAppearance="?android:attr/textAppearanceLarge" />
<SeekBar
android:id="@+id/SeekBarK"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/TextViewK"
android:layout_alignRight="@+id/seekBarTotal"
android:layout_below="@+id/TextViewK"
android:max="61" />
<TextView
android:id="@+id/textViewKing"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignLeft="@+id/SeekBarK"
android:layout_alignParentBottom="true"
android:layout_alignTop="@+id/buttonDo"
android:layout_marginRight="30dp"
android:text="@string/king"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="42sp" />
<Button
android:id="@+id/buttonDo"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@+id/textView1"
android:layout_below="@+id/SeekBarK"
android:layout_marginRight="25dp"
android:layout_marginTop="0dp"
android:onClick="onButtonGetKing"
android:text="@string/getking" />
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">MonkeyLottery</string>
<string name="menu_settings">Settings</string>
<string name="ruletext">猴王大选:所有猴子围坐一圈,1至K报数,凡报到K的猴子退出到圈外,如此循环,直到圈内只剩下一只猴子即猴王。</string>
<string name="total">总数 =</string>
<string name="lottery">K =</string>
<string name="king">猴王</string>
<string name="getking">计算猴王</string>
</resources>
3.订阅事件
在Android中经常采用事件侦听的方式来订阅事件。看这里的SeekBar的事件都是怎么订阅的:
MainActivity.java 片段
private SeekBar seekBarTotal,seekBarK;
private TextView textViewTotal,textViewK,textViewKing;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
seekBarTotal = (SeekBar) this.findViewById(R.id.seekBarTotal);
seekBarK = (SeekBar) this.findViewById(R.id.SeekBarK);
textViewTotal = (TextView) this.findViewById(R.id.textViewTotal);
textViewK = (TextView) this.findViewById(R.id.TextViewK);
textViewKing = (TextView) this.findViewById(R.id.textViewKing);
seekBarTotal.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override public void onStopTrackingTouch(SeekBar seekBar){}
@Override public void onStartTrackingTouch(SeekBar seekBar){}
@Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser){
textViewTotal.setText("总数 = "+Integer.toString((int)Math.pow(1.161584, progress)+1));
}});
seekBarK.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override public void onStopTrackingTouch(SeekBar seekBar){}
@Override public void onStartTrackingTouch(SeekBar seekBar){}
@Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser){
textViewK.setText("K = "+Integer.toString(progress+2));
}});
seekBarTotal.setProgress(23);//32
seekBarK.setProgress(13-3);//13
//default answer 28
}
如果缺少引用就右击那个类,选择 Quick Fix > Import ...
按钮的点击事件很容易订阅:设置下onClick属性(参考activity_main.xml最后一块代码),然后在Java中实现同名函数就可以了。(下一步骤将完成这个例子)
4.实现算法
用Java实现一个算法,让程序运行起来。这里我选择了这样一个问题作为例子:
猴王大选:所有猴子围坐一圈,1至K报数,凡报到K的猴子退出到圈外,如此循环,直到圈内只剩下一只猴子即猴王。
这是C语言习题中会列举的,典型的链表应用实例。这里的lms数组隐式地实现了利用链表的算法,为了不偏离主题,这里就忽略对这个问题的分析了,下面贴出求解的源代码,来完成整个开发流程。
MainActivity.java 片段
private short[] lms=new short[8000];
public void onButtonGetKing(View view){
int n=(int)Math.pow(1.161584, seekBarTotal.getProgress())+1,a=seekBarK.getProgress()+2,m=0;
for(int i=0;i<n-1;i++)lms[i]=(short)(i+1);
lms[n-1]=0;
for(int c=n;c>1;c--){
for(int j=1;j<a-1;j++)m=lms[m];
m=lms[m]=lms[lms[m]];
}
textViewKing.setText(Integer.toString(m+1));
}
MainActivity.java
package com.byalex.ahello;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.SeekBar;
import android.widget.TextView;
public class MainActivity extends Activity {
private SeekBar seekBarTotal,seekBarK;
private TextView textViewTotal,textViewK,textViewKing;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
seekBarTotal = (SeekBar) this.findViewById(R.id.seekBarTotal);
seekBarK = (SeekBar) this.findViewById(R.id.SeekBarK);
textViewTotal = (TextView) this.findViewById(R.id.textViewTotal);
textViewK = (TextView) this.findViewById(R.id.TextViewK);
textViewKing = (TextView) this.findViewById(R.id.textViewKing);
seekBarTotal.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override public void onStopTrackingTouch(SeekBar seekBar){}
@Override public void onStartTrackingTouch(SeekBar seekBar){}
@Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser){
textViewTotal.setText("总数 = "+Integer.toString((int)Math.pow(1.161584, progress)+1));
}});
seekBarK.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override public void onStopTrackingTouch(SeekBar seekBar){}
@Override public void onStartTrackingTouch(SeekBar seekBar){}
@Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser){
textViewK.setText("K = "+Integer.toString(progress+2));
}});
seekBarTotal.setProgress(23);//32
seekBarK.setProgress(13-3);//13
//default answer 28
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
private short[] lms=new short[8000];
public void onButtonGetKing(View view){
int n=(int)Math.pow(1.161584, seekBarTotal.getProgress())+1,a=seekBarK.getProgress()+2,m=0;
for(int i=0;i<n-1;i++)lms[i]=(short)(i+1);
lms[n-1]=0;
for(int c=n;c>1;c--){
for(int j=1;j<a-1;j++)m=lms[m];
m=lms[m]=lms[lms[m]];
}
textViewKing.setText(Integer.toString(m+1));
}
}
5.最后是一张截图:这就是我的第一个手机应用