android中Service使用详解

service用于长期在后台处理任务,而不需要对用户可见。
service有2种基本的启动方式:
startService():使用这种方式,来进行单一的任务,不需要返回结果给调用者
bindService():与上面的相反。

一个简单的单一模式启动的服务图示:


当然除了上面的,也可以混合着使用,图示:



下面是一些关于服务的重要说明,非常值得详细了解的:


继承service,实现自己的service;
在manifest中声明service,服务位于主线程,并不会创建自己的子线程。
下面是一些重写的方法:


onCreate();当服务被创建时调用,只调用一次。


onStartCommand();它与startService()对应,当服务启动后调用。如果你重写了该方法,你就有责任自己去
当任务结束以后,调用stopSelf()或者stopService()来停止服务。如果你是绑定的服务,就不需重新该方法了。


onBind();它与bindService()对应,通过返回IBinder,来与service交流。如果你并不像绑定它,就直接返回null


onDestroy();当服务不再被使用时需要销毁时调用,你应该在这里用来停止线程,注销监听器,广播。


如果一个组件如activity使用的是 startService()来启动服务的话,就会触发 onStartCommand(),然后服务就会一直运行,直到任务结束;服务的停止需要
手动控制:在启动服务的组件中调用 stopService()或者在服务本类中调用stopSelf() 


如果一个组件使用的是bindService()来启动服务的话,该服务就会运行,直到组件不约束它。


在系统内存很低的情况下,系统会强制停止服务,来恢复用于运行activity;但是下面这些情况下的服务不易被系统停止:服务与activity绑定或者
服务位于前台。但是如果服务最后还是被停止了,我们要重新启用服务。


service的创建和使用,

也可以使用IntentService,它是service子类,在处理后台任务时,不需要你自己开线程,可以直接在 onHandleIntent()中进行任务。

<span style="font-size:14px;">1.<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" 
      		 android:exported="false"
      />
      ...
  </application>
</manifest>

public class HelloIntentService extends IntentService {

    A constructor is required, and must call the super IntentService(String)
    constructor with a name for the worker thread.
  public HelloIntentService() {
      super("HelloIntentService");
  }

   * The IntentService calls this method from the default worker thread with
   * the intent that started the service. When this method returns, IntentService
   * stops the service, as appropriate.
  @Override
  protected void onHandleIntent(Intent intent) {
      // Normally we would do some work here, like download a file.
      // For our sample, we just sleep for 5 seconds.
      try {
          Thread.sleep(5000);
      } catch (InterruptedException e) {
          // Restore interrupt status.
          Thread.currentThread().interrupt();
      }
  }
}</span>
<span style="font-size:14px;">
</span>

使用intentservice,在任务结束后会自动关闭服务。

2.使用startService()来启动服务,一般在不需要交互的情况下使用这种方式,

在onStartCommand()中接受Intent ;
Intent intent = new Intent(this, HelloService.class);
startService(intent);


如果你的服务没有经过绑定,那么startService(intent)中的intent就是唯一的向service交互的方式;service中通过广播来向外发布回调。如果多次启动服务,会导致onStartCommand()被多次调用。


onStartCommand()必须返回一个整形值,描述这当系统杀掉服务时,系统该如何继续service
START_NOT_STICKY,杀掉后,不再重建
START_STICKY,杀掉后,自动重启,然后接收的intent=null
START_REDELIVER_INTENT,杀掉后,自动重启,然后接收的intent不为空;像文件下载

3.使用bindService()启动;这种方式使用更加复杂,但也更加灵活。

当你需要与服务交互的时候,可以采用这种方式;
必须重写 onBind()方法返回IBinder,来与其他组件交流。在其他组件中获取该IBinder对象
有一种client-server的感觉,可以有多个客户端与server交流


客户端组件,必须创建ServiceConnection,用来监听与service的连接;
可以有多个客户端连接service,但是service的onBind只会执行一次,所以分发给其他客户端的都是同一个IBinder对象
只有在多个客户端都解绑了服务之后,服务才会被销毁。


使用这种服务有个最重要的地方就是定义IBinder;
有3种方式:
a.直接继承Binder,这也是一般app常用的一种方式,适用于你的服务与你的app是处于同一个进程中的。你的服务仅仅是为了
处理后台任务。


b.使用Messenger,适用于需要跨进程通讯且线程安全且非并发,同一个时间只能接受到一个请求,你可以使用Messenger和Handler这2个 类来进行service和client的交互。

服务端建立信使对象,通过通过onbind将服务端信使对象放在IBind中回调给客户端,这样子客户端就可以拿到服务端信使对象,使用服务端信使对象给服务端发信息了。

在客户端连接上服务端后,可以建立客户端信使对象,将客户端信使对象以消息的形式,发送给服务端,服务端拿到客户端信使对象后可以给客户端发消息。

这个是线程安全的。因为都是在同一个队列里,在同一个线程里进行的。它本身是基于AIDL构建的。



c.使用AIDL,适用于需要跨进程,且非线程安全,能够同一时间处理多个请求的场合。
它的使用是创建一个.aidl file文件,里面定义接口,然后android sdk tool使用它来生成一个抽象类,抽象类里实现各种接口方法。
注意:对于大多数应用,我们都不应该使用AIDL来bindservice.因为这会使得结果更复杂。


首先我们看第一种方式;直接继承Binder,
例如一个场景一个app是用来音乐播放,它关联了一个activity。activity启用服务来后台播放音乐。


服务端:

<span style="font-size:14px;">public class LocalService extends Service {
    // Binder given to clients
    private final IBinder mBinder = new LocalBinder();
    // Random number generator
    private final Random mGenerator = new Random();

     //Class used for the client Binder.  Because we know this service always
     //runs in the same process as its clients, we don't need to deal with IPC.
    public class LocalBinder extends Binder {
        LocalService getService() {
            // Return this instance of LocalService so clients can call public methods
            return LocalService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    //method for clients 
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}</span>

客户端:

<span style="font-size:14px;">public class BindingActivity extends Activity {
    LocalService mService;
    boolean mBound = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to LocalService
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

    //** Called when a button is clicked (the button in the layout file attaches to
    //  * this method with the android:onClick attribute) 
    public void onButtonClick(View v) {
        if (mBound) {
            // Call a method from the LocalService.
            // However, if this call were something that might hang, then this request should
            // occur in a separate thread to avoid slowing down the activity performance.
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }

 // Defines callbacks for service binding, passed to bindService() 
    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
}</span>


接下来我们看第二种方式;使用Messenger,

如果您只需要在 Activity 可见时与服务交互,则应在 onStart() 期间绑定,在 onStop() 期间取消绑定。

如果您希望 Activity 在后台停止运行状态下仍可接收响应,则可在 onCreate() 期间绑定,在 onDestroy() 期间取消绑定。请注意,这意味着您的 Activity 在其整个运行过程中(甚至包括后台运行期间)都需要使用服务,因此如果服务位于其他进程内,那么当您提高该进程的权重时,系统终止该进程的可能性会增加

下面是个完整的例子;客户端和服务端使用Messenger来通讯。

服务端:Server_Service

<span style="font-size:14px;">package com.example.administrator.service_messenger;

import android.app.Service;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.widget.Toast;

/**
 * 在manifest清单文件中声明service为另起新的进程。
 */
public class Server_Service extends Service {

    private Messenger clientMessenger;

    /**
     * 服务端的handler
     */
    class ServerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case Client.MSG_FROM_CLIENT:

                    String fromClient = msg.getData().getString("msgFromClient");
                    //收到来自客户端的信息
                    Toast.makeText(getApplicationContext(), fromClient, Toast.LENGTH_SHORT).show();

                    //回复一下客户端
                    Message msgFromServer = Message.obtain(null, Client.MSG_FROM_SERVER);
                    msgFromServer.arg1 =200;
                    try {
                        clientMessenger.send(msgFromServer);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                case Client.MSG_FROM_CLIENT_MESSE:  //得到客户端的信使对象
                    clientMessenger = msg.replyTo;
                    break;
            }
        }
    }

    /**
     * 服务端的Messenger,使用服务端的handler做参数构造。
     */
    final Messenger serverMessenger = new Messenger(new ServerHandler());

    /**
     * 我们在这里使用服务端的信使对象serverMessenger的IBinder,将其返回给客户端中的
     * serviceConntected
     */
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "客户端绑定服务端成功", Toast.LENGTH_SHORT).show();
        return serverMessenger.getBinder();
    }

    @Override
    public void unbindService(ServiceConnection conn) {
        super.unbindService(conn);
        Toast.makeText(getApplicationContext(), "客户端取消了服务绑定", Toast.LENGTH_SHORT).show();
    }
}
</span>
在manifest下配置:

<span style="font-size:14px;"><service android:name=".Server_Service"
            android:process=":v1"/></span>

客户端:Client
<span style="font-size:14px;">package com.example.administrator.service_messenger;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

import java.io.UnsupportedEncodingException;
import java.util.Calendar;

public class Client extends AppCompatActivity {

    public static final int MSG_FROM_CLIENT = 1;

    public static final int MSG_FROM_CLIENT_MESSE = 2;

    public static final int MSG_FROM_SERVER = 3;
    //在客户端里拿到了服务端的信使对象
    Messenger serverMessenger = null;

    //标记是否已经连上了服务端
    boolean mBound;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_client);

        //绑定服务
        findViewById(R.id.bt_bind).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                bindService();
            }
        });

        //通过服务端的信使来发信息给服务端
        findViewById(R.id.bt_send).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    sendInfoToServer();
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }
        });


        //手动解绑服务
        findViewById(R.id.bt_unbind).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                unBindService();
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //在activity销毁时,可以选择取消绑定服务
        unBindService();
    }

    //客户端的handler
    private Handler clientHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_FROM_SERVER:
                    Toast.makeText(getApplicationContext(),  "server received :"+msg.arg1+"", Toast.LENGTH_SHORT).show();
                    break;

            }
        }
    };

    /**
     * 客户端的Messenger,使用客户的handler做参数构造。
     */
    final Messenger clientMessenger = new Messenger(clientHandler);

    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            //当我们连接上服务端后,会调用该方法。在方法里会回调给我们IBinder对象,
            //由于bindservice绑定服务后,服务端返回给客户端的IBinder是同一个。
            //所以这里我们可以利用IBinder来构建服务端里的信使对象。
            //之后有了服务端的信使对象,就可以随便网服务端发送信息了。

            //构建服务端信使对象
            serverMessenger = new Messenger(service);
            mBound = true;

            //并将客户端的messenger发给服务端,让服务端也可以给客户端发送消息
            Message msg = Message.obtain(null, MSG_FROM_CLIENT_MESSE);
            msg.replyTo = clientMessenger;
            try {
                serverMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        public void onServiceDisconnected(ComponentName className) {
            // 当连接服务端异常时调用
            serverMessenger = null;
            mBound = false;
        }
    };

    private void sendInfoToServer() throws UnsupportedEncodingException {
        if (!mBound) return;
        Message msg = Message.obtain(null, MSG_FROM_CLIENT, 0, 0);
        String strMsg = "server received:" + Calendar.getInstance().getTime();
        Bundle data = new Bundle();
        data.putString("msgFromClient", strMsg);
        msg.setData(data);
        try {
            serverMessenger.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        // 在界面可见时,可以选择去绑定服务
    }

    @Override
    protected void onStop() {
        super.onStop();
        // 在界面不可见时,可以选择去解绑服务
    }

    private void bindService() {
        bindService(new Intent(this, Server_Service.class), mConnection,
                Context.BIND_AUTO_CREATE);
    }

    private void unBindService() {
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

}
</span>


最后我们来看第三种方式使用AIDL:
这种方式支持多个请求同时进行,使用的时候要保证能够并发。
基本流程:
与上面的使用信使一样,只不过IBinder的获取渠道不一样罢了,这里是使用创建.aidl文件来实现。
最重要的点:创建.aidl文件,里面定义一些接口,这些接口包括由服务端实现的,和客户端实现的。
服务端实现的接口,就是由.aidl文件生成的接口文件,我们在服务端继承该接口文件里定义的一个静态内部抽象类Stub,该类是Binder的子类,正好可以让我们在OnBind方法中返回给客户端。
  如果你要服务端回调该客户端信息,你要再定义一个.aidl文件,里面包含回调方法。将该接口作为参数,在上面一个.aidl文件中定义注册和反注册方法;这样,当客户端连接成功服务端后,让客户端实现该接口对象,作为实参传递进来,最后就传给了服务端。服务端拿着对象往客户端发信息。

下面是一个例子:
例子结构图:


Book.java
package com.example.books.aidl;

import android.os.Parcel;
import android.os.Parcelable;

//Book要实现跨进程,需要实现parcelable接口;还要定义该类的aidl文件
public class Book implements Parcelable {
	public int bk_id;
	public String name;
	public double price;

	public Book(int bk_id, String name, double price) {
		super();
		this.bk_id = bk_id;
		this.name = name;
		this.price = price;
	}

	public Book() {
		super();
	}

	@Override
	public String toString() {
		return "Book [bk_id=" + bk_id + ", name=" + name + ", price=" + price
				+ "]"+"\n";
	}

	@Override
	public int describeContents() {
		return 0;
	}

	@Override
	public void writeToParcel(Parcel dest, int flags) {
		dest.writeInt(bk_id);
		dest.writeString(name);
		dest.writeDouble(price);
	}

	public static final Creator<Book> CREATOR = new Creator<Book>() {
		@Override
		public Book createFromParcel(Parcel source) {
			int bk_id = source.readInt();
			String name = source.readString();
			double price = source.readDouble();
			return new Book(bk_id, name, price);
		}

		@Override
		public Book[] newArray(int size) {
			return new Book[size];
		}
	};
}

Book.aidl

package com.example.books.aidl;
parcelable Book;

ICallBack.aidl
package com.example.books.aidl;
import com.example.books.aidl.Book;

//用于服务端回调给客户端使用;在客户端中定义,由服务端调用
interface ICallBack {
    void success_GetBookList(in List<Book> allBook);
    void success_AddBook(in Book book);
}


IBookManager.aidl

package com.example.books.aidl;
import  com.example.books.aidl.Book;
import  com.example.books.aidl.ICallBack;

//服务端返回给客户端远程接口
interface IBookManager {
    //客户端调用
    void requestGetBookList(); // 返回书籍列表
    void requestAddBook(in Book book); // 添加书籍
    
    
    //监听客户端行为,在连接服务成功后,注册客户端监听
    void registerListener(ICallBack listener); // 注册接口
    void unregisterListener(ICallBack listener); // 注册接口
}


BookManagerService.java

package com.example.books.service;

import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;

import com.example.books.aidl.Book;
import com.example.books.aidl.IBookManager;
import com.example.books.aidl.ICallBack;

public class BookManagerService extends Service {

	protected static final String TAG = "book";

	// 支持并发读写
	private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

	// 客户端数量
	private RemoteCallbackList<ICallBack> mListenerList = new RemoteCallbackList<>();

	private ExecutorService cacheExecutorService = Executors
			.newCachedThreadPool();

	// 返回给客户端的接口对象;客户端调用
	private Binder mBinder = new IBookManager.Stub() {
		@Override
		public void registerListener(ICallBack listener) throws RemoteException {
			mListenerList.register(listener);
			int num = mListenerList.beginBroadcast();
			mListenerList.finishBroadcast();
			Log.e(TAG, "添加完成, 注册接口数: " + num);
		}

		@Override
		public void unregisterListener(ICallBack listener)
				throws RemoteException {
			mListenerList.unregister(listener);
			int num = mListenerList.beginBroadcast();
			mListenerList.finishBroadcast();
			Log.e(TAG, "删除完成, 注册接口数: " + num);
		}

		//多线程处理,可以在同一时间同时进行多个请求。
		@Override
		public void requestGetBookList() throws RemoteException {
			cacheExecutorService.submit(new Runnable() {
				@Override
				public void run() {
					try {
						Thread.sleep(4000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					mListenerList.beginBroadcast();
					ICallBack listener = mListenerList.getBroadcastItem(0);
					Log.e(TAG, "发送通知: " + listener.toString());
					try {
						listener.success_GetBookList(mBookList);
					} catch (RemoteException e) {
						e.printStackTrace();
					}
					mListenerList.finishBroadcast();
				}
			});
		}

		//多线程处理
		@Override
		public void requestAddBook(final Book book) throws RemoteException {
			cacheExecutorService.submit(new Runnable() {
				@Override
				public void run() {
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					mBookList.add(book);
					mListenerList.beginBroadcast();
					ICallBack listener = mListenerList.getBroadcastItem(0);
					Log.e(TAG, "发送通知: " + listener.toString());
					try {
						listener.success_AddBook(book);
					} catch (RemoteException e) {
						e.printStackTrace();
					}
					mListenerList.finishBroadcast();
				}
			});
		}
	};

	@Nullable
	@Override
	public IBinder onBind(Intent intent) {
		return mBinder;
	}

}


MainActivity.java
package com.example.books;

import java.util.List;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.example.books.aidl.Book;
import com.example.books.aidl.IBookManager;
import com.example.books.aidl.ICallBack;
import com.example.books.service.BookManagerService;

public class MainActivity extends Activity implements OnClickListener {

	private TextView tv_list;

	private IBookManager mRemoteBookManager;

	// 服务端给客户端的回调
	private ICallBack callBack = new ICallBack.Stub() {
		@Override
		public void success_GetBookList(final List<Book> allBook)
				throws RemoteException {
			runOnUiThread(new Runnable() {
				public void run() {
					tv_list.setText(allBook.toString());
				}
			});
		}

		@Override
		public void success_AddBook(final Book book) throws RemoteException {
			runOnUiThread(new Runnable() {
				public void run() {
					toast("成功添加书本:" + book);
				}
			});
		}

	};

	private ServiceConnection mConnection = new ServiceConnection() {
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			isConnected = true;
			mRemoteBookManager = IBookManager.Stub.asInterface(service);
			toast("connected success");
			// 监听将数据回调给客户端
			try {
				mRemoteBookManager.registerListener(callBack);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			mRemoteBookManager = null;
			isConnected = false;
		}
	};

	private EditText et_id;

	private EditText et_name;

	private EditText et_price;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		tv_list = (TextView) findViewById(R.id.tv_list);
		findViewById(R.id.bt_bind).setOnClickListener(this);
		findViewById(R.id.bt_getAll).setOnClickListener(this);
		findViewById(R.id.bt_add).setOnClickListener(this);

		et_id = (EditText) findViewById(R.id.et_id);
		et_name = (EditText) findViewById(R.id.et_name);
		et_price = (EditText) findViewById(R.id.et_price);
	}

	@Override
	protected void onDestroy() {
		if (isConnected == true) {
			isConnected = false;
			try {
				mRemoteBookManager.unregisterListener(callBack);
				unbindService(mConnection);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		super.onDestroy();
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.bt_bind:
			startBindService();
			break;

		case R.id.bt_add:
			if (isConnected == false) {
				return;
			}

			String id = et_id.getText().toString();
			String name = et_name.getText().toString();
			String price = et_price.getText().toString();
			Book book = new Book(Integer.valueOf(id), name,
					Double.valueOf(price));

			try {
				mRemoteBookManager.requestAddBook(book);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
			break;

		case R.id.bt_getAll:
			if (isConnected == false) {
				return;
			}

			try {
				mRemoteBookManager.requestGetBookList();
			} catch (RemoteException e) {
				e.printStackTrace();
			}
			break;
		}
	}

	private boolean isConnected = false;

	private void startBindService() {
		if (isConnected == false) {
			Intent intent = new Intent(this, BookManagerService.class);
			bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
		} else {
			toast("has Connected");
		}
	}

	private void toast(String msg) {
		Toast.makeText(getApplicationContext(), msg, 0).show();
	}
}

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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:orientation="vertical" >

    <Button
        android:id="@+id/bt_bind"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="bindService"
        android:text="绑定服务" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <EditText
            android:id="@+id/et_id"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:layout_weight="1"
            android:text="1" />

        <EditText
            android:id="@+id/et_name"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:layout_weight="1"
            android:text="红马" />

        <EditText
            android:id="@+id/et_price"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:layout_weight="1"
            android:inputType="number"
            android:text="32" />
    </LinearLayout>

    <Button
        android:id="@+id/bt_add"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="添加一本图书" />

    <Button
        android:id="@+id/bt_getAll"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="获取所有图书" />

    <TextView
        android:id="@+id/tv_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="getBookList"
        android:text="显示图书列表" />

</LinearLayout>

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.books"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="22" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".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="com.example.books.service.BookManagerService"
            android:process=":remote_v1" >
        </service>
    </application>

</manifest>

效果图:






  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值