学习《第一行代码》笔记
最下面注意:防止踩android8.0的坑,第一行代码第二版例子太老了。
服务(Service)是 Android中实现程序后台运行的解决方案。服务的运行不依赖于任何用户界面,即使 当程序被切换到后台,或者用户打开了另外一个应用程序,服务仍然能够保持正常运行。
服务并不是运行在一个独立的进程当中的,而是依赖于创建服务 时所在的应用程序进程。当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停 止运行。
实际上服务并不会自动开启线程,所有的代码 都是默认运行在主线程当中的。也就是说,我们需要在服务的内部手动创建子线程,并在这 里执行具体的任务,否则就有可能出现主线程被阻塞住的情况。
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
//服务创建的时候调用
@Override
public void onCreate() {
super.onCreate();
}
//,如果我们希望服务一旦启动就立刻去执行某个动作,就可以将逻辑写在这个方法
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override public void onDestroy() {
super.onDestroy();
}
}
//需要在AndroidManifest中注册才可以使用
<service android:name=".MyService" > </service>
启动和停止服务:
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent); // 启动服务
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent); //停止服务
在Service类中也可以调用 stopSelf()停止服务
onCreate()方法是在服务第一次创建的时候调用的,而 onStartCommand()方法则在 每次启动服务的时候都会调用,由于刚才我们是第一次点击 Start Service按钮,服务此时还 未创建过,所以两个方法都会执行,之后如果你再连续多点击几次 Start Service按钮,你就 会发现只有 onStartCommand()方法可以得到执行。
活动和服务进行通信:
可以在Activity中使用服务去下载数据,然后服务会把下载进度返回给Activity。实现异步线程的效果
public class MainActivity extends Activity implements OnClickListener {
private Button startService;
private Button stopService;
private Button bindService;
private Button unbindService;
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) { }
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
downloadBinder = (MyService.DownloadBinder) service;
downloadBinder.startDownload();
downloadBinder.getProgress();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
……
bindService = (Button) findViewById(R.id.bind_service);
unbindService = (Button) findViewById(R.id.unbind_service);
bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
……
case R.id.bind_service:
Intent bindIntent = new Intent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE); // 绑定服务
break;
case R.id.unbind_service:
unbindService(connection); // 解绑服务
break;
default:
break;
}
}
}
public class MyService extends Service {
private DownloadBinder mBinder = new DownloadBinder();
class DownloadBinder extends Binder {
public void startDownload() {
Log.d("MyService", "startDownload executed");
}
public int getProgress() {
Log.d("MyService", "getProgress executed");
return 0;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
……
}
调用 Context的 bindService()来获取一个服务的持久连接,这时就会回调 服务中的 onBind()方法。类似地,如果这个服务之前还没有创建过,onCreate()方法会先于 onBind()方法执行。之后,调用方可以获取到 onBind()方法里返回的 IBinder对象的实例,这 样就能自由地和服务进行通信了。只要调用方和服务之间的连接没有断开,服务就会一直保 持运行状态。
当调用了 startService()方法后,又去调用 stopService()方法,这时服务中的 onDestroy() 方法就会执行,表示服务已经销毁了。类似地,当调用了 bindService()方法后,又去调用 unbindService()方法,onDestroy()方法也会执行,这两种情况都很好理解。但是需要注意, 我们是完全有可能对一个服务既调用了 startService()方法,又调用了 bindService()方法的, 这种情况下该如何才能让服务销毁掉呢?根据 Android系统的机制,一个服务只要被启动或 者被绑定了之后,就会一直处于运行状态,必须要让以上两种条件同时不满足,服务才能被 销毁。所以,这种情况下要同时调用 stopService()和 unbindService()方法,onDestroy()方法才 会执行。
由于服务的优先级比较低,在内存不够的时候,容易被服务回收,因此可以将服务前台化,这样服务可以不被回收。
在学习前台服务的时候,由于第一行代码第二版例子太老了,例子中的android版本小于android 8.0 ,因此会出现android8.0的坑
在startForeground的时候会报错StartForeground Bad Notification Error 。解决方法为:https://blog.csdn.net/LJW874362735/article/details/100863117
需要添加的代码是两个if
Intent intent = new Intent(this, TestActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher));
...
...
// 【适配Android8.0】设置Notification的Channel_ID,否则不能正常显示
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId("notification_id");
}
...
...
// 额外添加:
// 【适配Android8.0】给NotificationManager对象设置NotificationChannel
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel("notification_id", "notification_name", NotificationManager.IMPORTANCE_LOW);
notificationManager.createNotificationChannel(channel);
}
// 启动前台服务通知
startForeground(1, builder.build());
实例:
MyService:
public class MyService extends Service {
public static final String TAG = "MyService";
private MyBinder myBinder = new MyBinder();
public class MyBinder extends Binder{
public void startDownload(){
Log.d(TAG,"startDownload");
Toast.makeText(getApplicationContext(),"startDownload",Toast.LENGTH_SHORT).show();
}
public void getProcess(){
Log.d(TAG,"getProcess");
Toast.makeText(getApplicationContext(),"getProcess",Toast.LENGTH_SHORT).show();
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) { //在ServiceConnection中返回IBinder
return myBinder;
}
@Override
public void onCreate() {
super.onCreate();
//第一次创建服务的时候调用
Log.d(TAG,"onCreate");
Toast.makeText(getApplicationContext(),"onCreate",Toast.LENGTH_SHORT).show();
Log.d("MyService", "onCreate executed");
}
public void startFrontService(){
Notification.Builder builder = new Notification.Builder(this.getApplicationContext());; //获取一个Notification构造器
// 【适配Android8.0】设置Notification的Channel_ID,否则不能正常显示
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(String.valueOf(1));
}
// 额外添加:
// 【适配Android8.0】给NotificationManager对象设置NotificationChannel
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(String.valueOf(1), "notification_name", NotificationManager.IMPORTANCE_LOW);
notificationManager.createNotificationChannel(channel);
}
Intent nfIntent = new Intent(this, ServiceDemoActivity.class);
builder.setContentIntent(PendingIntent.getActivity(this, 0, nfIntent, 0)); // 设置PendingIntent
builder.setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.drawable.ic_launcher_background));// 设置下拉列表中的图标(大图标)
builder.setContentTitle("下拉列表中的Title"); // 设置下拉列表里的标题
builder.setSmallIcon(R.mipmap.ic_launcher); // 设置状态栏内的小图标
builder.setContentText("要显示的内容"); // 设置上下文内容
builder.setWhen(System.currentTimeMillis()); // 设置该通知发生的时间
Notification notification = builder.build(); // 获取构建好的Notification
notification.defaults = Notification.DEFAULT_SOUND; //设置为默认的声音
startForeground(1, notification);// 开始前台服务
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//每次startService的时候调用
Log.d(TAG,"onStartCommand");
Toast.makeText(getApplicationContext(),"onStartCommand",Toast.LENGTH_SHORT).show();
if(intent.getBooleanExtra("create",false)){
startFrontService();
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG,"onDestroy");
Toast.makeText(getApplicationContext(),"onDestroy",Toast.LENGTH_SHORT).show();
stopForeground(true);// 停止前台服务
}
}
主Activity:
public class ServiceDemoActivity extends AppCompatActivity implements View.OnClickListener {
private MyService.MyBinder myBinder;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = (MyService.MyBinder) service;
myBinder.startDownload();
myBinder.getProcess();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_service_demo);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.startService:
Intent intent = new Intent(this,MyService.class);
startService(intent);
break;
case R.id.stopService:
Intent intent1 = new Intent(this,MyService.class);
stopService(intent1);
break;
case R.id.bindService:
Intent intent2 = new Intent(this,MyService.class);
bindService(intent2,serviceConnection,BIND_AUTO_CREATE);
break;
case R.id.unbindService:
unbindService(serviceConnection);
break;
case R.id.FrontStart:
Intent intent3 = new Intent(this,MyService.class);
intent3.putExtra("create",true);
startService(intent3);
break;
case R.id.FrontStop:
Intent intent4 = new Intent(this,MyService.class);
stopService(intent4);
break;
default:
break;
}
}
}
IntentService,基于多线程的服务,因为服务需要处理比较耗时的任务,所以可以开启线程处理
Android 专门提供了一个 IntentService类:可以简单地创建一个异步的、会自动停止的服务。
IntentService 兼容new Thread(),stopSelf();于一身。
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService"); // 调用父类的有参构造函数
}
@Override
protected void onHandleIntent(Intent intent) {
// 打印当前线程的id 去处理一些具体的逻辑
Log.d("MyIntentService", "Thread id is " + Thread.currentThread(). getId());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyIntentService", "onDestroy executed");
}
}
第一行代码里面的骚操作:长期定时执行服务的程序逻辑:定义一个服务,服务中onStartCommand每隔一小时发一次广播,广播收到消息后启动服务,这样循环之行。