安卓第十三天笔记-服务(Service)

Servcie服务

1.服务概念

服务

  • windows

服务没有界面,一直运行在后台, 运行在独立的一个进程里面

  • android

服务没有界面,一直运行在后台,默认是运行当前的应用程序进程里面。

2.建立服务

  • 建立一个类继承Service类

    public class ServiceDemo extends Service {
  • 在清单文件中注册service

    <service android:name="com.ithiema.servicequick.servcie.ServiceDemo"></service>

3.生命周期

只会在开启服务时初始化一次

    @Override
public void onCreate() {
//只会在开启服务时初始化一次
super.onCreate();
}

每次开启服务都会执行调用

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//每次开启服务都会执行调用
return super.onStartCommand(intent, flags, startId);
}

停止服务时,只执行一次

public void onDestroy() {
//停止服务时,只执行一次
super.onDestroy();
}
  • 完整生命周期

onCreate -- onStartCommand--onDestroy

  • 启动多次服务

onCreate方法只会执行一次, 但是onStartCommand执行多次

  • 多次停止服务

只会执行一次onDestroy方法。

4.进程

Foreground process

前台进程: 当前与用户进行交互的应用所处的进程

  • Visible process

可见进程: 应用程序不能交互,但是界面可见。 有点类似activity生命周期的onPause

  • Service process

服务进程: 应用程序里面运行着一个服务

  • Background process

后台进程: 应用程序被最小化了(home)

  • Empty process

空进程:应用程序里面没有任何活动的组件(activity \ service)

前台进程 > 可见进程 > 服务进程 > 后台进程 > 空进程

5.开启与停止服务

public void start(View v){
startService(new Intent(this , ServiceDemo.class));
} public void stop(View v){
stopService(new Intent(this , ServiceDemo.class));
}

6.为什么要使用服务

  1. 可以让进程所处的优先级有所保障。
  2. 可以长久的在后台执行逻辑操作,即使所有的activity都销毁了,也不影响。

场景: 1. 在后台检测设备接入状况 2. 在后台执行联网数据请求(类似股票软件) 3. 音乐播放器

7.绑定服务第一种

Extending the Binder class 第一种

1 先写一个继承Service类

2.在Service中写一个public类继承Binder,在这个类中写个方法返回具服务类的对象实例

3.在onBind 中返回 这个内部类的实例

4.在service中写几个public的方法提供给子类调用必须为public的

 /**
* Created by 刘楠 on 2016-02-28 14:19.
*/
public class LocalService extends Service {
private static final String TAG = "LocalService";
//声明绑定的类
private LocalBinder mBinder = new LocalBinder();
//给客户端使用
private final Random mGenerator = new Random(); /*
第一种继承Binder类
*/
public class LocalBinder extends Binder { public LocalService getServcie() {
Log.d(TAG, "==getServcie==");
return LocalService.this;
} } @Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "==IBinder==");
return mBinder;
} @Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "==onCreate==");
} @Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "==onStartCommand==");
return super.onStartCommand(intent, flags, startId);
} /**
* 返回一个随机数
*
* @return
*/
public int getRandom() {
Log.d(TAG, "==getRandom==");
return mGenerator.nextInt(500);
} @Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "==onUnbind==");
return super.onUnbind(intent);
} @Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "==onDestroy==");
}
}

5.在Manifest.xml中注册service

 <service android:name=".serivice.LocalService"/>

6.在客户端的Activity中绑定service bindService(intnet,ServiceConnection,Context.BINDAUTOCREATE)

7.写一个类实现ServiceConnection接口,在方法中完成获取Service类的实例

8.调用服务中的方法

9.解除绑定

布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> <Button
android:onClick="bind1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="绑定服务1"/> <TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"/> <Button
android:onClick="show"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="调用方法"/> <Button
android:onClick="unBind"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="解绑绑定服务1"/>
</LinearLayout>

客户端类

        Activity

    /**
* 绑定服务
*/
public class MainActivity extends AppCompatActivity {
private static final String TAG ="MainActivity" ;
//服务
private LocalService mService; private boolean mbound = false; private MyServiceConnection mConection; private TextView tv; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "=====MainActivity==onCreate====="); tv = (TextView) findViewById(R.id.tv);
} /**
* 绑定服务
* @param view 按键
*/
public void bind1(View view){
Log.d(TAG, "=====MainActivity==bind1=====");
Intent intent = new Intent(this,LocalService.class); if(mConection==null){
mConection = new MyServiceConnection();
} mbound = bindService(intent, mConection, Context.BIND_AUTO_CREATE); Log.d(TAG, "=====MainActivity==flag=====" + mbound); } public void show(View v) { if (mbound) {
int random = mService.getRandom();
tv.setText(random + "");
} } /**
* 服务连接类
*/
private class MyServiceConnection implements ServiceConnection{ @Override
public void onServiceConnected(ComponentName name, IBinder service) {
LocalService.LocalBinder mBinder= (LocalService.LocalBinder) service; mService = mBinder.getServcie();
Log.d(TAG, "=====MainActivity==MyServiceConnection==onServiceConnected==="); } @Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG,"=====MainActivity==MyServiceConnection==onServiceDisconnected===");
mbound=false;
}
} /**
* 解除绑定
* @param v
*/
public void unBind(View v){
Log.d(TAG,"=====MainActivity==unBind======");
if(mService!=null) {
unbindService(mConection);
mConection=null; }
} /**
* 销毁
*/
@Override
protected void onDestroy() {
Log.d(TAG,"=====MainActivity==onDestroy======");
super.onDestroy();
if(mService!=null&&mConection!=null) {
unbindService(mConection);
mConection=null; }
}
}

以上的方式如果只要给自己程序用同时不要把一些方法私有化的话都可以在Service中写public的方法,使用这种方式

8.绑定服务第二种

使用Messenger与Handler的机制处理,这种方式是一种单线程的,不安全的,目前很少有人用了,会造成排队现象

Service

    /**
* Created by 刘楠 on 2016-02-28 16:29.
*/
public class MessengerService extends Service { public static final int MSG_SAY_HELLO = 1; Messenger mMessenger = new Messenger(new IncomingHandler()); public class IncomingHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){ case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(),"hello Android===handleMessage==",Toast.LENGTH_SHORT).show(); }
}
} /**
*
* @param intent 意图
* @return
*/
@Nullable //参数可以为空
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "===onBind====", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}

清单文件

注册
<service android:name=".service.MessengerService"/>

客户端

    public class MainActivity extends AppCompatActivity {

        private Messenger mService;

        private MyServiceConnection mServiceConnection;

        @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} /**
* 界面可见时,绑定Service
*/
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent();
intent.setClass(this, MessengerService.class); if(mServiceConnection==null){
mServiceConnection = new MyServiceConnection();
}
//绑定
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
} private class MyServiceConnection implements ServiceConnection{ @Override
public void onServiceConnected(ComponentName name, IBinder service) { mService = new Messenger(service);
} @Override
public void onServiceDisconnected(ComponentName name) { }
} /**
* 点击事件
* @param v
*/
public void sayHello(View v) { Message msg = Message.obtain();
msg.what=MessengerService.MSG_SAY_HELLO;
//发送消息
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
} @Override
protected void onDestroy() {
super.onDestroy();
if(mService!=null&&mServiceConnection!=null) {
unbindService(mServiceConnection);
mServiceConnection=null;
}
}
}

9.使用普通接口完成

  • 使用接口的方式建立Service

接口

/**
* Created by 刘楠 on 2016-02-28 18:25.
* 接口定义方法让Service中自定义的Binder来实现这个接口,同时调用Service中公用与私有方法
*/
public interface IRemoteService { public void display(String name,int age); public void gogo(); }

建立Service

    /**
* Created by 刘楠 on 2016-02-28 18:20.
*/
public class LocalService extends Service {
//自定义的IBinder对象
private LocalBinder mLocalBinder= new LocalBinder(); /**
* 自定义的Binder
* 对面接头的内线
*/
public class LocalBinder extends Binder implements IRemoteService {
//接口的方法
@Override
public void display(String name, int age) {
//调用Service中的方法
show(name,age);
}
//接口的方法
@Override
public void gogo() {
//调用Service中的方法
go();
}
//这里算自定义的方法
public void showData(){
Toast.makeText(getApplicationContext(),new Date().toString(),Toast.LENGTH_SHORT).show();
}
} private static final String TAG ="LocalService" ; @Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG,"=====onBind=======");
return mLocalBinder;
} @Override
public void onCreate() {
super.onCreate();
Log.d(TAG,"=====onCreate=======");
} @Override
public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG,"=====onStartCommand=======");
return super.onStartCommand(intent, flags, startId);
} @Override
public boolean onUnbind(Intent intent) {
Log.d(TAG,"=====onUnbind=======");
return super.onUnbind(intent);
} @Override
public void unbindService(ServiceConnection conn) {
super.unbindService(conn);
Log.d(TAG, "=====unbindService=======");
} @Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "=====onDestroy=======");
} /*
上面会调用这个方法
*/
private void show(String name,int age){
Toast.makeText(getApplicationContext(),"姓名:"+name+"年龄:"+age,Toast.LENGTH_SHORT).show();
Log.d(TAG, "====service=show======");
} /*
上面的Binder会调用这个方法
*/ private void go(){
Toast.makeText(getApplicationContext(),"gogogogogogoggog",Toast.LENGTH_SHORT).show();
Log.d(TAG, "====service=go======");
}
}

布局就5个按键

    <?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"> <Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="bind"
android:text="绑定bindService服务"/> <Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="callMethod"
android:text="调用服务中的方法"/> <Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="unBind"
android:text="解除unBindService绑定"/> <Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="start"
android:text="使用startService开始服务"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="stop"
android:text="使用stopService停止服务"/>
</LinearLayout>

Activity

public class MainActivity extends AppCompatActivity {
private static final String TGA ="MainActivity" ;
//连接的类
private MyServcieConnection mConnection;
//IBinder对象
private LocalService.LocalBinder mLocalBinder; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} /**
* 绑定服务
*
* @param view 按键
*/
public void bind(View view) {
Log.d(TGA, "========Activity===bind======");
Intent intent = new Intent(); intent.setClass(this, LocalService.class);
if(mConnection==null){
mConnection = new MyServcieConnection();
}
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
} /**
* 调用服务中的方法
*
* @param view 按键
*/
public void callMethod(View view) {
Log.d(TGA,"========Activity===callMethod======");
if(mLocalBinder!=null){
//调用方法
mLocalBinder.display("张三",18);
mLocalBinder.gogo();
mLocalBinder.showData(); }
} /**
* 解除绑定服务
*
* @param view 按键
*/
public void unBind(View view) {
Log.d(TGA,"========Activity===unBind======");
if(mConnection!=null){
unbindService(mConnection);
mConnection=null;
mLocalBinder=null;
}
} /**
* 开始服务
*
* @param view 按键
*/
public void start(View view) {
Log.d(TGA,"========Activity===start======");
Intent intent = new Intent();
intent.setClass(this, LocalService.class);
startService(intent);
} /**
* 停止服务
*
* @param view 按键
*/
public void stop(View view) {
Log.d(TGA,"========Activity===stop======");
//停止服务
Intent intent = new Intent();
intent.setClass(this, LocalService.class);
stopService(intent);
} @Override
protected void onDestroy() {
super.onDestroy();
Log.d(TGA, "========Activity===onDestroy======");
/* if(mConnection!=null){
unbindService(mConnection);
mConnection=null;
mLocalBinder=null;
return;
}
//停止服务
Intent intent = new Intent();
intent.setClass(this, LocalService.class);
stopService(intent);*/
} /**
* 服务连接类
*/
private class MyServcieConnection implements ServiceConnection{ @Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TGA,"========Activity===onServiceConnected======");
mLocalBinder = (LocalService.LocalBinder) service;
} @Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TGA,"========Activity===onServiceDisconnected======");
}
}
}

注册

    <!--注册-->
<service android:name=".service.LocalService"/>

结果:

02-28 06:39:24.895 4350-4350/? D/MainActivity: ========Activity===bind======

02-28 06:39:24.968 4350-4350/? D/LocalService: =====onCreate=======
02-28 06:39:24.969 4350-4350/? D/LocalService: =====onBind=======
02-28 06:39:24.985 4350-4350/? D/MainActivity: ========Activity===onServiceConnected======
02-28 06:39:28.625 4350-4350/? D/MainActivity: ========Activity===callMethod====== 02-28 06:39:28.631 4350-4350/? D/LocalService: ====service=show======
02-28 06:39:28.637 4350-4350/? D/LocalService: ====service=go======
ace 0xeb9b54a0, error=EGL_SUCCESS
02-28 06:39:30.906 4350-4350/? D/MainActivity: ========Activity===unBind====== 02-28 06:39:30.937 4350-4350/? D/LocalService: =====onUnbind=======
02-28 06:39:30.937 4350-4350/? D/LocalService: =====onDestroy======= 02-28 06:39:32.721 4350-4368/? V/RenderScript: 0xe1f0e200 Launching thread(s), CPUs 4
02-28 06:39:35.827 4350-4350/? D/MainActivity: ========Activity===start====== 02-28 06:39:35.846 4350-4350/? D/LocalService: =====onCreate=======
02-28 06:39:35.846 4350-4350/? D/LocalService: =====onStartCommand=======
02-28 06:39:36.878 4350-4350/? D/MainActivity: ========Activity===stop====== 02-28 06:39:36.902 4350-4350/? D/LocalService: =====onDestroy=======

10.AIDL (AIDL-Android Interface Definition Language)

AIDL可以同时处理多个线程的请求,并且是线程安全的.

To use AIDL directly, you must create an .aidl file that defines the programming interface. The Android SDK tools use this file to generate an abstract class that implements the interface and handles IPC, which you can then extend within your service.

要使用AIDL必须建立一个扩展名为.aidl的文件,SDK会自动生成对象的抽象类同是实现接口与Handles IPC通信,在Service中可以选择继承

例如:支付宝,就只是暴露了,接口给商家使用,里面具体怎么实现并没有暴露

将上面的接口后缀名改为.aidl

  • 更改Serive中的自定义IBinder

    public class LocalBinder extends IRemoteService.Stub {

  • 更改客户端 中声明不在写原来的binder

       更改这个为
    private LocalService.LocalBinder mLocalBinder;

更改后

    //IRemoteService
private IRemoteService mIRemoteService;

Activity

  public class MainActivity extends AppCompatActivity {
private static final String TGA ="MainActivity" ;
//连接的类
private MyServcieConnection mConnection;
//IRemoteService
private IRemoteService mIRemoteService; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} /**
* 绑定服务
*
* @param view 按键
*/
public void bind(View view) {
Log.d(TGA, "========Activity===bind======");
Intent intent = new Intent(); intent.setClass(this, LocalService.class);
if(mConnection==null){
mConnection = new MyServcieConnection();
}
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
} /**
* 调用服务中的方法
*
* @param view 按键
*/
public void callMethod(View view) {
Log.d(TGA,"========Activity===callMethod======");
if(mIRemoteService!=null){
//调用方法
try {
mIRemoteService.display("张三",18);
mIRemoteService.gogo();
} catch (RemoteException e) {
e.printStackTrace();
} }
} /**
* 解除绑定服务
*
* @param view 按键
*/
public void unBind(View view) {
Log.d(TGA,"========Activity===unBind======");
if(mConnection!=null){
unbindService(mConnection);
mConnection=null;
mIRemoteService=null;
}
} /**
* 开始服务
*
* @param view 按键
*/
public void start(View view) {
Log.d(TGA,"========Activity===start======");
Intent intent = new Intent();
intent.setClass(this, LocalService.class);
startService(intent);
} /**
* 停止服务
*
* @param view 按键
*/
public void stop(View view) {
Log.d(TGA,"========Activity===stop======");
//停止服务
Intent intent = new Intent();
intent.setClass(this, LocalService.class);
stopService(intent);
} @Override
protected void onDestroy() {
super.onDestroy();
Log.d(TGA, "========Activity===onDestroy======"); } /**
* 服务连接类
*/
private class MyServcieConnection implements ServiceConnection{ @Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TGA,"========Activity===onServiceConnected======");
mIRemoteService =IRemoteService.Stub.asInterface(service);
} @Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TGA,"========Activity===onServiceDisconnected======");
}
}
}

这种aidl的方式只能调用Serivce接口中的方法,不能调用Service和自定义Binder中特有的方法 常用于给第三方的应用提供服务 * 通过

 IRemoteService.Stub.asInterface(service)

方法得到aidl定义的接口实例

11.startService与bindService的生命周期

  • startService与stopService

startService

        onCreate---创建服务时只执行一次
onStartCommand ----每次开启服务都会调用

stopService

       onDestory--停止服务时多次调用,只会执行一次

*bindService与unBindServcie

bindService

    onCreate---创建服务时只执行一次
onBind----多次调用,只执行一次

unBindService

    onUnbind----解除时,只执行一次
onDestory----只执行一次

安卓第十三天笔记-服务(Service)的更多相关文章

  1. Android学习笔记--服务(Service)

    1.服务概述 1.服务是Android四大组件之一,在使用上可以分为本地服务和远程服务,本地服务是指在不影响用户操作的情况下在后台默默的执行一个耗时操作,例如下载,音频播放等.远程服务是指可以供其他应 ...

  2. 安卓第十一天笔记-Intent与inter-filter配置

    安卓第十一天笔记-Intent与inter-filter配置 Intent与inter-filter配置 1.Intent对象简述 Android应用中有包含三种重要组件:Activity,Servi ...

  3. android服务Service(上)- IntentService

    Android学习笔记(五一):服务Service(上)- IntentService 对于需要长期运行,例如播放音乐.长期和服务器的连接,即使已不是屏幕当前的activity仍需要运行的情况,采用服 ...

  4. 【Android】18.1 利用安卓内置的定位服务实现位置跟踪

    分类:C#.Android.VS2015: 创建日期:2016-03-04 一.安卓内置的定位服务简介 通常将各种不同的定位技术称为位置服务或定位服务.这种服务是通过电信运营商的无线电通信网络(如GS ...

  5. AngularJs学习笔记--Managing Service Dependencies

    原版地址:http://docs.angularjs.org/guide/dev_guide.services.managing_dependencies angular允许service将其他ser ...

  6. Android服务Service

    安卓Service服务 一    Service简介 Service是运行在后台的,没有界面的,用来处理耗时比较长的.Service不是一个单独的进程,也不是一个单独的线程. Service有两种类型 ...

  7. Spring Boot 2.X(十三):邮件服务

    前言 邮件服务在开发中非常常见,比如用邮件注册账号.邮件作为找回密码的途径.用于订阅内容定期邮件推送等等,下面就简单的介绍下邮件实现方式. 准备 一个用于发送的邮箱,本文是用腾讯的域名邮箱,可以自己搞 ...

  8. 在 CentOS7 上将自定义的 jar 包注册为 linux 服务 service

    在 CentOS7 上将自定义的 jar 包注册为 linux 服务 service 1.在 /etc/rc.d/init.d/ 目录下创建一个名字和服务名完全相同的 shell 脚本文件 joyup ...

  9. 安卓第十天笔记-fragment

    安卓第十天笔记-fragment Fragment(片段) 一.Fragment简介 *Fragment是3.0引入的API,主要为了解决平板,大屏幕手机显示问题 *Fragment代表了Activi ...

随机推荐

  1. 使用JavaScript判断用户是否为手机设备

    最近在做微信服务号开发,其中遇到一个问题是微信服务号查看的个人的消息,如果点击在浏览器中查看(iOS中是在Safari中打开)应该是跳转到登录页面,因为页面需要从后台获取,因为需要服务端判断,如果是存 ...

  2. [Git] Git 常用技巧

    版本对比 1. 对比两个 COMMIT git diff <commit> <commit> 2. 对比 COMMIT 和父 COMMIT git diff <commi ...

  3. 白话Redis与Memcached区别

    如果简单地比较Redis与Memcached的区别,外在的区别是: 1  Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储. 2  Redis ...

  4. Sqoop实现关系型数据库到hive的数据传输

    Sqoop实现关系型数据库到hive的数据传输 sh脚本 #!/bin/sh v_columns=NOTE_ID_1,NOTE_NAME_1,NOTE_ID_2,NOTE_NAME_2,NOTE_ID ...

  5. [Python] Interpreter setting in Pycharm

    From: http://blog.csdn.net/u013088062/article/details/50135135 From: http://blog.csdn.net/u013088062 ...

  6. [ML] Naive Bayes for Text Classification

    TF-IDF Algorithm From http://www.ruanyifeng.com/blog/2013/03/tf-idf.html Chapter 1, 知道了"词频" ...

  7. 快速清除文件夹svn版本控制信息

    将下面内容另存为clear.bat文件,在有版本控制的目录执行即可 @echo On @Rem 清除SVN版本控制信息 @for /r . %%a in (.) do @if exist " ...

  8. Google Code jam Qualification Round 2015 --- Problem A. Standing Ovation

    Problem A. Standing Ovation Problem's Link:   https://code.google.com/codejam/contest/6224486/dashbo ...

  9. WinForm公共控件

    公共控件:1.Button:按钮 用户点击时触发事件 行为属性 Enabled -是否启用 Visible -是否隐藏2.CheckBox .CheckListBox - 复选框 复选框组 3.Com ...

  10. 利用name或id属性设置页面跳转的锚点

    理论准备         网页中的链接按照链接路径的不同,可以分为3种类型,分别是内部类型.锚点链接和外部链接:         按照使用对象的不同,网页中的链接又分为文本超链接,图像超链接,E-ma ...