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的生命周期就和组件相关了,需要调用unBindService()解绑。且直到所有组件都解绑了,系统才会销毁Service。
如果是组件startService启动的Service,需要用户手动stopService()或者在Service执行完工作以后stopSelf(),否则即便组件被销毁,Service也会一直存在。
首先看一段话,摘自《深入理解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相关了,即便MyActivityfinish掉,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中,类信息以对象的形式存放,这些对象是一种元对象,它们的类型就是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”吧。