Activity和Service通信
/*************************Service代码****************************************/
public class LocalService extends Service {
private final IBinder binder = new LocalBinder();
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
}
}
public IBinder onBind(Intent intent) {
return binder;
}
}
/*****************************Activity代码*************************************/
public class BindingActivity extends Activity {
LocalService localService;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,IBinder localBinder) {
localService = (LocalBinder) localBinder.getService();
}
public void onServiceDisconnected(ComponentName arg0) {
localService = null;
}
};
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
protected void onStop() {
super.onStop();
unbindService(mConnection);
}
public void printRandomNumber{
int num = localService.getRandomNumber();
System.out.println(num);
}
}
Activity能进行绑定得益于Service的接口onBind()。
先回顾下使用context.bindService()启动Service,会经历:
context.bindService()->onCreate()->onBind()->Service running
onUnbind() -> onDestroy() ->Service stop
Service和Activity的连接可以用ServiceConnection来实现,需要实现一个新的ServiceConnection,重写onServiceConnected和onServiceDisconnected方法。执行绑定,调用bindService方法,传入一个选择了要绑定的Service的Intent(显式或隐式)和一个你实现了的ServiceConnection实例。一旦连接建立,你就能通Service的接口onBind()得到serviceBinder实例进而得到Service的实例引用。一旦Service对象找到,就能得到它的公共方法和属性。但这种方式,一定要在同一个进程和同一个Application里。
一个组件绑定Service后,其他组件还能绑定吗?
一个service可以绑定多个组件。当组件与Service绑定后,Service的生命周期就和组件相关了,需要调用unBindService()解绑。且直到所有组件都解绑了,系统才会销毁Service。
如果是组件startService启动的Service,需要用户手动stopService()或者在Service执行完工作以后stopSelf(),否则即便组件被销毁,Service也会一直存在。
GC如何识别垃圾对象?
首先看一段话,摘自《深入理解Java虚拟机》:
在主流的商用程序语言实现中,都是通过可达性分析来判定对象是否存活。该算法的基本思想就是通过一系列的被称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索过程中所走过的路径成为“引用链”,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
在Java中,可作为GC Roots的对象包括:
虚拟机栈中引用的对象。
方法区中类静态属性引用的对象。
方法区中常量引用的对象。
本地方法栈中JNI引用的对象。
从引用可达性的角度分析内存泄露
首先参照 GC如何识别垃圾对象
,然后我们以Context为例,看下内存泄露,不过在这之前我们先看一个正常案例:
public class MyActivity extends Activity{
private ActivityManager mManager = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
mManager = new ActivityManager(this);
}
public class ActivityManager {
private Context mContext = null;
public ActivityManager (Context context) {
mContext = context;
}
}
很显然,MyActivity
和 ActivityManager
是相互依赖的,这种情况会不会造成内存泄露呢?不会!
因为两个类虽然属于互相引用,但是当这个Activity销毁以后,这两个类都处于GC Roots不可达到的区域,这种情况在GC的时候这两个类是会被同时回收掉的,如同下图中的object5、6、7。
而如果我们把ActivityManager
改成单例:
public class ActivityManager{
private Context mContext = null;
private static ActivityManager mInstance = null;
public static ActivityManager getInstance(Context context) {
if (mInstance == null) {
synchronized (ActivityManager.class) {
if (mInstance == null) {
mInstance = new ActivityManager(context);
}
}
}
return mInstance;
}
private ActivityManager(Context context){
mContext = context;
}
}
MyActivity
对应的改动这里就不写了。此时ActivityManager
的生命周期变成和app相关了,即便MyActivity
finish掉,GC时,它们也不会被回收。因为ActivityManager
仍然引用了MyActivity(Context)
,这里GC Roots就是上面提到的第二条:方法区中类静态属性引用的对象。所以MyActivity
仍然无法被系统回收。
一个对象有几个锁?
对象锁:java的所有对象都含有1个互斥锁,这个锁由JVM自动获取和释放。线程进入synchronized方法的时候获取该对象的锁,当然如果已经有线程获取了这个对象的锁,那么当前线程会等待;synchronized方法正常返回或者抛异常而终止,JVM会自动释放对象锁。这里也体现了用synchronized来加锁的1个好处,方法抛异常的时候,锁仍然可以由JVM来自动释放。
类锁:对象锁是用来控制实例方法之间的同步,类锁是用来控制静态方法(或静态变量互斥体)之间的同步。其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的。我们都知道,java类可能会有很多个对象,但是只有1个Class对象,也就是说类的不同实例之间共享该类的Class对象。Class对象其实也仅仅是1个java对象,只不过有点特殊而已。由于每个java对象都有1个互斥锁,而类的静态方法是需要Class对象。所以所谓的类锁,不过是Class对象的锁而已。获取类的Class对象有好几种,最简单的就是MyClass.class的方式。
对象的引用是在堆中还是栈中?
Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配。也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在栈中分配的内存只是一个指向这个堆对象的指针(引用变量)而已。 在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。
在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。
引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。
Java为什么会设计反射?
首先反射,就是“反其道”去创建一个对象,或者调用类中的方法,基本概念不再讲,这里只说反射存在的必要性。
Java运行时仍然拥有类型信息,它包含了这个类一切:它有哪些字段、哪些方法,各是何种保护级别等等,还有这个类依赖于哪些类。在Java中,类信息以对象的形式存放,这些对象是一种元对象,它们的类型就是Class。拥有了这些信息,无论是动态创建对象还是调用某些方法都是轻而易举的,这就给反射提供了存在的可能性。
通过反射机制,我们在程序运行过程中,用类的元信息实例化对象。而在设计代码阶段,我们完全可以无视“它是什么”,这不正符合,程序开发过程中,我们一直强调的低耦合
嘛。比如设计模式中的工厂模式
,不用反射时,是这样:
interface Fruit {
public void eat() ;
}
class Apple implements Fruit {
public void eat() {
System.out.println("吃苹果。");
}
}
classFactory {
public static Fruit getInstance(String className) {
if("apple".equals(className)){
return new Apple() ;
}
return null;
}
}
public class FactoryDemo {
public static void main(String[] args) {
Fruit f = Factory.getInstance("apple") ;
f.eat() ;
}
}
此时,当继续扩展时,每次都要改代码,而如果用了反射:
interface Fruit {
public void eat() ;
}
class Apple implements Fruit {
public void eat() {
System.out.println("吃苹果。");
};
}
class Orange implements Fruit {
public void eat() {
System.out.println("吃橘子。");
};
}
class Factory {
public static Fruit getInstance(String className) {
Fruit f = null;
try{
f = (Fruit) Class.forName(className).newInstance() ;
} catch(Exception e) {
e.printStackTrace();
}
return f ;
}
}
public class FactoryDemo {
public static void main(String[] args) {
Fruit f = Factory.getInstance("cn.mldn.demo.Orange") ;
f.eat() ;
}
}
这时,这个时候即使增加了接口的子类,工厂类照样可以完成对象的实例化操作,这个才是真正的工厂类,可以应对于所有的变化,这样的代码无疑更加优雅,也更省事。
反射机制还广泛运用在框架、注解和一些“黑科技”中,用来动态加载代码。深究Java设计反射的初衷,我认为可能更多的还是更方便、更优雅、更“Don’t repeat yourself”吧。