2017年5月17日 Kylin_Wu
标注(★☆)为考纲明确给出考点(必考)
常见手机系统(★☆)
- Android(安卓) 由Google公司和开放手机联盟领导及开发。是一种基于Linux开发的操作系统,主要使用于移动设备
- iOS(苹果) 由苹果公司开发的移动操作系统。以Darwin为基础的,属于类Unix的商业操作系统。
- Symbian(塞班) 塞班公司为手机而设计的操作系统。
- Windows Phone(WP) 微软发布的一款手机操作系统。
- BlackBerry(黑莓) 由Research In Motion为其智能手机产品BlackBerry开发的专用操作系统。
Android重要版本代号(★☆)
- 4.1&4.2&4.3 Jelly Bean(软心豆粒糖)
- 4.4 KitKat(奇巧巧克力)
- 5.0/5.1 Lollopop(棒棒糖)
- 6.0 Marshmallow(棉花糖)
- 7.0 Android Nougat(牛轧糖)
AVD、dex文件、apk文件、gradle(★☆)
- AVD Android Virtual Device(安卓模拟器)
- dex文件 Android平台上可执行文件类型
- apk文件 AndroidPackage的缩写,即Android安装包文件。
- gradle AS内置的项目构建工具名称。
Android SDK目录和文件(★☆)
SDK目录(★☆)
C:\Users\用户名\AppData\Local\Android
该目录下的文件
- add-ons 存放Google API,比如Google Maps
- build-tools 存放各版本SDK编译工具(★☆)
- docs 离线开发者文档 Android SDK API
- extras 扩展开发包,如HAXM加速
-
【platforms】 各版本SDK,如android-25、android-24等。每一个目录里面主要结构为(★☆)
- data 保存一些系统资源
- skins 安卓模拟器皮肤
- templates 工程创建的默认模板
- android.jar 该版本的主要framework文件
- 其他文件
- 【platforms-tools】 各版本SDK通用工具,如adb、sqlite等(★☆)
- samples 各版本API样例
- skins Android模拟器的皮肤
- sources 各版本SDK源码
- 【tools】 重要的工具,比如ant、ddms、logcat、emulator等(★☆)
- system-images AVD模拟器映像文件
该目录下的两个管理器(★☆)
- AVD Mananger.exe 用于管理安卓虚拟机的管理器
- SDK Manager.exe 用来下载sdk的管理android sdk更新的.。安卓开发人员,使用工具eclipse 或者android studio需要它。
Android项目的res子目录作用(★☆)
- layout 放置布局资源文件
- values 放置常量资源文件
- menu 放置菜单资源文件
- drawable 放置图片资源文件
资源文件访问格式(★☆)
- 自定义资源文件访问格式 R.资源文件类型.资源文件名称
- 系统资源文件访问格式 android.R.资源文件类型.资源文件名称
Toast、Notification特点、Log基本用法(★☆)
-
Toast特点 浮动显示信息,一定时间自动消失
Toast.makeText(MainActivity.this,"info",Toast.LENGTH_SHORT).show();
Notification特点 在通知栏中显示通知消息
-
Log基本用法
Log.x("Tag","Message");
其中"Tag"是自定义的日志标签用于过滤,"Message"是的日志信息。x可以取一下五个值(级别从大到小,级别高的可以屏蔽级别低的,但不能屏蔽级别更高的,如Log.w可以屏蔽Log.i、Log.d、Log.v,但是不能屏蔽Log.e)
* e 输出错误(error)级别的日志信息
* w 输出警告(warning)级别的日志信息
* i 输出一般提示性的消息(information )
* d 输出调试(debug)级别的日志信息
* v 输出任何消息verbose
Activity
1. Activity生成上下文菜单的方法(★☆)
上下文菜单,是指如长按列表项目后弹出的菜单
@Override
public void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo) {
// 设置上下文菜单的标题
menu.setHeaderTitle("文件操作");
// 添加菜单项
menu.add(0, 1, Menu.NONE, "发送");
menu.add(0, 2, Menu.NONE, "标记为重要");
menu.add(0, 3, Menu.NONE, "重命名");
menu.add(0, 4, Menu.NONE, "删除");
}
第一个int类型的group ID参数,代表的是组概念,你可以将几个菜单项归为一组,以便更好的以组的方式管理你的菜单按钮。
第二个int类型的item ID参数,代表的是项目编号。这个参数非常重要,一个item ID对应一个menu中的选项。在后面使用菜单的时候,就靠这个item ID来判断你使用的是哪个选项。
第三个int类型的order ID参数,代表的是菜单项的显示顺序。默认是0,表示菜单的显示顺序就是按照add的显示顺序来显示。
第四个String类型的title参数,表示选项中显示的文字。
2. 上下文菜单响应回调方法
@Override
public boolean onContextItemSelected(MenuItem item) {
switch(item.getId()){
case 1:
.....
3. Activity生成选项菜单的方法(★☆)
@Override
public boolean onCreateOptionsMenu(Menu menu){
//调用基类的方法,以便调出系统菜单(如果有的话)
super.onCreateOptionsMenu(menu);
menu.add(0,1,0,“重新开始”).setIcon(R.drawable.reflash);
....
//返回true,使其可以显示
return true;
}
与上下文菜单生成类似,其中setIcon方法设置菜单项目的图标
4. Activity一些方法功能(★☆)
- setContentVIew(); 设置显示界面
- finish(); 结束活动
5. Activity中一些回调函数特点(★☆)
onCreate Activity启动时第一个被调用的方法
onStart Activity有不可见变为可见时调用的方法
onResume Activity准备好与用户交互时调用的方法
onPause Activity准备去启动或者恢复另一个活动时调用(部分可见)
onStop Activity在完全不可见时调用
onDestroy Activity被终止前被调用的方法
onRestart Activity被重新启动(停止状态到运行状态之前)时调用
onActivityResult 接受Activity回传数据时的回调函数
UI部分
1. 调整组件大小(★☆)
- wrap_content 根据内部内容自动扩展以适应大小
- match_parent 强制扩展组件大小以填充布局单元内部尽可能多的空间
2. 设置响应监听器的方法(★☆)
-
点击事件的监听 除了Button也可以设置在ImageView等其他组件上
btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub ... .... } });
-
选择状态改变事件的监听 主要是CheckedBox、ToggleButton、RadioGroup
@Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { // TODO Auto-generated method stub if (isChecked) { .... }else { .... } });
-
项目点击事件监听 一般用于ListView
ListView listView=(ListView)findViewById(R.id.listView); listView.setAdapter(adapter); listView.setOnItemClickListener(new Adapter.OnItemClickListener(){ @Override public void onItemClick(AdapterView<?> parent,View view,int position,long id){ //position代表被点击项是列表中的第几个元素 } });
3. EditText属性(★☆)
- android:ems="10" 宽度为十个字符宽度
- android:maxEms="10" 宽度最多十个字符,超出则换行
-
android:inputType 输入内容的类型,比如
- android:inputType="none" 输入普通字符(none或者text)
- android:inputType="textPassword" 密码格式
- android:inputType="number" 数字格式
- android:inputType="numberSigned" 有符号数字格式
- android:inputType="numberDecimal" 可以带小数点的浮点格式
- android:inputType="textMultiLine" 多行输入
- android:digits="12abc" 输入只接受“12abc”这五个字符,其他不接受
- android:hint="提示语" 输入框显示提示语
- android:maxLength="5" 限制输入最多5个字符
【注意!】 在java代码中,使用
((EditText)findViewById(R.id.text)).getText().toString();
来获得字符,仅仅是.getText()是无法获取String的。
4. Spinner(下拉控件)属性(★☆)
Spinner提供了从一个数据集合中快速选择一项值的办法。点击Spinner会弹出一个包含所有可选值的dropdown菜单,从该菜单中可以为Spinner选择一个新值。
-
spinnerMode 选择下拉列表的模式
- dropdown 如上图所示,点击后下拉
- dialog 点击后,列表会以对话框形式弹出。
-
entris 下拉列表的数据源设置
//在values/arrays.xml中 ?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="languages"> <item>c语言</item> <item>java </item> </string-array> </resources>
.
//在布局文件中
<Spinner
android:entries="@array/languages"
....
5. 常用布局特点(★☆)
RelativeLayout(相对布局) 相对布局是一种非常灵活的布局方式;通过指定界面元素与其他元素的相对位置关系,来确定界面中所有元素的布局位置;优点:能够最大程度保证在各种屏幕尺寸的手机上正确显示界面布局。
-
LinearLayout(线性布局) 线性布局是常用的一种布局方式;通过设置
android:orientation
来确定是水平布局(默认)
android:orientation="horizontal"
还是垂直布局
android:orientation="vertical" 水平布局
垂直布局时,每行仅包含一个界面元素。水平布局时,所有界面元素都在一行
Android四大组件(★☆)
- Activity(活动) 是用户和应用程序交互的窗口
- BroadCast Recevicer(广播接收器) 接受一种或者多种Intent作触发事件,接受相关消息,做一些简单处理,转换成一条Notification。
- Service(服务) 是一种程序,它可以运行很长的时间,相当于后台的一个服务。
- Content provider(内容提供者) 可通过它来共享自己的数据给外部调用,给第三方应用提供数据访问的接口。
Intent(★☆)
作用(★☆)
Intent 是一种组件之间消息传递机制,它是一个动作的完整描述:包含了动作产生组件、接收组件和传递的数据信息。
主要用途
启动Activity、Service,在Android系统上发布Broadcast消息。
使用方法
startActivity(intent);
startActivityForResult(intent,requestCode);
放入数据
intent中其实维护了一个Bundle对象,所以可以直接把一个装填了数据的Bundle放进去
//注意Extra后面有s
void putExtras(Bundle bundle);
//取出Bundle
Bundle getExtras();
存数据时使用putExtra。Xxx表示基础数据类型如Int、Char等等,xxx表示int、char等等。值得注意的是,当填入一个对象时,该对象要是序列化的,比如实现Serializable接口。
//注意这里Extra后面没有s
void putExtra(String key,xxx value);
//取出数据,取出序列化对象要用getSerializableExtra(String key);
xxx getXxxExtra(String key);
xxx[] getXxxArrayExtra(String key);
显示调用intent(★☆)
隐式调用intent(★☆)
无需指明具体启动哪一个Activity,而由Android系统根据Intent的动作和数据来决定启动哪一个Activity。
例如:希望启动一个浏览器,却不知道具体应该启动哪一个Activity,此时则可以使用Intent的隐式启动,由Android系统决定启动哪一个Activity来接收这个Intent。
隐式启动的可以是Android系统内置的Activity,也可是程序本身的Activity,还可是第三方应用程序的Activity.
注意到上图中最重要是添加下面代码:
<category android:name="android.intent.category.DEFAULT"/>
如果不添加导致程序崩溃,这是因为在调用startActivity方法时,会自动把这个category添加到intent中,所以,如果没有添加这个语句,则会出现category无法匹配的情况,自然会导致崩溃。
- 一个活动的清单中可以包括多个intent-filter
- 一个intent-filter中可以有多个action、category。
- 一个intent对象中只能有一个action,但是可以携带多个category。
常见动作(★☆)
在java类中,使用如上方法定义intent,在构造函数中传递动作,也可以调用相应的设置方法设置动作
Intent intent=new Intent(Intent.ACTION_VIEW,Uri.parse(".."));
//或
Intent intent=new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(".."));
//或
Intent intent=new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(".."));
比如,启动返回桌面
Intent intent=new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
startActivity(intent);
因为上述调用的是系统应用的活动,所以不必配置相应的<action>和<category>标签。启动部分活动需要权限设置,参考后面“权限”部分。
启动主活动的过滤器
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
Intent传递数据机制
返回数据给上一个活动
使用startActivityForResult来启动其他活动,因为一个主活动可能打开不同的其他的活动,为了区分哪个活动传回的intent需要设置一个请求码requestCode,只要不同活动不同就可以。
其他活动接受了主活动传来的intent,进行相应的操作后要传回数据给主活动。创建一个intent,塞入数据,然后使用setResult()来传回给主活动。注意到创建的intent并没有明确指明是传回给主活动,事实上这不需要指明,setResult会为我们完成这个工作。
在主活动中重写onActicityResult,每次由主活动调用的其他活动传回intent时便会调用这个方法。第一个参数请求码用于区别是哪一个活动传回的;第二个参数结果码用于标识结果状态,一般是RESULT_OK或者RESULT_CANCEL,第三个参数就是传回的intent,它里面携带有传回的数据。
保存临时数据——Bundle对象
主活动启动次活动,次活动可以使用intent回传给主活动数据。但是次活动处于返回栈栈顶时,主活动面临着会被销毁的风险。主活动不被销毁最好,若是销毁,必须将其内的临时数据进行保存,以便在主活动重新创建时能够复原。
onSaveInstanceState方法保证了在主活动被销毁之前一定能够被调用,他可以作为保存临时数据的方法,保存数据的载体是Bundle。使用Bundle对象的putXxx方法来存储数据。
在主活动中重写下列方法:
@Override
protected void onSaveInstanceState(Bundle bundle){
super.onSaveInstanceState(state);
String username="Tom";
int age=16;
bundle.putInt("age",age);//键值对
bundle.putString("username",username);//键值对
}
当主活动被销毁时,临时数据通过这种方法被保存了;下一次为了复原,主活动会被创建,此时会调用onCreate,我们发现这个方法的参数列表中恰好有Bundle对象。第一次被创建时,因为不可能有临时数据,所以这个Bundle对象是null,若下一次被创建,则一般不可能为null(除非没有onSaveInstanceState方法被重写),此时就可以取出数据了:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState!=null){ //若不为空
String username=savedInstanceState.getString("username");
int age=savedInstanceState.getInt("age");
}
......
Bundle类常用方法(★☆)
-
putBundle 将一个Bundle对象存入
void putBundle (String key, Bundle value);
-
putAll 把另一个Bundle对象中的数据全部添加进来
void putAll(Bundle bundle);
-
putXxx或putXxxArray Xxx代表基本类型,如Byte、Int、Short、String、Char等等,而XxxArray代表可以接受Byte[]、Int[]、String[]等基本类型的数组类型
void putXxx(String key,Xxx obj); void putXxxArray(String key,Xxx[] objs);
-
get或getXxx 通过键直接取值,或者指定范围如getInt、getBundle等再用键取值。注意前者要类型转换。
Object get(String key); xxx getXxx(String key); xxx[] getXxxArray(String key);
-
remove 移除某个属性
void remove(String key);
-
clear 移除所有属性
void clear();
Android广播(★☆)
分类
- 系统广播 如系统启动完成了、拨打电话了、收到短信了、手机没电了等系统发送的消息。
- 自定义广播 将自定义的消息广播给应用程序
三种广播发送方法
普通广播 完全异步。接收器的执行顺序不确定。
有序广播 sendOrderedBroadcast()发送。所有的receiver依次执行。
-
粘性消息 sendStickyBroadcast()发送。在发送后就一直存在于系统的消息容器里,等待对应的receiver去处理,如果暂时没有receiver处理,则一直在消息容器里面处于等待状态。这个广播需要权限:
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
实质(★☆)
用sendBroadcast方法讲一个Intent对象发出去。
触发BroadcastReceiver的方法(★☆)
.... extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent){
...
}
}
自定义广播发送端+接收端主要框架(★☆ 简答题)
我们假设,收发器的匹配动作是action="com.example.mybroadcast"(自定义)
-
定义广播发送器
Intent intent=new Intent("com.example.mybroadcast"); Bundle bundle=new Bundle(); bundle.putString("msg","测试信息"); intent.putExtras(bundle); //由该方法发出(普通异步广播) sendBroadcast(intent); //或发送有序广播(优先级见下图后) sendOrderedBroadcast(intent);
-
定义广播接收器 myBroadcastReceiver 。继承BroadcastReceiver
public class MyBroadcastReceiver extends BroadcastReceiver { public MyBroadcastReceiver() {} @Override public void onReceive(Context context, Intent intent) { //代码逻辑,接收到广播后在此触发 //不能做一些比较耗时的操作,一般限于10s内 if(intent.getAction.equals("com.example.mybroadcast")){ ... //一个广播接收器可以接受多个广播类型,在此可以判断 } } }
-
注册这个接收器
-
静态注册
在AndroidManifest.xml文件中添加注册信息
<receiver android:name=".MyBroadcastReceiver" //是否这个broadcast receiver能由系统实例化。默认为true。 android:enabled="true" //是否broadcast receiver能够接收来自外部应用的消息。能接收则为true,否则为false。 android:exported="true" > <intent-filter> //广播与action和category相匹配时触发广播 <action android:name="com.example.mybroadcast" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver>
-
动态注册
在xxx.java文件中进行注册。通过这种方式注册的广播称之为非常驻型广播,它会跟随Activity的生命周期。通常,我们会在Activity的onCreate方法中对其进行初始化,在onResume方法中对其进行注册,在onPause方法中对其取消注册。
onCreate(){ .... //在onCreate方法中初始化广播接收器 MyBroadcastReceiver myBroadCastReceiver; myBroadCastReceiver = new MyBroadCastReceiver(); //然后创建一个过滤器,如果广播动作能匹配这个,则该接收器被触发 IntentFilter filter = new IntentFilter(); filter.addAction("com.example.mybroadcast"); //设置过滤器,注册广播 registerReceiver(myBroadCastReceiver, filter); ...
我们在其他方法中可以注销接收器
@Override protected void onPause() { super.onPause(); // 注销广播 unregisterReceiver(myBroadCastReceiver); }
-
有序广播(同步)的接收器优先级
高优先级的触发器组件可以通过
BroadcastReceiver.abortBroadcast
方法来终止这个广播事件的传播,从而,使低优先级的触发器组件没有机会在处理该事件了。
可以通过在AndroidManifest.xml文件中的标签中增加
android:priority
属性设置优先级,也可以在IntentFilter类实例中调用
setPriority
方法设置优先级
常见系统广播(★☆)
可能需要权限设置,权限设置见后。
Android服务(★☆)
特点(★☆)
长生命周期,,没有可视化界面,后台运行的程序。
分类
-
本地服务:用于应用程序内部
- 服务通过调用startService(intent)启动,stopService(intent)结束。
- 在服务内部可以调用stopSelf() 或 stopSelfResult()来自己停止。
- 无论调用了多少次startService(),都只需调用一次stopService()来停止。
-
远程服务:用于应用程序之间
- 过调用bindService()方法建立连接并启动服务,调用unbindService()关闭连接。
- 多个客户端可以绑定至同一个服务。如果服务此时还没有加载, bindService()会先加载它。
- 远程服务可被其他应用复用。
上图说明
- 如果使用startService,则第一次会执行onCreate方法,然后执行onStartCommand(android2.0以下的版本中使用onStart),销毁时,调用onDestroy方法。
- 如果使用bindService,则第一次会执行onCreate方法,然后执行onBind方法,不会执行onStart方法,完成后调用onUnbind方法,然后onDestroy销毁。
服务如何启动和结束(★☆)
startService(intent);
stopService(intent);
stopSelf(); //服务内部终止
stopSelfResult; //服务内部终止
/*---------------------------------------*/
bindService() //建立连接开启服务
unbindService() //关闭连接
【注意】
第一次startService()后会调用服务的onCreate()、onStart()方法,如果服务已经启动,通过startService()再次开启服务时,只会执行onStart()方法。
服务的框架
-
创建服务
自定义的服务类继承Service类,并要在配置文件中说明public class MyService extends Service { public MyService() {} @Override //由startService()触发,注意这里参数列表为空,没有Bundle public void onCreate(){ super.onCreate .... } //通常将service的主要代码写在onStart()/onStartCommand()中 /*------------------以下两个版本可以共存------------------------*/ //在android2.0以下的版本中使用 @Override public void onStart(Intent intent, int startId) { //由启动服务的活动传来的intent保存在参数中,可获得数据 super.onStart(intent, startId); ... } //在android2.0以上的版本中使用 @Override public int onStartCommand(Intent intent, int flags ,int startId) { //由启动服务的活动传来的intent保存在参数中,可获得数据 super.onStartCommand(intent, int flags ,startId); ... return 返回值见下 } /*-----------------------------------------------------------*/ @Override //由stopService()或stopSelf()触发 public void onDestroy() { super.onDestroy } @Override //其使用见“活动与服务的通信” public IBinder onBind(Intent intent) { throw new UnsupportedOperationException("Not yet implemented"); } }
在清单文件中声明
<service android:name=".MusicService"
//是否启用这个服务
android:enabled="true"
//是否允许除了当前程序之外的其它的程序访问这个服务
android:exported="true" >
</service>
-
启动服务
..... Intent intent = new Intent( MainActivity.this, MyService.class ); startService(intent); ..... stopService(intent);
3.onStartCommand的返回值
* START_STICKY
>如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。
* START_NOT_STICKY
>“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务
* START_REDELIVER_INTENT
>重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
* START_STICKY_COMPATIBILITY
>START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
活动与服务的通信——使用onBind方法(可选)
原理与流程如上图所示,关键一点是纽带。即,继承了Binder类的对象,会被服务中的onBind方法(该方法参数列表接受活动传来的数据携带体intent,经过处理后得到回传对象x)返回这个纽带x;x进入服务连接对象(继承了ServiceConnection并重载两个关键方法)的onServiceConnected方法的参数列表,从而被活动类所持有。从而,二者进行了关联,相关交互可以在这个纽带中进行。当然关键是在onServiceConnected方法中,对binder进行类型转换
Android数据存储(★☆)
SharedPreferences
特点(★☆)
- 轻量级数据存储方式,能够实现不同应用程序间数据共享。
- 基于XML文件来存储key-value键值对数据,通常用来存储一些简单的配置信息。
- 只能存储 int、long、float、boolean和String这5种简单数据类型。
存放位置(★☆)
/data/data/包名/shared_prefs/
基本框架
-
首先有一个数据的映射表(xml),因为它只能存基础数据
<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map> <string name="password">123456</string> <int name="age" value="42" /> </map>
-
可以写入数据
//获得SharedPreferences对象(如果不存在则新建) SharedPreferences sp = getSharedPreferences(XML文件名, 访问模式); //获得可编辑对象 SharedPreferences.Editor editor = sp.edit(); //put方法写数据(key-value) editor.putString("Name", "hello"); //一定要提交才能保存 editor.commit();
-
可以读取数据
//获得SharedPreferences对象(如果不存在则新建) SharedPreferences sp = getSharedPreferences(XML文件名, 访问模式); //get方法读数据,在无法获取值的时候使用的缺省值 String name = sp.getString("Name", "默认值");
SQLite
Android 在运行时集成了 SQLite数据库,所以每个 Android 应用程序都可以使用 SQLite 数据库。
基本特点(★☆)
开源的、轻量级的、嵌入式的、关系型数据库。
存放位置(★☆)
/data/data/包名/databases/
框架
-
SQLiteOpenHelper类 Android提供的用来管理数据库的创建和版本更新。继承此类,实现它的一些方法来对数据库进行管理。
public class DBHelper extends SQLiteOpenHelper { @Override public void onCreate(SQLiteDatabase db) { //数据库第一次被创建时将调用onCreate //通常将创建表的操作放在这里 } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { //当数据库版本发生变化时会调用onUpgrade //这里可写更新数据表的操作(谨慎使用),也可空着不写 }
-
实例
public class DBHelper extends SQLiteOpenHelper { //context:上下文对象 name:数据库名字 factory:游标工厂 version:数据库版本号 //版本号为整数值,以后设置需递增,不要设置为0,0表示每次都创建数据库 DBHelper (Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); //必须的 } @Override public void onCreate(SQLiteDatabase db) { //创建一个数据库 db.execSQL( "CREATE TABLE IF NOT EXISTS person (" +"id INTEGER PRIMARY KEY AUTOINCREMENT," +"name VARCHAR(20)," +"age SMALLINT)"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS person"); //删除数据表,谨慎使用 onCreate(db); //重新建表 } }
-
使用方法
/*--------------BEGIN---------------*/ int DB_VERSION=1; //使用辅助类,游标工厂一般设置为null //一旦版本号改变,则会触发onUpgrade方法!!!! DBHelper helper = new DBHelper(getApplicationContext(), "test.db", null,DB_VERSION); //调用getWritableDatabase()或getReadableDatabase()才会真正创建或打开 SQLiteDatabase db=helper.getWritableDatabase(); .....//操作,见下方execSQL和rawQuery db.close(); //操作完成后关闭数据库连接 /*--------------END-----------------*/ //或直接打开,不要辅助类~~~~~~~~~~~~~~~~~ /*-------------BEGIN------------------*/ //数据库名、访问权限(只能是被创建它的包访问)、游标工厂(一般为null) SQLiteDatabase db = openOrCreateDatabase("test.db",Context.MODE_PRIVATE, null); .... db.close(); /*-------------END--------------------*/
SQLiteOpenHelper类的getReadableDatabase()、getWritableDatabase()运行情况(★☆)
- 如果数据库不存在, 就会调用onCreat(), 不会调用onUpgrade();
- 如果数据库存在, 但是版本不一样, 就调用onUpgrade(), 不会调用onCreate()
- 如果数据库存在, 版本一样, 不会调用onCreate()和onUpgrade(),此时如果数据库没有打开, 就调用onOpen方法打开,如果打开了就不调onOpen。
SQLiteDatabase类的execSQL()和rawQuery方法区别(★☆)
-
db.execSQL(...)
public void execSQL(String sql, Object[] args);
执行insert、delete、update和create table等有更改行为的SQL语句。
-
db.rawQuery(…)
//返回Cursor(类似于JDBC中的ResultSet) public Cursor rawQuery(String sql, String[] args)
执行select语句
举个例子
//因为id是设置自增的,所以不用设置id,只要NULL就可以
//另外,因为要用后面数组填充,所以要用占位符?
db.execSQL("INSERT INTO person VALUES (NULL, ?, ?)",new Object[ ] { "Ok", Integer(15) } );
下面是查询例子
//获得结果游标(注意占位符?使用)
Cursor cursor = db.rawQuery( "SELECT * FROM person where age>?", new String[]{"10"} );
while (cursor.moveToNext()) { //遍历结果集
//cursor.getColumnIndex("id") 获得id所在列的标号
//cursor.getInt 通过标号获得值并转为int
int id = cursor.getInt(cursor.getColumnIndex("id") );
String name = cursor.getString(cursor.getColumnIndex("name") );
}
//关闭cursor!!!!!!!!!!!!!!!
cursor.close();
db.close();
改进
注意到,db.execSQL(...)方法返回值是void,也就是不知道更新操作是否成功,SQLiteDatebase本身提供了insert()、delete()、update()、 query()四个方法对数据进行操作。
-
插入数据(insert:返回值为插入行的row ID)
//ContentValues以键值对的形式存放基本类型数据 ContentValues cv = new ContentValues(); cv.put("name", "Tom"); cv.put("age", 26); DBHelper helper = new DBHelper(getApplicationContext(), "test.db", null,1); SQLiteDatabase db=helper.getWritableDatabase(); //将ContentValues对象添加到数据表中 //返回值为插入行的row ID int rowId=db.insert("person", null, cv); //第二个参数一般设置为null db.close(); if(rowId>0)//说明查询成功
-
删除数据(delete:返回值为删除行的row ID)
//返回值为删除行的row ID int count=db.delete("person", "age < ? and name=?",new String[ ]{ "40","Tom" });
-
更新数据(update:返回值为更新行的row ID)
ContentValues cv = new ContentValues(); cv.put("name", "QQ"); //键值对 cv.put("age", 35); //键值对 //用ContentValues中的数据更新记录条件值 int rowId=db.update("person", cv, "id = ?", new String[]{"1"}); db.close(); if(rowId>0) //说明插入成功
-
查询数据(query:返回Cursor)
Cursor cursor = db.query( "person",new String[]{ "id", "name" }, //SELECT id,name FROM person "id > ?",new String[]{ "10" }, //WHERE id>10 null,null, //String groupBy=null,String having=null "id desc", //order by id desc "1,3"); //LIMIT 1,3
请注意上面这个LIMIT。SQL语法是
LIMIT offset,lines;
意思是,从偏移量offset开始查询,最多查lines行,即限定查询范围
LIMIT 1,3; //从第二行开始查,最多三行。即第二行到第四行范围内查询
- 初始记录行的偏移量是 0(而不是 1)
- 为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1
- LIMIT n 等价于 LIMIT 0,n
ContentProvider(★☆)
作用(★☆)
ContentProvider提供了应用程序之间共享数据的方法。也就是说一个应用程序可以通过ContentProvider将自己的数据暴露出去
ContentResolver
应用程序使用 ContentResolver 对象,利用URI,才能访问ContentProvider提供的数据集。
一个ContentProvider可以提供多个数据集,可为多个ContentResolver服务
ContentProvider数据集
ContentProvider可以形象地看作是"数据库", ContentProvider数据集类似于数据库的"表"。
ContentProvider数据集的每条记录都包含一个long型的字段_ID,用来唯一标识每条记录
一个ContentProvider表并不要求必须有一个 _ID 列,不过如果想要把查询的数据放在ListView中显示(使用SimpleCursorAdapter填充),则必须有 _ID 列
URI
统一资源标识符,用来标识资源的逻辑位置,不提供资源的具体位置。
(可用Uri.parse(串)转换为Uri)
content://<authority>/<data_path>/<id>
- content:固定前缀
- <authority>:ContentProvider名称(唯一标识)(近似理解为数据库名)
- <data_path>:数据集名称(近似理解为表)
- <id>:数据编号,用来匹配数据集中_ID字段的值(用来唯一确定数据集中的一条记录)如果请求的数据并不只限于一条数据,则<id>可以省略
URI示例(★☆)
content://contacts/people/ //表示全部联系人信息的URI
content://contacts/people/1 //表示ID=1的联系人信息的URI
了解ContentResolver的query方法(★☆)
-
创建一个内容提供器
ContentResolver resolver = getContentResolver();
-
增删改查
- query(Uri uri, String[] projection, String selection, String[] selectionAr gs,String sortOrder):通过Uri进行查询,返回一个Cursor。
- insert(Uri url, ContentValues values):将一组数据插入到Uri 指定的地方,返回最新添加那个记录的Uri。
- update(Uri uri, ContentValues values, String where, String[] selectio nArgs):更新Uri指定位置的数据,返回更新的行数。
- delete(Uri url, String where, String[] selectionArgs):删除指定Uri并且符合一定条件的数据,返回删除的行数。
例
Cursor cursor = resolver.query( "content://com.android.contacts/contacts", //FROM "content://com.android.contacts/contacts" new String[]{"_id","display_name","has_phone_number"}, //SELECT _id,display_name,has_phone_number //(null表示所有字段) "display_name like ?", new String[]{"Tom"}, //WHERE display_name like "Tom" //(null表示无条件) "display_name asc" //ORDER BY display_name asc //(null表示按默认排序) );
系统ContentProvider主要功能(★☆)
- Browser:存储如浏览器的信息。
- CallLog:存储通话记录等信息。
- Contacts:存储联系人等信息。(URI:ContactsContract.Contacts.CONTENT_URI)
- MediaStore:存储媒体文件的信息。(URI:MediaStore.Audio.Media.EXTERNAL_CONTENT_URI)
- Settings:存储设备的设置和首选项信息。
ContentResolver+SimpleCursorAdapter
SimpleCursorAdapter adapter = new SimpleCursorAdapter(
this, R.layout.list_row, cursor, from, to);
其中,this是上下文,第二个参数是列表的行视图布局文件,第三个cursor是查询结果,第四个from是查询字段数组,第五个to是与from对应的视图id,将会把对应的查询内容结果绑定到视图
//一一对应绑定
String[] from = { "_id","name","age"}; //查询结果字段
int[] to={R,id,ID,R.id.NAME,R.id.AGE}; //视图控件id
然后可以把适配器绑定至ListView
ListView li=(ListView)findViewById(R.id.listView1);
li.setAdapter(adapter);
MediaPlayer媒体播放器(★☆)
- 当一个MediaPlayer对象被创建或者调用reset()方法之后,它处于空闲状态,调用release()方法后处于结束状态
- 调用了reset()方法后,再调用其它方法可能会触发OnErrorListener.onError()事件
- 不再被使用时,最好调用release()方法对其进行释放,使其处于结束状态,此时它不能被使用
- 由于种种原因导致错误。此时可通过注册setOnErrorListener方法实现监控。如果发生了错误,Mediaplayer对象将处于多雾状态,可以使用reset()方法来回复错误。
- 任何Mediaplayer对象都必须先处于准备状态,然后才开始播放
- 要开始播放Mediaplayer对象都必须成功调用start()方法,可通过isPlaying()方法来检测是否正在播放
- 当Mediaplayer对象在播放时,可以进行暂停和停止操作,pause()方法暂停播放,stop()方法停止播放。处于暂停暂停时可通过start()方法恢复播放,但是处于停止状态时则必须先调用prepare()方法使其处于准备状态,再调用start()方法。
获取实例
可以使用直接new的方式:
MediaPlayer mp = new MediaPlayer();
也可以使用create的方式,如:
MediaPlayer mp = MediaPlayer.create(this, R.raw.music);
//这时就不用调用setDataSource了
设置要播放的文件
-
用户在应用中事先自带的resource资源
MediaPlayer.create(this, R.raw.music);
-
存储在SD卡或其他文件路径下的媒体文件
mp.setDataSource("/sdcard/test.mp3");
-
网络上的媒体文件
mp.setDataSource("http://www.citynorth.cn/music/confucius.mp3");
主要方法(★☆)
- start() 启动文件播放
- pause() 暂停播放
- stop() 停止播放
- release() 释放播放器占用的资源
- setDataSource() 设置数据来源
- seekTo() 定位方法,可以让播放器从指定的位置开始播放(异步方法,该方法返回时并不意味着定位完成。定位真正完成后会触发OnSeekComplete.onSeekComplete(),如果需要是可以调用setOnSeekCompleteListener(OnSeekCompleteListener)设置监听器来处理的。)
- isPlaying() 判断是否正在播放
- prepare() 设置播放器进入prepare状态(如果MediaPlayer实例是由create方法创建的,那么第一次启动播放前不需要再调用prepare()了,因为create方法里已经调用过了。)
- reset() 播放器从Error状态中恢复过来,重新会到Idle状态。
音乐播放器的一般步骤
- mp.reset(); //重置
- mp.setDataSource(song_path);
- mp.prepare(); //准备
- mp.start(); //播放
- mp.pause(); //暂停
- mp.start(); //继续播放
- mp.stop(); //停止
- mp.release(); //释放音乐播放器
Handler+Message机制代码框架(★☆简答题)
多线程
-
继承Thread
class MyThread extends Thread { public void run() { .... } } new MyThread().start();
-
实现Runnable接口
class MyThread implements Runnable { @Override public void run() { ....... } } MyThread mythread=new MyThread(); new Thread(mythread).start(); //把接口放到Thread中跑
-
简化写法
new Thread( new Runnable() { @Override public void run() { .... } }).start();
UI的更新必须在主线程(UI线程)中进行,如果在子线程中更新 UI 会导致异常。
Handler消息处理机制
不直接在子线程中进行 UI 操作,子线程通过 Handler 将Message 传送出去。主线程中的Handler接收这个Message,然后进行 UI 等相关操作。
注意!,导入包是
import android.os.Handler;
而不是(默认)包
import java.util.logging.Handler;
-
首先,在主线程中创建Handler(作为Activity的一个成员变量)
private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 某个消息标识) { ... } .... } };
-
子线程中发送消息
new Thread( new Runnable() { @Override public void run() { // 子线程完成某耗时操作 //推荐Message m = Message.obtain(); Message m = new Message(); m.what = int值; //消息标识码 m.arg1 = int值 //要传递的值如进度 // 发送消息到指定Handler mHandler.sendMessage(m); } }
子线程也可以使用主线程生成的Hanlder的
post(Runnable)方法,将Runnable钩到主线程中运行。(★☆)//主线程中 Button bt=(Button)findViewById(R.id.btn_test); bt.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //子线程 new Thread(new Runnable() { @Override public void run() { while (x<100) { Log.d("test", "x=" + (x++) ); mHandler.post(new Runnable() { @Override public void run() { tv.setText("x="+x); } }); } try { Thread.sleep(500); } catch (InterruptedException e){ e.printStackTrace(); } } }).start(); } }
HttpURLConnection网络编程(★☆填空)
1.远程登录
客户端工作
-
AndroidManifest.xml中添加访问权限
<uses-permission android:name="android.permission.INTERNET"/>
由于Android模拟器已将127.0.0.1指定给自己,因此要访问本机的Web服务器,用它设定的另一个地址:http://10.0.2.2
-
在安卓客户端的活动代码
public class HttpClientActivity extends Activity { EditText username; EditText psd; TextView result; Handler mHandler = new Handler(); String httpUrl = "http://10.0.2.2:8080/MyServer/login.jsp"; //网站url URL url; //URL对象 HttpURLConnection conn; //连接对象 //自定义登录函数 public String login(String username, String psd) { String resultData = ""; //服务器返回结果 try { url = new URL(httpUrl); //创建URL对象(必须try/catch) } catch (MalformedURLException e){ e.printStackTrace(); } if (url != null) { try { //关键代码 conn = (HttpURLConnection)url.openConnection(); //创建网站连接对象 conn.setRequestMethod("POST"); //Post方式访问 conn.setRequestProperty("Charset", "UTF-8"); //设置uft-8字符集 String datastr = "username=" + URLEncoder.encode(username, "utf-8") + "&psd=" + URLEncoder.encode(psd, "utf-8"); byte[] data = datastr.getBytes(); //将字符串转化为一个字节数组 //向服务器发送数据(Java输出流) DataOutputStream outputStream = new DataOutputStream(conn.getOutputStream()); outputStream.write(data); outputStream.close(); //关闭流 int code = conn.getResponseCode(); //获得服务器反馈信息 if( code == HttpURLConnection.HTTP_OK ) { //HTTP_OK值为200 //接收服务器返回的信息(Java输入流) BufferedReader reader = new BufferedReader( new InputStreamReader(conn.getInputStream())); String line = ""; //使用循环来获得数据,一次一行 while ( ( line = reader.readLine() ) != null) { resultData += line + "\n"; } // end while reader.close(); //关闭流 } } catch (IOException e) { e.printStackTrace(); } } return resultData; } //在onCreate方法中 ......//获得登录按钮btn_login bt_login.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //启动线程 new Thread(new Runnable() { @Override public void run() { String u=username.getText().toString(); String p=psd.getText().toString(); final String r = login(u,p); //登录 mHandler.post(new Runnable() { @Override public void run() { result.setText("结果:"+r); //显示结果 } //end run }); } //end run }).start(); } }); .....
2.web服务手机号归属地查询
public class TelNumberFind extends Activity {
//成员变量
EditText mobileCode;
TextView result;
URL url;
HttpURLConnection conn;
Handler mHandler = new Handler();
//web服务url串
String httpUrl =
"http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx/getMobileCodeInfo";
//方法
public String getRemoteInfo(String mobileCode,String userId) {
String resultData = ""; //服务器返回结果
try {
//创建URL对象(必须try/catch)
url = new URL(httpUrl);
} catch (MalformedURLException e) {
e.printStackTrace();
}
if (url != null) {
try {
//创建网站连接对象
conn = (HttpURLConnection)url.openConnection();
//必须Post方式访问
conn.setRequestMethod("POST");
//设置uft-8字符集
conn.setRequestProperty("Charset", "UTF-8");
String datastr =
"mobileCode=" +
URLEncoder.encode(mobileCode, "utf-8") +
"&userId=" +
URLEncoder.encode(userId, "utf-8");
//将字符串转化为一个字节数组
byte[] data = datastr.getBytes();
//向服务器发送数据
DataOutputStream outputStream =
new DataOutputStream(conn.getOutputStream());
outputStream.write(data);
outputStream.close(); //关闭流
//获得服务器反馈信息
int code = conn.getResponseCode();
if(code ==HttpURLConnection.HTTP_OK) {
//接收服务器返回的信息
BufferedReader reader =
new BufferedReader(
new InputStreamReader(conn.getInputStream()
)
);
String line = "";
//使用循环来获得数据,一次一行
while ((line = reader.readLine()) != null) {
resultData += line + "\n";
}
reader.close(); //关闭
}
}catch (IOException e){
e.printStackTrace();
}
}
return filterHtml(resultData);
}
//使用正则表达式过滤HTML标记
private String filterHtml(String source) {
if(null == source){
return "";
}
return source.replaceAll("</?[^>]+>","").trim();
}
//在onCreate中
.....//获得点击按钮btn
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final String m = mobileCode.getText().toString().trim();
// 简单判断用户输入的手机号码(段)是否合法
if ("".equals(m) || m.length() < 7) {
mobileCode.setError("您输入的手机号不足7位!");
mobileCode.requestFocus();
result.setText("查询结果:无");
return;
}
//※启动线程※
new Thread(new Runnable() {
@Override
public void run() {
//个人用户 userId=""
final String r = getRemoteInfo(m,"");
mHandler.post(new Runnable() {
@Override
public void run() {
//显示结果
result.setText("结果:"+r);
} //end run
});
}
}).start();
}
});
......
权限
1.常用权限(★☆)
<uses-permission android:name="…………"/>
其中引号中可以填写的(常用)权限有
- android.permission.READ_CONTACTS 读取联系人
- android.permission.READ_EXTERNAL_STORAGE 读取存储权限
- android.permission.INTERNET 访问网络权限
- android.permission.BROADCAST_SMS 收到短信时广播权限
- android.permission.SEND_SMS 发送短信权限
- android.permission.CALL_PHONE 拨打电话
- android.permission.RECORD_AUDIO 录音权限
2.运行时权限
所谓运行时权限是指应用在运行时动态申请权限,由用户决定是否授权,当拒绝授权时,仍然可以使用该应用的其他功能。
安卓6.0中,权限分为三种:普通权限、危险权限、特殊权限。第三种权限应用的非常少,可以忽略。
A.普通权限
普通权限是指不会威胁到用户安全和隐私的权限,这一类权限系统会自动授权。而我们只需要在配置清单文件中声明
<uses-permission android:name="…………"/>
即可。
B.危险权限
危险权限是指威胁到用户安全和隐私的权限,如拨打电话、查询地理位置等。除了要在XML文件中声明
<uses-permission android:name="…………"/>
我们还需要在java代码文件中进行一些操作(以拨打电话为例)
......//在onCreate中点击btn按钮进行拨打电话
......
btn.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
if(
ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.CALL_PHONE)
!=PackageManager.PERMISSION_GRANTED
){
//说明没有被用户授权,尝试申请权限
//该方法第一个是上下文,第二个是权限组,第三个是请求码,只要唯一即可
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.CALL_PHONE},1);
}else{
//说明已经获得授权,可以进行相关操作
}
}
});
.....
当ActivityCompat.requestPermissions方法调用完后,系统自动弹出申请权限对话框,无论同意与否,最终都要回调onRequestPermissionResult方法。
protected void onCreate(Bundle savaInstanceState){.....}
......
@Override
public void onRequestPermissionResult(
int requestCode,String[] permissions,int[] grantResults){
//匹配某个权限申请的请求码,如上面的1表示是按钮btn申请拨打电话
switch(requestCode){
//用户不曾授权,申请权限。
//用户对第i个权限的授权结果放在grantResults[i]中
case 1:
if(grantResults.length>0
&& grantResults[0]==PackageManager.PERMISSION_GRANTED){
//用户同意了拨打电话权限
//调用相关方法,如拨打电话
}else{
//用户拒绝权限,进行提示性操作
}
..........
}
}
编程题(★☆)
1.简单音乐播放器
-
AndroidManifest.xml中添加SD卡访问权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"> </uses-permission>
-
注意Android6.0/7.0版本权限问题(一般都放在onCreate中)
//onCreate ...... if (ActivityCompat.checkSelfPermission( MusicActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ) { ActivityCompat.requestPermissions( MusicActivity.this, new String[] { Manifest.permission.READ_EXTERNAL_STORAGE }, 0x1); return; } ......
-
判断是否是AndroidN以及更高的版本(N=24)
....//放于上面权限判断之后 if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ) { /*Android7.0新的安全策略不推荐使用file://格式来直接访问文件,这里可简单关闭该安全策略*/ StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.setVmPolicy(builder.build()); }
-
自定义的文件过滤器类
class MyFilter implements FilenameFilter { private String type; public MyFilter(String type){ this.type=type; } @Override //实现FilenameFilter接口accept()方法 public boolean accept(File dir,String name) { //dir当前目录, name文件名 return name.endsWith(type);//返回true的文件则合格 } }
-
获取mp3音乐文件路径代码
......//放于上面版本判断之后 ArrayList<String> list = new ArrayList<String>(); //音乐列表 File sdpath=Environment.getExternalStorageDirectory(); //获得手机SD卡路径 File path=new File(sdpath+"//mp3//"); //获得SD卡的mp3文件夹 //返回以.mp3结尾的文件 (自定义文件过滤) //File[ ] java.io.File.listFiles(FilenameFilter filter): //filter是一个文件过滤类,该函数将保留符合filter规定的文件 File[ ] songFiles = path.listFiles( new MyFilter(".mp3") ); for (File file :songFiles){ list.add( file.getAbsolutePath() ); //获取文件的绝对路径 } .....
-
ListView适配器数据填充
......//放于上面文件过滤完成之后 ArrayAdapter<String> adapter = new ArrayAdapter<String>( MusicActivity.this, android.R.layout.simple_list_item_single_choice, list); ListView li=(ListView)findViewById(R.id.listView); li.setAdapter(adapter); li.setChoiceMode(ListView.CHOICE_MODE_SINGLE); //单选
-
音乐播放(设已有成员变量MediaPlayer mp=new MediaPlayer(); String song_path="";)
li.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick( AdapterView<?> parent, View view, int position, long id) { song_path="音乐源路径"; try{ mp.reset(); //重置 mp.setDataSource(song_path); mp.prepare(); //准备 mp.start(); //播放 }catch (Exception e){ e.printStackTrace(); } } });
8.音乐停止播放(按返回键结束Activity时触发onDestroy())
@Override
protected void onDestroy() {
super.onDestroy();
if(mp!=null ){
mp.stop();
mp.release();
}
//可以输出提示信息
}
9.音乐暂停和继续
.....
//在点击事件中:
if(song_path.isEmpty()){
//音乐路径空不合法,输出提示信息
}
if( mp.isPlaying() ){//如果播放器正在播放
mp.pause(); //暂停
}else if( !song_path.isEmpty() ){ //音乐路径有效不空
mp.start(); //继续播放
}
.......
2.多线程:填充长度100数组,模拟耗时操作
public class MyActivity extends Activity {
private int[] data = new int[100]; //填充长度为100的数组
private int hasData = 0; //数组已经填充的元素个数
ProgressBar p; //一个进度条组件
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 0x111) { //判断消息标识
int progress=msg.arg1; //取出子线程传来的值
p.setProgress(progress);//设置进度条显示进度
Log.d("test", "progress=" + progress);//打印日志信息
if (progress >= 100){
//输出提示信息,进度100%
//Toast.makeText(getApplicationContext(),......
}
}
}
}
//以填充数组来模拟一个耗时的操作
public int doWork(){
data[hasData++] = (int)(Math.random() * 100); //填充一个数组元素
try{
Thread.sleep(100); //线程休息100毫秒,用于模拟其他耗时动作
}catch (InterruptedException e){
e.printStackTrace();
}
return hasData; //返回已填充的个数,亦即进度值
}
//onCreate方法
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
p = (ProgressBar) findViewById(R.id.progressBar);
Button btn1=(Button)findViewById(R.id.button);
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
hasData=0; //重新开始
//启动线程
new Thread(new Runnable() { //新建一个(子)线程
@Override
public void run() {//实现run()方法,完成耗时操作
while (hasData < 100){
int progress = doWork();
//获取的是耗时操作的完成百分比
Message m = new Message(); //创建消息
m.what = 0x111; //设置消息标识
m.arg1=progress; //简单传值
mHandler.sendMessage(m); // 发送消息到Handler
}
}
}).start();
}
});
}
(完)