移动应用开发 课程设计(大作业) by:Luz
题目信息
题目:
实现“获取手机通话记录”功能的Android程序。
功能要求:
1) 在模拟器中创建联系人并分别进行通话,示例如下:
2) 在自行开发的APP中,以ListView组件形式显示ID、联系人姓名及联系人电话号码,示例如下:
课程设计结果提交方式:
17周结束前将完整android项目代码电子版+设计文档纸质版统一上交。
设计文档要求:
文档具体书写内容要求如下:
1) 系统的需求分析
2) 系统的概要设计
3) 设计与实现部分
4) 运行画面截图
5)每一部分附上关键性代码
6)课程设计总结体会
评分标准:
根据提交的设计文档、程序功能的实现进行考核:
1) 无任何文档,无程序,得 0 分;
2) 文档混乱,没有思路,程序不能运行,10-20分;
3) 文档描述清晰,程序实现了基本功能,20-30分;
4) 文档描述清晰准确,思路清晰,程序实现了要求的所有功能,30-50分;
5)文档完备,设计合理有创新,报告清晰明确,深入分析了自己进行实验的体会感想,程序实现了全部功能,功能完善,并有其它的创新实现,50-60分。
课程设计报告
目录
系统需求分析
环境需求分析
开发环境:Eclipse
开发语言:JAVA
手机运行:Android2.3.3以及上版本开发软件(API10)
所需插件:JDK
基本功能说明
本软件需要实现从手机中获取通话记录的功能。其中,通话记录指手机自带的电话(拨号器)所拨出与收到电话的记录。在自行开发的APP中,以ListView组件形式显示ID、联系人姓名及联系人电话号码。
拓展功能说明
基于线程循环的程序界面实时刷新(通话记录实时获取)
若无此功能,则程序只会在加载时读取一次通话记录。由于只在onCreate中触发更新事件,因此在手机的通话记录更新后(如在程序使用时收到了一通电话),程序并不会再次更新。只有杀死程序进程重启程序后才会再次从系统中获取通话记录。
加入功能后,当系统中的通话记录改变时,程序立即更新,无需重启程序。由于在单独的线程中更新界面,程序主线程不会阻塞,后续程序功能的开展。
基于Service的软件背景音乐
软件播放背景音乐,让使用者在查看通话记录时放松心情。
系统概要设计
界面设计
本程序主界面显示ListView,在ListView中显示ID、联系人姓名、联系人电话号码。当 通话记录中所包含的电话号码不存在于通讯录列表中时,联系人姓名显示为“未知”。并添加背景图,简洁美观。
通话记录获取功能设计
在安卓系统中,每个应用程序的数据都是采用私有的形式进行操作的,不管这些数据是用文件还是数据库进行保存,都不能被外部应用程序访问。但是在本程序中,用户需要在不同程序之间进行数据交换,即在本程序与系统自带拨号器间进行数据交换。因此,需要使用安卓提供的ContentProvider类,此类的作用是将不同应用程序的数据操作标准联系起来,并且将各个应用程序的数据操作标准表明给其他应用程序。这样,系统拨号器的数据就可以按照ContentProvider所指定的标准被本程序所访问和操作。
基于runOnUiThread的线程刷新设计
程序启动后另起一个线程实现通讯录的显示功能,Android的Thread+Handler方式实现线程在主界面上的UI刷新较为繁琐,本程序使用Activity提供的另外一种简单的方法runOnUiThread实现线程中的UI更新操作。
Activity.this. runOnUiThread(new Runnable() {
@Override
public void run() {
// refresh ui 的操作代码
}
});
基于service的背景音乐播放
在播放音乐的时候把MediaPlayer放在Service中,因为如果放在Activity中会使得界面特别卡。而且音乐不能放到后台里播放,一旦退出Activity,音乐就会暂停播放,Activitiy来启动这个Service。要通过UI与Service交互,可以通过Intent对象传递消息。
系统关键性代码
MainActivity.java
package com.example.luz;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.os.Bundle;
import android.provider.CallLog;
import android.provider.CallLog.Calls;
import android.app.Activity;
import android.database.Cursor;
import android.view.Menu;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.util.Log;
import android.content.Intent;
public class MainActivity extends Activity {
private Intent intent;
private Thread thread;
private Runnable runOnUiThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gettxl();
startService(new Intent(MainActivity.this, musicService.class));
thread = new Thread( new Runnable() {
@Override
public void run() {
while(true){
try {
//延迟两秒
Thread.sleep( 2000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread( new Runnable() {
@Override
public void run() {
gettxl();
}
});
}}
});
thread.start();
}
private void contactput(String string, String string2) {
// TODO Auto-generated method stub
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
protected void onDestroy() {
if(thread!=null) {
thread.interrupt();
thread=null;
}
Log.i("提示:","中断线程");
super.onDestroy();
}
public void gettxl(){
ListView callList=null;
Cursor result=null;
List<Map<String,Object>>allCalls=null;
SimpleAdapter simple =null;
callList=(ListView) super.findViewById(R.id.callList);
result=super.getContentResolver().query(
CallLog.Calls.CONTENT_URI, null,null, null, null);
startManagingCursor(result);
allCalls=new ArrayList<Map<String,Object>>();
for(result.moveToFirst();!result.isAfterLast();result
.moveToNext()){
Map<String,Object> contact=new HashMap<String,Object>();
contact.put("_id",
result.getInt(result.getColumnIndex(Calls._ID)));
String nameTemp =result.getString(result.getColumnIndex(CallLog.Calls.CACHED_NAME));
if(nameTemp==null||"".equals(nameTemp)){
nameTemp="未知";
}
contact.put("name",nameTemp);
contact.put("number",
result.getString(result.getColumnIndex(CallLog.Calls.NUMBER)));
allCalls.add(contact);
}
simple=new SimpleAdapter(this,allCalls,R.layout.calls,
new String[]{"_id","name","number"},new int[]{R.id._id,
R.id.name,R.id.number});
callList.setAdapter(simple);
}
}
musicService.java
package com.example.luz;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.IBinder;
public class musicService extends Service implements
MediaPlayer.OnCompletionListener {
MediaPlayer player;
private final IBinder binder = new AudioBinder();
@Override
public IBinder onBind(Intent intent) {
return binder;
}
public void onCreate() {
super.onCreate();
player = MediaPlayer.create(this, R.raw.mojito);
player.setOnCompletionListener(this);
player.setLooping(true);//
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if (!player.isPlaying()) {
new MusicPlayThread().start();
} else
player.isPlaying();
return START_STICKY;
}
public void onCompletion(MediaPlayer mp) {
stopSelf();
}
public void onDestroy() {
super.onDestroy();
if (player.isPlaying()) {
player.stop();
}
player.release();
}
public class AudioBinder extends Binder {
public musicService getService() {
return musicService.this;
}
}
private class MusicPlayThread extends Thread {
public void run() {
if (!player.isPlaying()) {
player.start();
}
}
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.luz"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="10" />
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITER_CONTACTS"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.luz.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".musicService"
android:exported="true"
android:enabled="true"
>
</service>
</application>
</manifest>
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bj"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<ListView
android:id="@+id/callList"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</ListView>
</LinearLayout>
calls.xml
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity"
android:orientation="vertical" >
<TableRow>
<TextView
android:id="@+id/_id"
android:textSize="20px"
android:layout_width="30px"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/name"
android:textSize="20px"
android:layout_width="180px"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/number"
android:textSize="20px"
android:layout_width="180px"
android:layout_height="wrap_content"/>
</TableRow>>
</TableLayout>
系统测试与运行截图
图0‑1 第一次打开程序图
软件第一次运行时截图如图1所示,其中显示了5个通话记录。此时,背景音乐同时响起
我们让软件进程常驻,避免界面切换后系统杀死进程导致再次打开软件时软件重新加载。
不退出程序,避免重新打开程序后再次触发onCreate事件。(可以听到背景音乐并未关闭)
图 2 拨号截图
如图2所示,再次拨号。
图 3 第二次切换到程序界面
如图3所示,切换到程序界面,程序主线程未重新加载,但主界面已经被刷新(刚才的通话记录已经出现)
课程设计总结体会
本次的课程设计是一次很宝贵的Android开发的经验,在课程设计中,我对安卓系统中的线程调用与Service有了更深的了解。本程序使用线程实现通话记录在程序界面上的刷新功能,通过service实现背景音乐播放功能。基本达到预期要求。设计中用到很多知识上课都没有讲到过,需要上网查阅相关资料,培养了我自主学习的能力。从各种文档的阅读到开始的需求分析、概念结构设计、逻辑结构设计、物理结构设计。亲身体验了一回系统的设计开发过程。很多东西书上写的很清楚,貌似看着也很简单,思路非常清晰。但真正需要自己想办法去设计一个系统的时候才发现其中的难度。经常做到后面突然就发现自己一开始的设计有问题,然后又回去修改程序,在各种反复中不断完善自己的想法。程序编写过程中,经常遇到不明原因的程序闪退、功能无法使用等错误,学习了安卓代码的调试,积累了宝贵的软件测试经验。