安卓逆向入门-Android后端开发入门

Activity

在一个Android应用中,多个Activity组成Activity栈,当前活动的Activity位于栈顶,之前的被压入下面,成为非活动Activity,等待是否可能被恢复为活动状态。Activity声明周期中有4个状态:

状态 描述
运行状态 当前Activity,位于Activity栈顶,用户可见,可获得焦点
暂停状态 失去焦点的Activity,仍然可见
停止状态 被其他Activity覆盖,不可见,但保存所有状态信息。低内存状态可能被系统kill
销毁状态 该Activity结束

生命周期中回调方法有:

方法 描述
onCreate 创建Activity时被回调
onStart 启动Activity时被回调
onResume 由暂停恢复为活动状态时调用
onPause 暂停Activity时被回调
onRestart 重启Activity时被回调
onStop 停止Activity时被回调
onDestroy 销毁Activity时被回调

创建Activity方法为:

1
2
3
4
5
6
7
8
import android.app.Activity;
public class MyActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my); //设置要显示的页面activity_my.xml
}
}

还要在AndroidManifest.xml中application标签中配置activity标签:

1
2
3
4
5
6
7
8
9
<application>
<activity
android:anme=".DetailActivity"
android:label="说明性文字"
android:theme="要应用的主题"
>
...
</activity>
</application>

操作有:

1
2
3
4
5
6
//启动DetailActivity
Intent intent=new Intent(MainActivity.this,DetailActivity.class);
startActivity(intent);

//关闭当前Activity 非主活动则返回调用的Activity 否则返回主屏幕
finish();

Bundle是个键值对的组合,保存要携带的数据包。在Activity之间交换数据时,可将要保存的数据存放在Bundle对象中,通过Intent传递数据。

1
2
3
4
5
6
7
8
9
10
11
//发送至AddressActivity
Intent intent=new Intent(MainActivity.this, AddressActivity.class);
Bundle bundle=new Bundle();
bundle.putCharSequence("name",name);
intent.putExtras(bundle);
startActivity(intent); //启动Activity

//获取
Intetnt intent=getIntent();
Bundle bundle=intent.getExtras();
System.out.println(bundle.getString("name"));

还可以调用另一个Activity并返回结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//调用HeadActivity
Intent intent=new Intent(MainActivity.this,HeadActivity.class);
startActivityForResult(intent,0x11); //启动intent对应Activity

//被调用的返回结果
Intent intent=getIntent();
Bundle bundle=new Bundle();
bundle.putInt("imageId",imageId[posistion]);
intent.putExtras(bundle);
setResult(0x11,intent); //设置返回的结果码
finish(); //关闭当前Activity

//调用方获取结果
@Override
protected void onActivityResult(int requestCode,int resultCode,Intent data) {
super.onActivityResult(requestCode,resultCode,data);
if(requestCode==0x11&&resultCode==0x11){
Bundle bundle=data.getExtras();
int imageId=bundle.getInt("imageId");
//...
}
}

Fragment可在一个单独的Activity中建立多个UI面板,也可在多个Activity中重用Fragment。一个Fragment必须嵌入到一个Activity中,也有自己的生命周期。当Activity被暂停/销毁时,其中的Fragmen也被暂停/销毁。当一个Activity在运行时,可单独对每个Fragment进行操作。创建方法如下:

1
2
3
4
5
6
7
8
public class NewsFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState){
View v=inflater.inflate(R.layout.news,container,false);
return false;
}
}

为布局文件.xml添加Fragment:

1
2
3
4
5
6
7
8
<fragment
android:name="com.xxx.ListFragment"
android:id="@+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent"
/>
...

或者在Activity运行时添加Fragment法:

1
2
3
4
DetailFragment details=new DetailFragment();
FragmentTransaction ft=getFragmentManager().beginTransaction();
ft.add(android.R.id.content,details);
ft.commit();

Intent

一个Android程序由Activity、Service和BroadcastReceiver组件组成,它们之间通信由Intent协助完成,后者负责对应用中一次操作的Action、Action涉及的Data、Extras进行描述。Android根据Intent的描述,找到对应组件,将Intent传给调用的组件,我弄成组件的调用。Intent起着媒体中介作用,提供组件间互相调用的相关信息,实现调用者和被调用者之间的解耦。

Intent对象包含以下属性:

属性 作用
Component name 指定为处理Intent对象的组件名称
Action Intent要完成的一个动作
Category 用来对执行动作的类别进行描述
Data 向Action提供要操作的数据
Extras 向Intent组件添加附加信息
Flags 指示Android程序如何启动一个Activity

Component name属性设置Intent对象的组件名称,属性值是个ComponentName对象。,需要指定包名和类名,可选。若设置该属性则Intent对象或被发送给指定类的实例,若没设置,Android用Intent中其他信息定位合适的目标组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//设置该属性
ComponentName(Context context,Class<?> cls);
ComponentName(String pkg,String cls);
//例如
Intent intent=new Intent();
ComponentName componentName=new ComponentName(MainActivity.this,DetailActivity.class);
//或ComponentName componentName=new ComponentName("com.xxx","com.xxx.xxx");
intent.setComponent(componentName);

//设置要打开的Activity类
public Intent setClass(Context packageContext,Class<?> cls);
//例如
Intent intent=new Intent();
intent.setClass(this,DetailActivity.class);

//设置要打开的Activity名称
public Intent setClassName(Context packageContext,String className);
//例如
Intent intent=new Intent();
intent.setClassName(MainActivity.this,"com.xxx.xxx");

//获取相关组件
public ComponentName getComponent();
//例如
ComponentName componentName=getIntent().getComponent();
Log.i(componentName.getPackageName()+componentName.getShortClassName()); //包名 类名

Action属性指定要执行的动作,目标组件分为Activity和Broadcast两类。标准Activity用于启动Activity,动作字符串如“android.intent.action.MAIN”:

字符串 说明
MAIN 初始Activity启动,没有数据输入输出
VIEW 显示数据
ATTACH_DATA 数据该附属于其他地方
EDIT 显示数据并用于编辑
PICK 选择并返回一项数据
CHOOSER 显示一个Activity选择器
GET_CONTENT 允许用户选择并返回特定类型数据
DIAL 拨打电话
CALL 给某人拨打电话
SEND 发送消息
SENDTO 指定接收者并发送消息
ANSWER 接听电话
INSERT 向容器中插入空白项
DELETE 从容器中删除给定数据
RUN 无条件运行数据
SYNC 执行数据同步
PICK_ACTIVITY 选择给定Intent的Activity,返回选择的类
SEARCH 执行查询
WEB_SEARCH 执行联机查询
FACTORY_TEST 工厂测试主注入点

标准广播动作用于接收广播:

字符串 说明
TIME_TICK 每分钟通知一次当前时间改变
TIME_CHANGED 通知时间被修改
TIMEZONE_CHANGED 通知时区被修改
BOOT_COMPLETED 系统启动完成后发出一次通知
PACKAGE_ADDED 通知新应用程序包已安装到设备上
PACKAGE_CHANGED 通知已安装应用程序包已被修改
PACKAGE_REMOVED 从设备中删除应用程序包
PACKAGE_RESTARTED 通知用户重启应用程序包,其所有进程被关闭
PACKAGE_DATA_CLEARED 通知用户清空程序包中数据
UID_REMOVED 通知从系统中删除用户ID值
BATTERY_CHANGED 含充电状态、等级和其他电池信息
POWER_CONNECTED 通知设备连接外置电源
POWER_DISCONNECED 通知设备移除外置电源
SHUTDOWN 通知设备已关闭

方法有:

1
2
3
4
//设置动作
public Intent setAction(String action);
//获取动作名称
public String getAction();

Data向Action提供要操作的数据,数据规格如下:

操作类型 实例
浏览网页 http://ww.xxx.com
拨打电话 tel:043187654321
发送短信 smsto:13666666666
查找SD卡文件 file:///sdcard/Download/ly.jpg
显示地图 geo:26.236966,-26.998566
联系人信息 content://com.android.contacts/contacts/1

方法有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//设置URI数据
public Intent setData(Uri data);
//例如
intent.setData(Uri.parse("content://com.android.contacts/contacts/1"));

//设置数据MIME类型
public Intent setType(String type);
//例如
intent.setType("image/");

//设置数据和MIME类型
public Intent setDataAndType(Uri data,String type);
Uri uri=Uri.fromFile(new File(Environment.getExternalStorageDirectory().getPath()+"/pictures/img01.png"));
intent.setDataAndType(uri,"image/*");

//获取相关数据
public Uri getData();

//获取MIME类型
public String getType();

Category属性对执行动作的类别进行描述。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//添加种类信息
public Intent addCategory(String category);
//例如设置Category为系统桌面
Intent intent=new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory("android.intent.category.HOME");

//删除指定种类信息
public void removeCategory(String category);
//例如移除系统桌面种类信息
intent.removeCategory("android.intent.category.HOME");

//获取种类信息
public Set<String> getCategories();
//例如
Set<String> categories=intent.getCategories();

Extras添加附加信息:

1
2
3
4
5
6
7
8
9
//添加附加信息
public Intent putExtras(Bundle extras);
//例如
Bundle bundle=new Bundle();
bundle.putCharSequence("name","xxx");
intent.putExtras(bundle);

//获取附加信息
public Bundle getExtras();

Flags属性指示Android程序如何启动一个Activity,以及启动后如何处理。

1
2
3
4
5
6
7
8
9
10
//设置标志
public Intent setFlags(int flags); //多次使用则将之前的替换掉
//例如 设置新Activity在用户离开它后自动关闭
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);

//添加标志
public Intent addFlags(int flags);

//获取标志
public int getFlags();

隐式Intent为创建Intent对象时不指定具体接收者,让系统根据相应匹配机制找,如:

1
2
3
4
Intent intent=new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.xxx.com"));
startActivity(intent);

消息机制

消息提示框:

1
Toast toast=Toast.makeText(this,"内容",Toast.LENGTH_SHORT);

对话框:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//按钮提示对话框
AlertDialog alertDialog=new AlertDialog.Builder(MainActivity.this).create();
alertDialog.setIcon(R.drawable.advise);
alertDialog.setTitle("标题");
alertDialog.setMessage("内容");
alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE,"是",new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog,int which){
Toast.makeText(MainActivity.this,"xxx",Toast.LENGTH_SHORT).show();
}
});
alertDialog.show();

//列表对话框
final String[] items=new String[]{"xxx","xxx","xxx","xxx"};
AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this);
builder.setIcon(R.drawable.advise1);
builder.setTitle("标题");
builder.setItems(items,new DialogInterface.OnClickListener(){ //单选对话框为setSingleChoiceItems 多选对话框为setMultiChoiceItems
@Override
public void onClick(DialogInterface dialog,int which){
Toast.makeText(MainActivity.this,items[which],Toast.LENGTH_SHORT).show();
}
});
builder.create.show();

状态栏上显示通知:

1
2
3
4
5
6
7
8
9
10
11
12
13
final int NOTIFYID=0x123; //通知的ID
final NotificationManager notificationManager=(NotificationManager)getSystemService(NOTIFICATION_SERVICE); //获取通知管理器
Notification.Builder notification=new Notification.Builder(this);
notification.setAutoCancel(true); //打开该通知后自动消失
notification.setSmallIcon(R.drawable.packet); //通知图标
notification.setContentTitle("标题");
notification.setContentText("内容");
notification.setDefaults(Notification.DEFAULT_SOUND|Notification.DEFAULT_VIBRATE); //默认声音和LED灯 启用震动 需要开权限
notification.setWhen(System.currentTimeMillis()); //发送时间
Intent intent=new Intent(MainActivity.this,DetailActivity.class);
PendingIntent pi=PendingIntent.getActivity(MainActivity.this,0,intent,0);
notification.setContentIntent(pi);
notificationManager.notify(NOTIFYID,notification.build());

当发生电池电量低、系统启动完成、系统时间/日期改变、系统连接电源、系统被关闭等事件时发送系统广播。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//发送广播
Intent intent=new Intent();
intent.setAction("xxx");
sendBroadcast(intent); //发送广播

//新建Broadcast Receiver 还要在AndroidManifest.xml中注册方法略
public class MyReceiver extends BroadcastReceiver{
private static final String action1="xxx";
public MyReceiver(){}
@Override
public void onReceive(Context context,Intent intent){
if(intent.getAction().equals(action1))
Toast.makeText(context,"xxx",Toast.LENGTH_SHORT).show();
}
}

多媒体

Android支持的音频格式为.mp3、.3gp、.ogg、.wav等,视频格式为.3gp、.mp4等。

使用MediaPlayer播放音频:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//装载对象
MediaPlayer player=MediaPlayer.create(this,Uri.parse("http://www.xxx.com/bg.mp3"));
//另一种装载
MediaPlayer player1=new MediaPlayer();
try{
player1.setDataSource("/sdcard/music.mp3");
player1.prepare();
}
catch(IOException e){
e.printStackTrace();
}

player.start(); //开始播放
player.stop(); //停止播放
player.pause(); //暂停播放

使用SoundPool播放音频:

1
2
3
4
5
6
7
8
9
//构造
SoundPool(int maxStreams,int streamType,int srcQuality); //可容纳多少音频 声音类型 音频品质
//加载音频
public int load(Context context,int resId,int priority);
public int load(Stirng path,int priority);
public int load(AssetFileDescriptor afd,int priority);
public int load(FileDescriptor fd,long offset,long length,int priority);
//播放音频
play(int soundID,float leftVolume,float rightVolume,int priority,int loop,float rate); //要播放的音频 左声道音量 右声道音量 播放音频优先级(越大越高) 循环次数(-1循环) 速率(0.5~2)

使用VideoView播放视频:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
VideoView video=(VideoView)findViewById(R.id.video);
File file=new File(Environment.getExternalStorageDirectory()+"/video.mp4");
if(file.exists()){
video.setVideoPath(file.getAbsolutePath());
video.setMediaController(mc); //关联
video.requestFocus(); //获得焦点
try{
video.start(); //开始播放
}
catch(Exception e){
e.printStackTrace();
}
video.setOnCompletionListener(new MediaPlayer.OnCompletionListener(){ //播放结束
@Override
public void onCompletion(MediaPlayer mp){
//...
}
});
}
else{
//...
}

数据存储

SharedPreferences方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//获取
getSharedPreferences(String name,int mode);
//name共享文件名 不含扩展名 XML格式
//mode访问权限 MODE_PRIVATE只能被本应用读写 写入覆盖 MODE_MULTI_PROCESS可跨进程/应用读取
getPreferences(int mode);//只有一个文件

//存储数据
SharedPreferences.Editor editor=getSharedPreferences("xxx",MODE_PRIVATE).edit();
editor.putString("username",username);
editor.putBoolean("status",false);
editor.putInt("age",20);
editor.commit();

//读取数据
SharedPreferences sp=getSharedPreferences("xxx",MODE_PRIVATE);
String username=sp.getString("username","xxx");
Boolean status=sp.getBoolean("status",false);
int age=sp.getInt("age",18);

内部存储如下,卸载应用程序时保存的数据文件一起被删除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//写入文件
try{
FileOutputStream fos=openFileOutput("xxx.txt",MODE_PRIVATE);
fos.write("xxx".getBytes());
fos.flush();
fos.close();
}
catch(FileNotFoundException e){
e.printStackTrace();
}

//读取文件
FileInputStream fis=openFileInput("xxx.txt");
byte[] buffer=new byte[fis.available()];
fis.read(buffer);

外部存储:

1
2
3
4
private File file;
file=new File(Environment.getExternalStorageDirectory(),"xxx.txt");
fos=new FileOutputStream(file);
fis=new FileInputStream(file);

Content Provider用于不同应用之间实现数据共享。它使用基于数据库模型的简单表格来提供其中数据,每条记录包含一个数值型“_ID”字段,用于唯一标识该记录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static final Uri CONTENT_URI=Uri.parse("content://com.xxx.xxx");
private String columns=ContactsContract.Contacts.DISPLAY_NAME;
private CharSequence getQueryData(){
StringBuilder sb=new StringBuilder();
ContentResolver resolver=getContentResolver();
Cursor cursor=resolver.query(ContactsContract.Contacts.CONTENT_URI,null,null,null,null);
int displayNameIndex=cursor.getColumnIndex(columns);
for(cursor.moveToFirst();!cursor.isAfterLast();cursor.moveToNext()){
String displayName=cursor.getString(displayNameIndex);
sb.append(displayName+"\n");
}
cursor.close();
return sb.toString();
}

Handler消息处理

创建新线程:

1
2
3
4
5
6
7
Thread thread=new Thread(new Runnable(){
@Override
public void run(){
//...
}
});
thread.start();

Handler时一个用来更新UI的机制,也是个消息处理的机制。

在任意线程中发送消息时,将Message用sendMessage发送到MessageQueue中,发送时指定延迟时间、发送时间以及要携带的Bundle数据。当Looper循环到该Message时,调用相应Handler对象handlerMessage对其处理。

在主线程中获取并处理消息时,重写Handler类中处理消息的方法,当新启动的线程发送消息时,Handler类中处理消息的方法会被自动回调。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//例如进度条
final int TIME=60;
final int TIMER_MSG=0x001;
private ProgressBar timer;
private int mProgressStatus=0;
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg){
if(TIME-mProgressStatus>0){
mProgressStatus++;
timer.setProgress(TIME-mProgressStatus);
handler.sendEmptyMessageDelayed(TIEMR_MSG,1000); //一秒后发送消息
}
else{
//...
}
}
};

一个线程对应一个Looper对象,而一个Looper对象又对应一个MessageQueue,后者用于存放Message。Handler发送Message给Looper管理的MessageQueue,然后Looper从MessageQueue中取出消息,分配给Handler进行处理。要在程序中使用Handler,必须在当前线程中有个Looper对象,线程中Looper对象有两种创建方式:主UI线程中,系统已经初始化了一个Looper对象,程序中可直接创建Handler;子线程中必须手动创建并启动一个Looper对象。

Message被存放在MessageQueue中,一个MessageQueue可包含多个Message对象。一个Message对象有5个属性:

属性 类型 描述
arg1 int 存放整型数据
arg2 int 存放整型数据
obj Object 存放发送给接收器的Object类型任意对象
replyTo Messenger 指定此Message发送到何处
what int 指定用户自定义的消息代码

例如循环播放广告的消息部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
final int FLAG_MSG=0X001;
private Message message;

Handler handler=new Handler(){
@Override
public void handleMessage(Message msg){
if(msg.what==FLAG_MSG)
flipper.showPrevious();
message=handler.obtainMessage(FLAG_MSG);
handler.sendMessageDelayed(message,3000);
}
}

//onCreate中
message=Message.obtain();
message.what=FLAG_MSG;
handler.sendMessage(message);

Looper对象用来为线程开启一个消息循环,操作MessageQueue。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//类
public class LooperThread extends Thread{
public Handler handler;
@Override
public void run(){
super.run();
Looper.prepare();
handler=new Handler(){
public void handleMessage(Message msg){
//...
}
};
Message m=handler.obtainMessage();
m.what=0x7;
handler.sendMessage(m);
Looper.loop();
}
}

//MainActivity
LooperThread thread=new LooperThead();
thread.start();

Service应用

Service时能够在后台长时间运行,并不提供用户界面的应用程序组件。其他应用程序组件能启动Service,即使用户切到另一个应用程序,Service还可在后台运行。组件能绑定到Service并与之交互,甚至执行进程间通信IPC。

Service按照启动方式分为Started Service和Bound Service两种。对于Started Service,当应用程序组件用startService启动Service时,Service处于启动状态。一旦启动,Service能在后台无限期运行。对于Bound Service,当应用程序组件用bindService绑定到Service时,后者处于绑定状态。多个组件可一次绑定到一个Service上,当它们都解绑定时,Service被销毁。

当其他组件用startService时,Service被创建,并无限期运行,自身必须用stopSelf或其他组件用stopService来停止Service。当Service停止时系统将其销毁。当其他组件用bindService时,Service被创建,接着客户端通过IBinder接口于Service通信。客户端通过unbindService关闭连接。多个客户端能绑定到同一个Service,当它们都解绑定时,系统销毁Service,而不需要被停止。

对于创建启动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class MyService extends Service{
public MyService(){}
@Override
public IBinder onBind(Intent intent){
throw new UnsupportedOperationException("xxx");
}
@Override
public void onDestroy(){
//...
super.onDestroy();
}
@Override
public void onCreate(){
//...
super.onCreate();
}
public int onStartCommand(Intent intent,int flags,int startId){
new Thread(new Runnable(){
@Override
public void run(){
//模拟一段耗时任务
long endTime=System.currentTimeMillis()+5*1000;
while(System.currentTimeMillis()<endTime){
synchronized(this){
try{
wait(endTime-System.currentTimeMillis());
}
catch(Exception e){
e.printStackTrace();
}
}
}
stopSelf(); //停止Service
}
}).start();
}
}

//启动
Intent intent=new Intent(this,MyService.class);
startService(intent);

对于绑定启动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class BinderService extends Service{
public MyService(){}
public class MyBinder extends Binder{
public BinderService getService(){
return BinderService.this;
}
}
@Override
public IBinder onBind(Intent intent){
return new MyBinder();
}
@Override
public void onDestroy(){
super.onDestroy();
}
}

//MainActivity
BinderService binderService;
private ServiceConnection conn=new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name,IBinder service){
binderService=((BinderService.MyBinder)service).getService();
}
@Override
public void onServiceDisconnected(ComponentName name){}
};
//MainActivity内
@Override
protected void onStart(){
super.onStart();
Intent intent=new Intent(this,BinderService.class);
bindService(intent,conn,BIND_AUTO_CREATE);
}
@Override
protected void onStop(){
super.onStop();
unbindService(conn); //解除绑定
}

Service不会专门启动一个线程执行好事操作,所有操作都是在主线程中进行,以至于容易出现应用无响应ANR情况,需要手动开一个子线程。Service不会自动停止,需要stopSelfstopService停止。使用IntentService时,开启Service后会自动开启一个新线程来执行,Service运行结束后会自动停止。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MyIntentService extends IntentService{
public MyIntentService(){
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent){
//...
}
@Override
public void onDestroy(){
//...
}
}

传感器

x轴沿屏幕向右,y轴沿屏幕向前,z轴垂直屏幕向上。

例如重力传感器和光线传感器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class MainActivity extends AppCompatActivity implements SensorEventListener{
private SensorManager sensorManager;
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
sensorManager=(SensorManager)getSystemService(SENSOR_SERVICE);
}
@Override
public void onSensorChanged(SensorEvent event){
float[] values=event.values;
int sensorType=event.sensor.getType();
switch(sensorType){
case Sensor.TYPE_GRAVITY:
StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append(values[0]); //x m/s^2
stringBuilder.append(values[1]); //y
stringBuilder.append(values[2]); //z
//...
break;
case Sensor.TYPE_LIGHT:
StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append(values[0]); //Lux
//...
break;
}
}
@Override
public void onAccuracyChanged(Sensor sensor,int accuracy){}
@Override
protected void onResume(){
super.onResume();
sensorManager.registerListener(this,sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY),SensorManager.SENSOR_DELAY_GAME);
sensorManager.registerListener(this,sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT),SensorManger.SENSOR_DELAY_GAME);
}
@Override
protected void onPause(){
sensorManager.unegisterListener(this);
super.onPause();
}
@Override
protected void onStop(){
sensorManager.unregisterListener(this);
super.onStop();
}
}

磁场传感器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private SensorManager sensorManager;
private float[] allValue;

//构造函数
sensorManager=(SensorManager)context.getSystemService(Context.SENSOR_SERIVCE);
sensorManager.registerListener(this,sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),SensorManager.SENSOR_DELAY_GAME);

//onSensorChanged
if(event.sensor.getType()==Sensor.TYPE_MAGNETIC_FIELD){
float value[]=event.values; //μT
allValue=value;
super.postInvalidate();
};

//onDraw
if(allValue!=null){
float x=allValue[0];
float y=allValue[1];
//...
};

加速度传感器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private SensorManager sensorManager;
private Vibrator vibrator;

//onCreate
sensorManager=(SensorManager)getSystemService(SENSOR_SERVICE);
vibrator=(Vibrator)getSystemService(Service.VIBRATOR_SERVICE);
sensorManager.registerListener(this,sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_GAME);

//onSensorChanged
int sensorType=event.sensor.getType();
if(sensorType==Sensor.TYPE_ACCELEROMETER){
float[] values=event.values; //m/s^2
//...
vibrator.vibrate(500);
sensorManager.unregisterListener(this);
};

位置服务

Android系统需要使用以下类访问定位服务:

API类 描述
LocationManager 提供系统定位服务访问功能
LocationProvider 定位组件抽象表示,可获得该定位组件相关信息
Location 特定时间地理位置信息,位置由经纬度、UTC时间戳、可选的高度、速度、方向等

LocationProvider是位置源,提供定位信息,常用的有:

方法名 描述
passive 被动定位,利用其他应用使用定位更新了定位信息,系统会保存下来,该应用收到消息后直接读取即可
gps 通过GPS芯片利用卫星获取定位信息
network 通过网络获取定位信息,通常用手机基站和WIFI节点地址大致定位

下面例子直接指定GPS为提供者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//onCreate
LocationManager locationManager=(LocationManager)getSystemService(LOCATION_SERVICE);
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
1000,
1,
new LocationListener(){
@Override
public void onLocationChanged(Location location){ //GPS信息改变时触发 更新位置信息
locationUpdates(location);
}
@Override
public void onStatusChanged(String provider,int status,Bundle,extras){} //位置状态改变
@Override
public void onProviderEnabled(String provider){} //定位提供者启动
@Override
public void onProviderDisabled(String provider){} //定位提供者关闭
}
);
Location location=locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
locationUpdates(location);

//locationUpdates
public void locationUpdates(Location location){
if(location!=null){
StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append(location.getLongitude());
stringBuilder.append(location.getLatitude());
stringBuilder.append(location.getAccuracy());
stringBuilder.append(location.getAltitude());
stringBuilder.append(location.geBearing());
stringBuilder.append(location.getSpeed());
stringBuilder.append(location.getTime());
};
}

网络编程

对于使用GET请求的HttpURLConnection:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
private Handler handler;
public String base64(String content){
try{
content=Base64.encodeToString(content.getBytes("utf-8"),Base64.DEFAULT);
content=URLEncoder.encode(content,"utf-8");
}
catch(UnsupportedEncodingException e){
e.printStackTrace();
}
return content;
}
public void send(){
String target="";
target="http://xxx.xxx.xxx.xxx:8080/get.jsp?content="+base64(content.getText().toString().trim());
URL url;
try{
url=new URL(target);
HttpURLConnection urlConn=(HttpURLConnection)url.openConnection();
InputStreamReader in=new InputStreamReader(urlConn.getInputStream());
BufferedReader buffer=new BufferedReader(in);
String inputLine=null;
while((inputLine=buffer.readLine())!=null)
result+=inputLine+"\n";
in.close();
urlConn.disconnect();
}
catch(MalformedURLException e){
e.printStackTrace();
}
catch(IOException e){
e.printStackTrace();
}
}

//onCreate
button.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
if("".equals(content.getText().toString())){
//...
return;
}
handler=new Handler(){
@Override
public void handleMessage(Message msg){
super.handleMessage(msg);
//...
}
};
new Thread(new Runnable(){ //网络调用一般不在主线程上
public void run(){
send();
Message m=handler.obtainMessage();
handler.sendMessage(m);
}
}).start();
}
})

对于POST请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//...
public void send(){
String target="http://xxx.xxx.xxx.xxx:8080/post.jsp";
URL url;
try{
url=new URL(target);
HttpURLConnection urlConn=(HttpURLConnection)url.openConnection();
urlConn.setRequestMethod("POST");
urlConn.setDoInput(true); //向连接中写入数据
urlConn.setDoOutput(true); //从连接中读取数据
urlConn.setUseCaches(false); //禁止缓存
urlConn.setInstanceFollowRedirects(true); //自动执行HTTP重定向
urlConn.setRequestProperty("Content-Type","application/x-www-form-urlencoded"); //设置内容类型
DataOutputStream out=new DataOutputStream(urlConn.getOutputStream()); //获取输出流
String param="usernam="+URLEncoder.encode(edit_Username.getText().toString(),"utf-8")+"&password="+URLEncoder.encode(edit_Password.getText().toString(),"utf-8"); //要提交的数据
out.writeBytes(param); //写入数据输出流
out.flush(); //输出缓存
out.close();
if(urlConn.getResponseCode()==HttpURLConnecion.HTTP_OK){
InputStreamReader in=new InputStreamReader(urlConn.getInputStream());
BufferedReader buffer=new BufferedReader(in);
String inputLine=null;
while((inputLine=buffer.readLine())!=null)
result+=inputLine+"\n";
in.close();
}
urlConn.disconnect();
}
catch(MalformedURLException e){
e.printStackTrace();
}
catch(IOException e){
e.printStackTrace();
}
}
//...

对于JSON格式的处理,演示handleMessage的重写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
TextView[][] tv={{step,time,heat,km},{step1,time1,heat1,km1}};
try{
JSONArray jsonArray=new JSONArray(result);
for(int i=0;i<jsonArray.length();i++){
JSONObject jsonObject=jsonArray.getJSONObject(i);
tv[i][0].setText(jsonObject.gtString("step"));
tv[i][1].setText(jsonObject.getString("time"));
tv[i][2].setText(jsonObejct.getString("heat"));
tv[i][3].setText(jsonObject.getString("km"));
}
}
catch(JSONException e){
e.printStackTrace();
}

Android提供内置浏览器,使用开源WebKit引擎,需要通过WebView组件实现。

1
2
3
4
5
6
7
8
9
10
11
WebView webView=(WebView)findViewById(R.id.webView1);
webView.loadUrl("http://xxx.xxx.com/xxx.html");
webView.getSettings().setUseWideViewPort(true); //可任意比例缩放
webView.getSettings().setLoadWithOverviewMode(true); //加载内容自适应屏幕

//加载HTML
webView.loadDataWithBaseURL(null,"<br>","text/html","utf-8",null); //第一个null表示用空白页 第二个null表示历史URL为空白页

//开启JavaScript
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient()) //处理对话框